diff --git a/src/byteptr.h b/src/byteptr.h index aa09d6be41f5251400cc902929c2ab87397f3802..9756e7d9562d8285402b8b75c4bb40900e6d2227 100644 --- a/src/byteptr.h +++ b/src/byteptr.h @@ -150,26 +150,78 @@ FUNCINLINE static ATTRINLINE UINT32 readulong(void *ptr) #undef DEALIGNED -#define WRITESTRINGN(p,s,n) { size_t tmp_i = 0; for (; tmp_i < n && s[tmp_i] != '\0'; tmp_i++) WRITECHAR(p, s[tmp_i]); if (tmp_i < n) WRITECHAR(p, '\0');} -#define WRITESTRING(p,s) { size_t tmp_i = 0; for (; s[tmp_i] != '\0'; tmp_i++) WRITECHAR(p, s[tmp_i]); WRITECHAR(p, '\0');} -#define WRITEMEM(p,s,n) { memcpy(p, s, n); p += n; } - -#define SKIPSTRING(p) while (READCHAR(p) != '\0') - -#define READSTRINGN(p,s,n) { size_t tmp_i = 0; for (; tmp_i < n && (s[tmp_i] = READCHAR(p)) != '\0'; tmp_i++); s[tmp_i] = '\0';} -#define READSTRING(p,s) { size_t tmp_i = 0; for (; (s[tmp_i] = READCHAR(p)) != '\0'; tmp_i++); s[tmp_i] = '\0';} -#define READMEM(p,s,n) { memcpy(s, p, n); p += n; } - -#if 0 // old names -#define WRITEBYTE(p,b) WRITEUINT8(p,b) -#define WRITESHORT(p,b) WRITEINT16(p,b) -#define WRITEUSHORT(p,b) WRITEUINT16(p,b) -#define WRITELONG(p,b) WRITEINT32(p,b) -#define WRITEULONG(p,b) WRITEUINT32(p,b) - -#define READBYTE(p) READUINT8(p) -#define READSHORT(p) READINT16(p) -#define READUSHORT(p) READUINT16(p) -#define READLONG(p) READINT32(p) -#define READULONG(p) READUINT32(p) -#endif +#define WRITESTRINGN(p, s, n) ({ \ + size_t tmp_i; \ + \ + for (tmp_i = 0; tmp_i < n && s[tmp_i] != '\0'; tmp_i++) \ + WRITECHAR(p, s[tmp_i]); \ + \ + if (tmp_i < n) \ + WRITECHAR(p, '\0'); \ +}) + +#define WRITESTRINGL(p, s, n) ({ \ + size_t tmp_i; \ + \ + for (tmp_i = 0; tmp_i < n - 1 && s[tmp_i] != '\0'; tmp_i++) \ + WRITECHAR(p, s[tmp_i]); \ + \ + WRITECHAR(p, '\0'); \ +}) + +#define WRITESTRING(p, s) ({ \ + size_t tmp_i; \ + \ + for (tmp_i = 0; s[tmp_i] != '\0'; tmp_i++) \ + WRITECHAR(p, s[tmp_i]); \ + \ + WRITECHAR(p, '\0'); \ +}) + +#define WRITEMEM(p, s, n) ({ \ + memcpy(p, s, n); \ + p += n; \ +}) + +#define SKIPSTRING(p) while (READCHAR(p) != '\0') + +#define SKIPSTRINGN(p, n) ({ \ + size_t tmp_i = 0; \ + \ + while (tmp_i < n && READCHAR(p) != '\0') \ + tmp_i++; \ +}) + +#define SKIPSTRINGL(p, n) SKIPSTRINGN(p, n) + +#define READSTRINGN(p, s, n) ({ \ + size_t tmp_i = 0; \ + \ + while (tmp_i < n && (s[tmp_i] = READCHAR(p)) != '\0') \ + tmp_i++; \ + \ + s[tmp_i] = '\0'; \ +}) + +#define READSTRINGL(p, s, n) ({ \ + size_t tmp_i = 0; \ + \ + while (tmp_i < n - 1 && (s[tmp_i] = READCHAR(p)) != '\0') \ + tmp_i++; \ + \ + s[tmp_i] = '\0'; \ +}) + +#define READSTRING(p, s) ({ \ + size_t tmp_i = 0; \ + \ + while ((s[tmp_i] = READCHAR(p)) != '\0') \ + tmp_i++; \ + \ + s[tmp_i] = '\0'; \ +}) + +#define READMEM(p, s, n) ({ \ + memcpy(s, p, n); \ + p += n; \ +}) diff --git a/src/d_main.c b/src/d_main.c index a607e97c58d4ec6259aeeec9487a0067eef782b7..ae54ea4715604dbebacdfea8497e145a875dfd7d 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1250,6 +1250,29 @@ void D_SRB2Main(void) configfile[sizeof configfile - 1] = '\0'; + // If config isn't writable, tons of behavior will be broken. + // Fail loudly before things get confusing! + FILE *tmpfile; + char testfile[MAX_WADPATH]; + + snprintf(testfile, sizeof testfile, "%s" PATHSEP "file.tmp", srb2home); + testfile[sizeof testfile - 1] = '\0'; + + tmpfile = fopen(testfile, "w"); + if (tmpfile == NULL) + { +#if defined (_WIN32) + I_Error("Couldn't write game config.\nMake sure the game is installed somewhere it has write permissions.\n\n(Don't use the Downloads folder, Program Files, or your desktop!\nIf unsure, we recommend making a subfolder in your Documents folder.)"); +#else + I_Error("Couldn't write game config.\nMake sure you've installed the game somewhere it has write permissions."); +#endif + } + else + { + fclose(tmpfile); + remove(testfile); + } + #ifdef _arch_dreamcast strcpy(downloaddir, "/ram"); // the dreamcast's TMP #endif diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 5999a363972c9867a98cd0568aafc0285a4b30c0..524aff53dce8fac85accece056b592da616ca303 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -4728,10 +4728,10 @@ static void Command_ListWADS_f(void) for (i = 0; i < wadfiles[WAD_MUSIC]->numlumps; ++i) { p = &wadfiles[WAD_MUSIC]->lumpinfo[i]; - if (strcmp(p->name, p->name2) == 0) + if (strcmp(p->name, p->fullname) == 0) CONS_Printf("%02d: %-8s\n", i, p->name); else - CONS_Printf("%02d: %-8s (%s)\n", i, p->name, p->name2); + CONS_Printf("%02d: %-8s (%s)\n", i, p->name, p->fullname); } } } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index b30e67a9d818100ba91a74afc106daaf626db855..1a21d0f69a63e8b518151a6c76a2c29c2d6de058 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -84,7 +84,7 @@ patch_t *frameslash; // framerate stuff. Used in screen.c static player_t *plr; boolean chat_on; // entering a chat message? -static char w_chat[HU_MAXMSGLEN]; +static char w_chat[HU_MAXMSGLEN + 1]; static size_t c_input = 0; // let's try to make the chat input less shitty. static boolean headsupactive = false; boolean hu_showscores; // draw rankings @@ -463,7 +463,7 @@ void HU_AddChatText(const char *text, boolean playsound) static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags) { - XBOXSTATIC char buf[254]; + XBOXSTATIC char buf[2 + HU_MAXMSGLEN + 1]; size_t numwords, ix; char *msg = &buf[2]; const size_t msgspace = sizeof buf - 2; @@ -544,7 +544,7 @@ static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags) } buf[0] = target; newmsg = msg+5+spc; - strlcpy(msg, newmsg, 252); + strlcpy(msg, newmsg, HU_MAXMSGLEN + 1); } SendNetXCmd(XD_SAY, buf, strlen(msg) + 1 + msg-buf); @@ -654,7 +654,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) target = READSINT8(*p); flags = READUINT8(*p); msg = (char *)*p; - SKIPSTRING(*p); + SKIPSTRINGL(*p, HU_MAXMSGLEN + 1); if ((cv_mute.value || flags & (HU_CSAY|HU_SERVER_SAY)) && playernum != serverplayer && !(IsPlayerAdmin(playernum))) { @@ -970,7 +970,7 @@ static void HU_queueChatChar(INT32 c) // send automaticly the message (no more chat char) if (c == KEY_ENTER) { - char buf[2+256]; + char buf[2 + HU_MAXMSGLEN + 1]; char *msg = &buf[2]; size_t i; size_t ci = 2; @@ -1060,7 +1060,7 @@ static void HU_queueChatChar(INT32 c) // we need to get rid of the /pm<node> newmsg = msg+5+spc; - strlcpy(msg, newmsg, 255); + strlcpy(msg, newmsg, HU_MAXMSGLEN + 1); } if (ci > 3) // don't send target+flags+empty message. { diff --git a/src/hu_stuff.h b/src/hu_stuff.h index be6798a823c38cb64211c46fdca236879486a7aa..77e0628e5d0928492346206b3f885bb24c9a998d 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -61,7 +61,7 @@ typedef struct //------------------------------------ // chat stuff //------------------------------------ -#define HU_MAXMSGLEN 224 +#define HU_MAXMSGLEN 223 #define CHAT_BUFSIZE 64 // that's enough messages, right? We'll delete the older ones when that gets out of hand. #define NETSPLITSCREEN // why the hell WOULDN'T we want this? #ifdef NETSPLITSCREEN diff --git a/src/lua_script.c b/src/lua_script.c index 7c951efb361d4d58be64615fd207998daff4be7f..7fcb99676f1a9ef3155044155603d3afacf487ad 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -202,9 +202,9 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump) else // If it's not a .lua file, copy the lump name in too. { lumpinfo_t *lump_p = &wadfiles[wad]->lumpinfo[lump]; - len += 1 + strlen(lump_p->name2); // length of file name, '|', and lump name + len += 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name name = malloc(len+1); - sprintf(name, "%s|%s", wadfiles[wad]->filename, lump_p->name2); + sprintf(name, "%s|%s", wadfiles[wad]->filename, lump_p->fullname); name[len] = '\0'; } diff --git a/src/m_menu.c b/src/m_menu.c index a96d19a2c1a91969df202cb313f868e8a26673e6..fe8a97fadaa6b518a5439bd61be37d2e3a8d1715 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -265,6 +265,10 @@ static menu_t SP_TimeAttackDef, SP_ReplayDef, SP_GuestReplayDef, SP_GhostDef; // Multiplayer #ifndef NONET +static void M_PreStartServerMenu(INT32 choice); +static void M_PreStartServerMenuChoice(event_t *ev); +static void M_PreConnectMenu(INT32 choice); +static void M_PreConnectMenuChoice(event_t *ev); static void M_StartServerMenu(INT32 choice); static void M_ConnectMenu(INT32 choice); static void M_ConnectMenuModChecks(INT32 choice); @@ -1006,7 +1010,7 @@ static menuitem_t MP_MainMenu[] = {IT_HEADER, NULL, "Host a game", NULL, 100-24}, #ifndef NONET - {IT_STRING|IT_CALL, NULL, "Internet/LAN...", M_StartServerMenu, 110-24}, + {IT_STRING|IT_CALL, NULL, "Internet/LAN...", M_PreStartServerMenu, 110-24}, #else {IT_GRAYEDOUT, NULL, "Internet/LAN...", NULL, 110-24}, #endif @@ -1014,7 +1018,7 @@ static menuitem_t MP_MainMenu[] = {IT_HEADER, NULL, "Join a game", NULL, 132-24}, #ifndef NONET - {IT_STRING|IT_CALL, NULL, "Internet server browser...",M_ConnectMenuModChecks, 142-24}, + {IT_STRING|IT_CALL, NULL, "Internet server browser...",M_PreConnectMenu, 142-24}, {IT_STRING|IT_KEYHANDLER, NULL, "Specify IPv4 address:", M_HandleConnectIP, 150-24}, #else {IT_GRAYEDOUT, NULL, "Internet server browser...",NULL, 142-24}, @@ -8762,6 +8766,15 @@ static void M_DrawConnectMenu(void) // Page num V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_page].alphaKey, highlightflags, va("%u of %d", serverlistpage+1, numPages)); + + // Did you change the Server Browser address? Have a little reminder. + int mservflags = V_ALLOWLOWERCASE; + if (CV_IsSetToDefault(&cv_masterserver)) + mservflags = mservflags|highlightflags|V_30TRANS; + else + mservflags = mservflags|warningflags; + V_DrawRightAlignedSmallString(BASEVIDWIDTH - currentMenu->x, currentMenu->y+14 + MP_ConnectMenu[mp_connect_page].alphaKey, + mservflags, va("MS: %s", cv_masterserver.string)); // Horizontal line! V_DrawFill(1, currentMenu->y+32, 318, 1, 0); @@ -8998,6 +9011,74 @@ static void M_ConnectMenuModChecks(INT32 choice) M_ConnectMenu(-1); } + +boolean firstDismissedNagThisBoot = true; + +static void M_HandleMasterServerResetChoice(event_t *ev) +{ + INT32 choice = -1; + + choice = ev->data1; + + if (ev->type == ev_keydown) + { + if (choice == ' ' || choice == 'y' || choice == KEY_ENTER || choice == gamecontrol[gc_accelerate][0] || choice == gamecontrol[gc_accelerate][1]) + { + CV_Set(&cv_masterserver, cv_masterserver.defaultvalue); + CV_Set(&cv_masterserver_nagattempts, cv_masterserver_nagattempts.defaultvalue); + S_StartSound(NULL, sfx_s221); + } + else + { + if (firstDismissedNagThisBoot) + { + if (cv_masterserver_nagattempts.value > 0) + { + CV_SetValue(&cv_masterserver_nagattempts, cv_masterserver_nagattempts.value - 1); + } + firstDismissedNagThisBoot = false; + } + } + } +} + +static void M_PreStartServerMenu(INT32 choice) +{ + (void)choice; + + if (!CV_IsSetToDefault(&cv_masterserver) && cv_masterserver_nagattempts.value > 0) + { + M_StartMessage(M_GetText("Hey! You've changed the Server Browser address.\n\nYou won't be able to host games on the official Server Browser.\nUnless you're from the future, this probably isn't what you want.\n\n\x83Press Accel\x80 to fix this and continue.\x80\nPress any other key to continue anyway.\n"),M_PreStartServerMenuChoice,MM_EVENTHANDLER); + return; + } + + M_StartServerMenu(-1); +} + +static void M_PreConnectMenu(INT32 choice) +{ + (void)choice; + + if (!CV_IsSetToDefault(&cv_masterserver) && cv_masterserver_nagattempts.value > 0) + { + M_StartMessage(M_GetText("Hey! You've changed the Server Browser address.\n\nYou won't be able to see games from the official Server Browser.\nUnless you're from the future, this probably isn't what you want.\n\n\x83Press Accel\x80 to fix this and continue.\x80\nPress any other key to continue anyway.\n"),M_PreConnectMenuChoice,MM_EVENTHANDLER); + return; + } + + M_ConnectMenuModChecks(-1); +} + +static void M_PreStartServerMenuChoice(event_t *ev) +{ + M_HandleMasterServerResetChoice(ev); + M_StartServerMenu(-1); +} + +static void M_PreConnectMenuChoice(event_t *ev) +{ + M_HandleMasterServerResetChoice(ev); + M_ConnectMenuModChecks(-1); +} #endif //NONET //=========================================================================== @@ -9227,6 +9308,15 @@ static void M_DrawLevelSelectOnly(boolean leftfade, boolean rightfade) static void M_DrawServerMenu(void) { M_DrawLevelSelectOnly(false, false); + if (currentMenu == &MP_ServerDef && cv_advertise.value) // Remind players where they're hosting. + { + int mservflags = V_ALLOWLOWERCASE; + if (CV_IsSetToDefault(&cv_masterserver)) + mservflags = mservflags|highlightflags|V_30TRANS; + else + mservflags = mservflags|warningflags; + V_DrawCenteredThinString(BASEVIDWIDTH/2, BASEVIDHEIGHT-12, mservflags, va("Master Server: %s", cv_masterserver.string)); + } M_DrawGenericMenu(); } diff --git a/src/mserv.c b/src/mserv.c index 3df64d46de54230fb3ea9a259771a82c53164b3b..925a23a33cbcd1fdfc2f2a28d05129ded48e8015 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -75,6 +75,8 @@ consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SA consvar_t cv_advertise = {"advertise", "No", CV_NETVAR|CV_CALL|CV_NOINIT, CV_YesNo, Advertise_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_masterserver_nagattempts = {"masterserver_nagattempts", "5", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; + #if defined (MASTERSERVER) && defined (HAVE_THREADS) int ms_QueryId; I_mutex ms_QueryId_mutex; @@ -98,6 +100,7 @@ void AddMServCommands(void) CV_RegisterVar(&cv_masterserver_timeout); CV_RegisterVar(&cv_masterserver_debug); CV_RegisterVar(&cv_masterserver_token); + CV_RegisterVar(&cv_masterserver_nagattempts); CV_RegisterVar(&cv_advertise); CV_RegisterVar(&cv_servername); CV_RegisterVar(&cv_server_contact); diff --git a/src/mserv.h b/src/mserv.h index 02aaf3675e81178e24a80f2b6c602507fda9751a..ef401aba5383109ee5f28c745b751f64009d5261 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -61,6 +61,8 @@ extern consvar_t cv_masterserver_token; extern consvar_t cv_advertise; +extern consvar_t cv_masterserver_nagattempts; + #ifdef HAVE_THREADS extern int ms_QueryId; extern I_mutex ms_QueryId_mutex; diff --git a/src/r_data.c b/src/r_data.c index 25387decab9f5fd123ea12c89946cb39c7633ac0..43bc6ed8e7be3d17323c4ef28632a21485dd75dc 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -429,16 +429,26 @@ void R_LoadTextures(void) // Add all the textures between TX_START and TX_END if (texstart != INT16_MAX && texend != INT16_MAX) { - numtextures += (UINT32)(texend - texstart); - } - - // If no textures found by this point, bomb out - if (!numtextures && w == (numwadfiles - 1)) - { - I_Error("No textures detected in any WADs!\n"); + // PK3s have subfolders, so we can't just make a simple sum + if (W_FileHasFolders(wadfiles[w])) + { + for (j = texstart; j < texend; j++) + { + if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it + numtextures++; + } + } + else + { + numtextures += (UINT32)(texend - texstart); + } } } + // If no textures found by this point, bomb out + if (!numtextures) + I_Error("No textures detected in any WADs!\n"); + // Allocate memory and initialize to 0 for all the textures we are initialising. // There are actually 5 buffers allocated in one for convenience. textures = Z_Calloc((numtextures * sizeof(void *)) * 5, PU_STATIC, NULL); @@ -484,8 +494,13 @@ void R_LoadTextures(void) continue; // Work through each lump between the markers in the WAD. - for (j = 0; j < (texend - texstart); i++, j++) + for (j = 0; j < (texend - texstart); j++) { + if (W_FileHasFolders(wadfiles[w])) + { + if (W_IsLumpFolder(w, texstart + j)) // Check if lump is a folder + continue; // If it is then SKIP IT + } patchlump = W_CacheLumpNumPwad((UINT16)w, texstart + j, PU_CACHE); // Then, check the lump directly to see if it's a texture SOC, @@ -524,6 +539,7 @@ void R_LoadTextures(void) texturewidthmask[i] = k - 1; textureheight[i] = texture->height << FRACBITS; } + i++; } } } diff --git a/src/w_wad.c b/src/w_wad.c index c35495d304959a005e0f8aed95497adae13fff5e..97e84b1bf5ab5bd7fd8ba805136728dc1db6b648 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1,5 +1,3 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1999-2018 by Sonic Team Junior. @@ -53,6 +51,7 @@ #include "fastcmp.h" #include "g_game.h" // G_LoadGameData +#include "d_main.h" #include "filesrch.h" #include "i_video.h" // rendermode @@ -96,7 +95,7 @@ typedef struct typedef struct lumpnum_cache_s { - char lumpname[8]; + char lumpname[32]; lumpnum_t lumpnum; } lumpnum_cache_t; @@ -114,10 +113,11 @@ FreeWAD (wadfile_t *wad) { if (wad->handle) fclose(wad->handle); - if (wad->filename) - Z_Free(wad->filename); - while (wad->numlumps--) - Z_Free(wad->lumpinfo[wad->numlumps].name2); + Z_Free(wad->filename); + while (wad->numlumps--) { + Z_Free(wad->lumpinfo[wad->numlumps].longname); + Z_Free(wad->lumpinfo[wad->numlumps].fullname); + } Z_Free(wad->lumpinfo); Z_Free(wad); } @@ -204,7 +204,6 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum) if (posStart != INT16_MAX) { posEnd = W_CheckNumForFolderEndPK3("Lua/", wadnum, posStart); - posStart++; for (; posStart < posEnd; posStart++) LUA_LoadLump(wadnum, posStart); } @@ -214,13 +213,12 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum) if (posStart != INT16_MAX) { posEnd = W_CheckNumForFolderEndPK3("SOC/", wadnum, posStart); - posStart++; for(; posStart < posEnd; posStart++) { lumpinfo_t *lump_p = &wadfiles[wadnum]->lumpinfo[posStart]; - size_t length = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name + size_t length = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name char *name = malloc(length + 1); - sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->name2); + sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->fullname); name[length] = '\0'; CONS_Printf(M_GetText("Loading SOC from %s\n"), name); DEH_LoadDehackedLumpPwad(wadnum, posStart); @@ -249,9 +247,9 @@ static inline void W_LoadDehackedLumps(UINT16 wadnum) for (lump = 0; lump < wadfiles[wadnum]->numlumps; lump++, lump_p++) if (memcmp(lump_p->name,"SOC_",4)==0) // Check for generic SOC lump { // shameless copy+paste of code from LUA_LoadLump - size_t length = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name + size_t length = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name char *name = malloc(length + 1); - sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->name2); + sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->fullname); name[length] = '\0'; CONS_Printf(M_GetText("Loading SOC from %s\n"), name); @@ -384,7 +382,11 @@ static lumpinfo_t* ResGetLumpsStandalone (FILE* handle, UINT16* numlumps, const lumpinfo_t* lumpinfo = Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL); GetFileLump(lumpinfo, handle); strlcpy(lumpinfo->name, lumpname, 9); - lumpinfo->name2 = Z_StrDup(lumpname); + + // Allocate the lump's full and long name. + lumpinfo->fullname = Z_StrDup(lumpname); + lumpinfo->longname = Z_StrDup(lumpname); + *numlumps = 1; return lumpinfo; } @@ -446,7 +448,8 @@ ResGetLumpsSpecial (FILE *fp, UINT16 *lumpcp, const char *filename, const char * if (p) { lumpinfo = wad->lumpinfo; - Z_Free(p->name2); + Z_Free(p->fullname); + Z_Free(p->longname); /* Remove old cache, if any */ Z_Free(wad->lumpcache[lump]); wad->lumpcache[lump] = 0; @@ -477,7 +480,8 @@ ResGetLumpsSpecial (FILE *fp, UINT16 *lumpcp, const char *filename, const char * wad->handle = fp; wad->handlelump = lump; - p->name2 = Z_StrDup(filename); + p->fullname = Z_StrDup(filename); + p->longname = Z_StrDup(filename); GetFileLump(p, fp); @@ -583,10 +587,16 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen lump_p->compression = CM_NOCOMPRESSION; memset(lump_p->name, 0x00, 9); strncpy(lump_p->name, fileinfo->name, 8); + + // Allocate the lump's long name. + lump_p->longname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL); + strncpy(lump_p->longname, fileinfo->name, 8); + lump_p->longname[8] = '\0'; + // Allocate the lump's full name. - lump_p->name2 = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL); - strncpy(lump_p->name2, fileinfo->name, 8); - lump_p->name2[8] = '\0'; + lump_p->fullname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL); + strncpy(lump_p->fullname, fileinfo->name, 8); + lump_p->fullname[8] = '\0'; } free(fileinfov); *nlmp = numlumps; @@ -677,8 +687,8 @@ typedef struct zlentry_s static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) { zend_t zend; - zentry_t* zentries; - zentry_t* zentry; + zentry_t zentry; + zlentry_t zlentry; UINT16 numlumps = *nlmp; lumpinfo_t* lumpinfo; @@ -706,40 +716,36 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) numlumps = zend.entries; lump_p = lumpinfo = Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL); - zentry = zentries = malloc(numlumps * sizeof (*zentries)); fseek(handle, zend.cdiroffset, SEEK_SET); - for (i = 0; i < numlumps; i++, zentry++, lump_p++) + for (i = 0; i < numlumps; i++, lump_p++) { char* fullname; char* trimname; char* dotpos; - if (fread(zentry, 1, sizeof(zentry_t), handle) < sizeof(zentry_t)) + if (fread(&zentry, 1, sizeof(zentry_t), handle) < sizeof(zentry_t)) { CONS_Alert(CONS_ERROR, "Failed to read central directory (%s)\n", M_FileError(handle)); Z_Free(lumpinfo); - free(zentry); return NULL; } - if (memcmp(zentry->signature, pat_central, 4)) + if (memcmp(zentry.signature, pat_central, 4)) { CONS_Alert(CONS_ERROR, "Central directory is corrupt\n"); Z_Free(lumpinfo); - free(zentry); return NULL; } - lump_p->position = zentry->offset + zentry->namelen + zentry->xtralen + sizeof(zlentry_t); - lump_p->disksize = zentry->compsize; - lump_p->size = zentry->size; + lump_p->position = zentry.offset; // NOT ACCURATE YET: we still need to read the local entry to find our true position + lump_p->disksize = zentry.compsize; + lump_p->size = zentry.size; - fullname = malloc(zentry->namelen + 1); - if (fgets(fullname, zentry->namelen + 1, handle) != fullname) + fullname = malloc(zentry.namelen + 1); + if (fgets(fullname, zentry.namelen + 1, handle) != fullname) { CONS_Alert(CONS_ERROR, "Unable to read lumpname (%s)\n", M_FileError(handle)); Z_Free(lumpinfo); - free(zentry); free(fullname); return NULL; } @@ -756,12 +762,15 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) memset(lump_p->name, '\0', 9); // Making sure they're initialized to 0. Is it necessary? strncpy(lump_p->name, trimname, min(8, dotpos - trimname)); - lump_p->name2 = Z_Calloc(zentry->namelen + 1, PU_STATIC, NULL); - strncpy(lump_p->name2, fullname, zentry->namelen); + lump_p->longname = Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL); + strlcpy(lump_p->longname, trimname, dotpos - trimname + 1); + + lump_p->fullname = Z_Calloc(zentry.namelen + 1, PU_STATIC, NULL); + strncpy(lump_p->fullname, fullname, zentry.namelen); free(fullname); - switch(zentry->compression) + switch(zentry.compression) { case 0: lump_p->compression = CM_NOCOMPRESSION; @@ -779,12 +788,46 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) lump_p->compression = CM_UNSUPPORTED; break; } + + // skip and ignore comments/extra fields + if (fseek(handle, zentry.xtralen + zentry.commlen, SEEK_CUR) != 0) + { + CONS_Alert(CONS_ERROR, "Central directory is corrupt\n"); + Z_Free(lumpinfo); + return NULL; + } + } + + // Adjust lump position values properly + for (i = 0, lump_p = lumpinfo; i < numlumps; i++, lump_p++) + { + // skip and ignore comments/extra fields + if ((fseek(handle, lump_p->position, SEEK_SET) != 0) || (fread(&zlentry, 1, sizeof(zlentry_t), handle) < sizeof(zlentry_t))) + { + CONS_Alert(CONS_ERROR, "Local headers for lump %s are corrupt\n", lump_p->fullname); + Z_Free(lumpinfo); + return NULL; + } + + lump_p->position += sizeof(zlentry_t) + zlentry.namelen + zlentry.xtralen; } *nlmp = numlumps; return lumpinfo; } +static void W_ReadFileShaders(wadfile_t *wadfile) +{ +#ifdef HWRENDER + if (rendermode == render_opengl) + { + HWR_LoadShaders(numwadfiles - 1, W_FileHasFolders(wadfile)); + } +#else + (void)wadfile; +#endif +} + // Allocate a wadfile, setup the lumpinfo (directory) and // lumpcache, add the wadfile to the current active wadfiles // @@ -851,8 +894,6 @@ UINT16 W_InitFile(const char *filename, const char *lumpname, UINT16 *wadnump) if (type != RET_UNKNOWN) { - important = !W_VerifyNMUSlumps(filename); - #ifndef NOMD5 // // w-waiiiit! @@ -872,6 +913,8 @@ UINT16 W_InitFile(const char *filename, const char *lumpname, UINT16 *wadnump) } } #endif + + important = !W_VerifyNMUSlumps(filename); } switch(type) @@ -947,10 +990,8 @@ UINT16 W_InitFile(const char *filename, const char *lumpname, UINT16 *wadnump) (*wadnump) = numwadfiles; numwadfiles++; // must come BEFORE W_LoadDehackedLumps, so any addfile called by COM_BufInsertText called by Lua doesn't overwrite what we just loaded -#ifdef HWRENDER - if (rendermode == render_opengl) - HWR_LoadShaders(numwadfiles - 1, (wadfile->type == RET_PK3)); -#endif + // Read shaders from file + W_ReadFileShaders(wadfile); // TODO: HACK ALERT - Load Lua & SOC stuff right here. I feel like this should be out of this place, but... Let's stick with this for now. switch (wadfile->type) @@ -1092,14 +1133,46 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump) UINT16 i; static char uname[9]; - memset(uname, 0x00, sizeof uname); - strncpy(uname, name, 8); - uname[8] = 0; + if (!TestValidLump(wad,0)) + return INT16_MAX; + + strlcpy(uname, name, sizeof uname); strupr(uname); + // + // scan forward + // start at 'startlump', useful parameter when there are multiple + // resources with the same name + // + if (startlump < wadfiles[wad]->numlumps) + { + lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; + for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) + if (!strncmp(lump_p->name, uname, sizeof(uname) - 1)) + return i; + } + + // not found. + return INT16_MAX; +} + +// +// Like W_CheckNumForNamePwad, but can find entries with long names +// +// Should be the only version, but that's not possible until we fix +// all the instances of non null-terminated strings in the codebase... +// +UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump) +{ + UINT16 i; + static char uname[256 + 1]; + if (!TestValidLump(wad,0)) return INT16_MAX; + strlcpy(uname, name, sizeof uname); + strupr(uname); + // // scan forward // start at 'startlump', useful parameter when there are multiple @@ -1109,25 +1182,40 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump) { lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) - { - if (memcmp(lump_p->name,uname,8) == 0) + if (!strcmp(lump_p->longname, uname)) return i; - } } // not found. return INT16_MAX; } +UINT16 +W_CheckNumForMarkerStartPwad (const char *name, UINT16 wad, UINT16 startlump) +{ + UINT16 marker; + marker = W_CheckNumForNamePwad(name, wad, startlump); + if (marker != INT16_MAX) + marker++; // Do not count the first marker + return marker; +} + // Look for the first lump from a folder. UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump) { + size_t name_length; INT32 i; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; + name_length = strlen(name); for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) { - if (strnicmp(name, lump_p->name2, strlen(name)) == 0) + if (strnicmp(name, lump_p->fullname, name_length) == 0) + { + /* SLADE is special and puts a single directory entry. Skip that. */ + if (strlen(lump_p->fullname) == name_length) + i++; break; + } } return i; } @@ -1141,7 +1229,7 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump) lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) { - if (strnicmp(name, lump_p->name2, strlen(name))) + if (strnicmp(name, lump_p->fullname, strlen(name))) break; } return i; @@ -1155,7 +1243,7 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump) lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) { - if (!strnicmp(name, lump_p->name2, strlen(name))) + if (!strnicmp(name, lump_p->fullname, strlen(name))) { return i; } @@ -1173,11 +1261,15 @@ lumpnum_t W_CheckNumForName(const char *name) INT32 i; lumpnum_t check = INT16_MAX; + if (!*name) // some doofus gave us an empty string? + return LUMPERROR; + // Check the lumpnumcache first. Loop backwards so that we check // most recent entries first for (i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--) { - if (strncmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name, 8) == 0) + if (!lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname[8] + && strncmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name, 8) == 0) { lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1); return lumpnumcache[lumpnumcacheindex].lumpnum; @@ -1206,6 +1298,7 @@ lumpnum_t W_CheckNumForName(const char *name) { // Update the cache. lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1); + memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', 32); strncpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 8); lumpnumcache[lumpnumcacheindex].lumpnum = (i<<16)+check; @@ -1213,6 +1306,58 @@ lumpnum_t W_CheckNumForName(const char *name) } } +// +// Like W_CheckNumForName, but can find entries with long names +// +// Should be the only version, but that's not possible until we fix +// all the instances of non null-terminated strings in the codebase... +// +lumpnum_t W_CheckNumForLongName(const char *name) +{ + INT32 i; + lumpnum_t check = INT16_MAX; + + if (!*name) // some doofus gave us an empty string? + return LUMPERROR; + + // Check the lumpnumcache first. Loop backwards so that we check + // most recent entries first + for (i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--) + { + if (strcmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name) == 0) + { + lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1); + return lumpnumcache[lumpnumcacheindex].lumpnum; + } + } + + if (check == INT16_MAX) + { + // scan wad files backwards so patch lump files take precedence + for (i = numwadfiles - 1; i >= 0; i--) + { + check = W_CheckNumForLongNamePwad(name,(UINT16)i,0); + if (check != INT16_MAX) + break; //found it + } + } + + if (check == INT16_MAX) return LUMPERROR; + else + { + if (strlen(name) < 32) + { + // Update the cache. + lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1); + memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', 32); + strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 32); + lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) + check; + } + + return (i << 16) + check; + } +} + // Look for valid map data through all added files in descendant order. // Get a map marker for WADs, and a standalone WAD file lump inside PK3s. // TODO: Make it search through cache first, maybe...? @@ -1236,9 +1381,13 @@ lumpnum_t W_CheckNumForMap(const char *name) else continue; // Now look for the specified map. - for (++lumpNum; lumpNum < end; lumpNum++) + for (; lumpNum < end; lumpNum++) if (!strnicmp(name, (wadfiles[i]->lumpinfo + lumpNum)->name, 8)) - return (i<<16) + lumpNum; + { + const char *extension = strrchr(wadfiles[i]->lumpinfo[lumpNum].fullname, '.'); + if (!(extension && stricmp(extension, ".wad"))) + return (i<<16) + lumpNum; + } } } return LUMPERROR; @@ -1261,6 +1410,24 @@ lumpnum_t W_GetNumForName(const char *name) return i; } +// +// Like W_GetNumForName, but can find entries with long names +// +// Should be the only version, but that's not possible until we fix +// all the instances of non null-terminated strings in the codebase... +// +lumpnum_t W_GetNumForLongName(const char *name) +{ + lumpnum_t i; + + i = W_CheckNumForLongName(name); + + if (i == LUMPERROR) + I_Error("W_GetNumForLongName: %s not found!\n", name); + + return i; +} + // // W_CheckNumForNameInBlock // Checks only in blocks from blockstart lump to blockend lump @@ -1343,7 +1510,7 @@ boolean W_IsLumpWad(lumpnum_t lumpnum) { if (wadfiles[WADFILENUM(lumpnum)]->type == RET_PK3) { - const char *lumpfullName = (wadfiles[WADFILENUM(lumpnum)]->lumpinfo + LUMPNUM(lumpnum))->name2; + const char *lumpfullName = (wadfiles[WADFILENUM(lumpnum)]->lumpinfo + LUMPNUM(lumpnum))->fullname; if (strlen(lumpfullName) < 4) return false; // can't possibly be a WAD can it? @@ -1353,6 +1520,22 @@ boolean W_IsLumpWad(lumpnum_t lumpnum) return false; // WADs should never be inside non-PK3s as far as SRB2 is concerned } +// +// W_IsLumpFolder +// Is the lump a folder? (not in a WAD obviously) +// +boolean W_IsLumpFolder(UINT16 wad, UINT16 lump) +{ + if (W_FileHasFolders(wadfiles[wad])) + { + const char *name = wadfiles[wad]->lumpinfo[lump].fullname; + + return (name[strlen(name)-1] == '/'); // folders end in '/' + } + + return false; // WADs don't have folders +} + #ifdef HAVE_ZLIB /* report a zlib or i/o error */ void zerr(int ret) @@ -1433,9 +1616,9 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si { if (wadfiles[wad]->handle) fclose(wadfiles[wad]->handle); - if (!( wadfiles[wad]->handle = fopen(l->name2, "rb") )) + if (!( wadfiles[wad]->handle = fopen(l->fullname, "rb") )) { - CONS_Alert(CONS_ERROR, "oops %s: %s\n", l->name2, strerror(errno)); + CONS_Alert(CONS_ERROR, "oops %s: %s\n", l->fullname, strerror(errno)); return 0; } wadfiles[wad]->handlelump = lump; @@ -1450,7 +1633,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si #ifdef NO_PNG_LUMPS { size_t bytesread = fread(dest, 1, size, handle); - ErrorIfPNG(dest, bytesread, wadfiles[wad]->filename, l->name2); + ErrorIfPNG(dest, bytesread, wadfiles[wad]->filename, l->fullname); return bytesread; } #else @@ -1491,7 +1674,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si Z_Free(rawData); Z_Free(decData); #ifdef NO_PNG_LUMPS - ErrorIfPNG(dest, size, wadfiles[wad]->filename, l->name2); + ErrorIfPNG(dest, size, wadfiles[wad]->filename, l->fullname); #endif return size; #else @@ -1553,7 +1736,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si Z_Free(decData); #ifdef NO_PNG_LUMPS - ErrorIfPNG(dest, size, wadfiles[wad]->filename, l->name2); + ErrorIfPNG(dest, size, wadfiles[wad]->filename, l->fullname); #endif return size; } @@ -1737,6 +1920,9 @@ void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag) void W_UnlockCachedPatch(void *patch) { + if (!patch) + return; + // The hardware code does its own memory management, as its patches // have different lifetimes from software's. #ifdef HWRENDER @@ -1830,79 +2016,237 @@ void W_VerifyFileMD5(UINT16 wadfilenum, const char *matchmd5) #endif } -// Note: This never opens lumps themselves and therefore doesn't have to -// deal with compressed lumps. -static int W_VerifyFile(const char *filename, lumpchecklist_t *checklist, - boolean status) +// Verify versions for different archive +// formats. checklist assumed to be valid. + +static int +W_VerifyName (const char *name, lumpchecklist_t *checklist, boolean status) +{ + size_t j; + for (j = 0; checklist[j].len && checklist[j].name; ++j) + { + if (( strncasecmp(name, checklist[j].name, + checklist[j].len) != false ) == status) + { + return true; + } + } + return false; +} + +static int W_VerifyWAD(FILE *fp, lumpchecklist_t *checklist, boolean status) { - FILE *handle; size_t i, j; - int goodfile = false; - int type; + // if we're here it's a WAD file + wadinfo_t header; + filelump_t lumpinfo; - if (!checklist) - I_Error("No checklist for %s\n", filename); - type = ResourceFileDetect(filename); - if (type == RET_WAD) + // read the header + if (fread(&header, 1, sizeof header, fp) == sizeof header + && header.numlumps < INT16_MAX + && strncmp(header.identification, "ZWAD", 4) + && strncmp(header.identification, "IWAD", 4) + && strncmp(header.identification, "PWAD", 4) + && strncmp(header.identification, "SDLL", 4)) { - // assume wad file - wadinfo_t header; - filelump_t lumpinfo; + return true; + } - // open wad file - if ((handle = W_OpenWadFile(&filename, false)) == NULL) + header.numlumps = LONG(header.numlumps); + header.infotableofs = LONG(header.infotableofs); + + // let seek to the lumpinfo list + if (fseek(fp, header.infotableofs, SEEK_SET) == -1) + return true; + + for (i = 0; i < header.numlumps; i++) + { + // fill in lumpinfo for this wad file directory + if (fread(&lumpinfo, sizeof (lumpinfo), 1 , fp) != 1) return -1; - // read the header - if (fread(&header, 1, sizeof header, handle) == sizeof header - && header.numlumps < INT16_MAX - && strncmp(header.identification, "ZWAD", 4) - && strncmp(header.identification, "IWAD", 4) - && strncmp(header.identification, "PWAD", 4) - && strncmp(header.identification, "SDLL", 4)) - { - fclose(handle); - return true; - } + lumpinfo.filepos = LONG(lumpinfo.filepos); + lumpinfo.size = LONG(lumpinfo.size); - header.numlumps = LONG(header.numlumps); - header.infotableofs = LONG(header.infotableofs); + if (lumpinfo.size == 0) + continue; - // let seek to the lumpinfo list - if (fseek(handle, header.infotableofs, SEEK_SET) == -1) - { - fclose(handle); + for (j = 0; j < NUMSPRITES; j++) + if (sprnames[j] && !strncmp(lumpinfo.name, sprnames[j], 4)) // Sprites + continue; + + if (! W_VerifyName(lumpinfo.name, checklist, status)) return false; - } + } + return true; +} - goodfile = true; - for (i = 0; i < header.numlumps; i++) +// List of blacklisted folders to use when checking the PK3 +static lumpchecklist_t folderblacklist[] = +{ + {"Lua/", 4}, + {"SOC/", 4}, + {"Sprites/", 8}, + {"Textures/", 9}, + {"Patches/", 8}, + {"Flats/", 6}, + {"Fades/", 6}, + {NULL, 0}, +}; + +static int +W_VerifyPK3 (FILE *fp, lumpchecklist_t *checklist, boolean status) +{ + int verified = true; + + zend_t zend; + zentry_t zentry; + zlentry_t zlentry; + + long file_size;/* size of zip file */ + long data_size;/* size of data inside zip file */ + + long old_position; + + UINT16 numlumps; + size_t i; + + char pat_central[] = {0x50, 0x4b, 0x01, 0x02, 0x00}; + char pat_end[] = {0x50, 0x4b, 0x05, 0x06, 0x00}; + + char lumpname[9]; + + // Haha the ResGetLumpsZip function doesn't + // check for file errors, so neither will I. + + // Central directory bullshit + + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + + if (!ResFindSignature(fp, pat_end, max(0, ftell(fp) - (22 + 65536)))) + return true; + + fseek(fp, -4, SEEK_CUR); + if (fread(&zend, 1, sizeof zend, fp) < sizeof zend) + return true; + + data_size = sizeof zend; + + numlumps = zend.entries; + + fseek(fp, zend.cdiroffset, SEEK_SET); + for (i = 0; i < numlumps; i++) + { + char* fullname; + char* trimname; + char* dotpos; + + if (fread(&zentry, 1, sizeof(zentry_t), fp) < sizeof(zentry_t)) + return true; + if (memcmp(zentry.signature, pat_central, 4)) + return true; + + if (verified == true) { - // fill in lumpinfo for this wad file directory - if (fread(&lumpinfo, sizeof (lumpinfo), 1 , handle) != 1) + fullname = malloc(zentry.namelen + 1); + if (fgets(fullname, zentry.namelen + 1, fp) != fullname) { - fclose(handle); - return -1; + free(fullname); + return true; } - lumpinfo.filepos = LONG(lumpinfo.filepos); - lumpinfo.size = LONG(lumpinfo.size); + // Strip away file address and extension for the 8char name. + if ((trimname = strrchr(fullname, '/')) != 0) + trimname++; + else + trimname = fullname; // Care taken for root files. - if (lumpinfo.size == 0) - continue; + if (*trimname) // Ignore directories, well kinda + { + if ((dotpos = strrchr(trimname, '.')) == 0) + dotpos = fullname + strlen(fullname); // Watch for files without extension. - for (j = 0; j < NUMSPRITES; j++) - if (sprnames[j] && !strncmp(lumpinfo.name, sprnames[j], 4)) // Sprites - continue; + memset(lumpname, '\0', 9); // Making sure they're initialized to 0. Is it necessary? + strncpy(lumpname, trimname, min(8, dotpos - trimname)); - goodfile = false; - for (j = 0; checklist[j].len && checklist[j].name && !goodfile; j++) - if ((strncmp(lumpinfo.name, checklist[j].name, checklist[j].len) != false) == status) - goodfile = true; + if (! W_VerifyName(lumpname, checklist, status)) + verified = false; - if (!goodfile) - break; + // Check for directories next, if it's blacklisted it will return false + else if (W_VerifyName(fullname, folderblacklist, status)) + verified = false; + } + + free(fullname); + + // skip and ignore comments/extra fields + if (fseek(fp, zentry.xtralen + zentry.commlen, SEEK_CUR) != 0) + return true; } + else + { + if (fseek(fp, zentry.namelen + zentry.xtralen + zentry.commlen, SEEK_CUR) != 0) + return true; + } + + data_size += + sizeof zentry + zentry.namelen + zentry.xtralen + zentry.commlen; + + old_position = ftell(fp); + + if (fseek(fp, zentry.offset, SEEK_SET) != 0) + return true; + + if (fread(&zlentry, 1, sizeof(zlentry_t), fp) < sizeof (zlentry_t)) + return true; + + data_size += + sizeof zlentry + zlentry.namelen + zlentry.xtralen + zlentry.compsize; + + fseek(fp, old_position, SEEK_SET); + } + + if (data_size < file_size) + { + const char * error = "ZIP file has holes (%ld extra bytes)\n"; + CONS_Alert(CONS_ERROR, error, (file_size - data_size)); + return -1; + } + else if (data_size > file_size) + { + const char * error = "Reported size of ZIP file contents exceeds file size (%ld extra bytes)\n"; + CONS_Alert(CONS_ERROR, error, (data_size - file_size)); + return -1; + } + else + { + return verified; + } +} + +// Note: This never opens lumps themselves and therefore doesn't have to +// deal with compressed lumps. +static int W_VerifyFile(const char *filename, lumpchecklist_t *checklist, + boolean status) +{ + FILE *handle; + int goodfile = false; + int type; + + if (!checklist) + I_Error("No checklist for %s\n", filename); + type = ResourceFileDetect(filename); + if (type == RET_WAD || type == RET_PK3) + { + // open wad file + if ((handle = W_OpenWadFile(&filename, false)) == NULL) + return -1; + + if (type == RET_PK3) + goodfile = W_VerifyPK3(handle, checklist, status); + else if (type == RET_WAD) + goodfile = W_VerifyWAD(handle, checklist, status); fclose(handle); } diff --git a/src/w_wad.h b/src/w_wad.h index 0a55b837c0033e45a889e6626b6fac0c6553c1a8..020663d47c0709a53ff07f9dd1f3e6fd5348d18e 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -66,8 +66,9 @@ typedef struct { unsigned long position; // filelump_t filepos unsigned long disksize; // filelump_t size - char name[9]; // filelump_t name[] - char *name2; // Used by PK3s. Dynamically allocated name. + char name[9]; // filelump_t name[] e.g. "LongEntr" + char *longname; // e.g. "LongEntryName" + char *fullname; // e.g. "Folder/Subfolder/LongEntryName.extension" size_t size; // real (uncompressed) size compmethod compression; // lump compression method } lumpinfo_t; @@ -80,6 +81,7 @@ typedef struct #define MAX_WADFILES 127 // maximum of wad files used at the same time // Replay code relies on it being an UINT8 and, just to be safe, in case some wad counter somewhere is a SINT8, you should NOT go above 127 here if you're lazy like me. // Besides, are there truly 127 wads worth your interrest? +// (2022 editor's note: yes, in Kart. And as of 2022-08-07, no WAD indices are SINT8. And SRB2 does not impose a limit.) #define lumpcache_t void * @@ -144,10 +146,16 @@ void W_UnloadWadFile(UINT16 num); // so that it stops with a message if a file was not found, but not if all is okay. INT32 W_InitMultipleFiles(char *(*filenames)[2], boolean addons); +#define W_FileHasFolders(wadfile) ((wadfile)->type == RET_PK3) + const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump); const char *W_CheckNameForNum(lumpnum_t lumpnum); UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump); // checks only in one pwad +UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump); + +/* Find the first lump after F_START for instance. */ +UINT16 W_CheckNumForMarkerStartPwad(const char *name, UINT16 wad, UINT16 startlump); UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump); UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump); @@ -155,7 +163,9 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump) lumpnum_t W_CheckNumForMap(const char *name); lumpnum_t W_CheckNumForName(const char *name); +lumpnum_t W_CheckNumForLongName(const char *name); lumpnum_t W_GetNumForName(const char *name); // like W_CheckNumForName but I_Error on LUMPERROR +lumpnum_t W_GetNumForLongName(const char *name); lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, const char *blockend); UINT8 W_LumpExists(const char *name); // Lua uses this. @@ -163,6 +173,7 @@ size_t W_LumpLengthPwad(UINT16 wad, UINT16 lump); size_t W_LumpLength(lumpnum_t lumpnum); boolean W_IsLumpWad(lumpnum_t lumpnum); // for loading maps from WADs in PK3s +boolean W_IsLumpFolder(UINT16 wad, UINT16 lump); // for detecting folder "lumps" #ifdef HAVE_ZLIB void zerr(int ret); // zlib error checking