diff --git a/src/g_game.c b/src/g_game.c index 5466b483a..aaaf558f4 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2622,6 +2622,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) boolean spectator; boolean outofcoop; boolean removing; + boolean muted; INT16 bot; SINT8 pity; INT16 rings; @@ -2639,6 +2640,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) spectator = players[player].spectator; outofcoop = players[player].outofcoop; removing = players[player].removing; + muted = players[player].muted; pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER)); playerangleturn = players[player].angleturn; oldrelangleturn = players[player].oldrelangleturn; @@ -2716,6 +2718,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->spectator = spectator; p->outofcoop = outofcoop; p->removing = removing; + p->muted = muted; p->angleturn = playerangleturn; p->oldrelangleturn = oldrelangleturn; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index caf13a445..a7d0aea74 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -587,8 +587,8 @@ static void Command_CSay_f(void) DoSayCommand(0, 1, HU_CSAY); } -static tic_t spam_tokens[MAXPLAYERS] = { 1 }; // fill the buffer with 1 so the motd can be sent. -static tic_t spam_tics[MAXPLAYERS]; +UINT8 spam_tokens[MAXPLAYERS] = { 1 }; // fill the buffer with 1 so the motd can be sent. +tic_t spam_tics[MAXPLAYERS]; /** Receives a message, processing an ::XD_SAY command. * \sa DoSayCommand @@ -649,14 +649,12 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) else spam_tokens[playernum] -= 1; - // run the lua hook even if we were supposed to eat the msg, netgame consistency goes first. + if (spam_eatmsg) + return; // don't proceed if we were supposed to eat the message. if (LUA_HookPlayerMsg(playernum, target, flags, msg)) return; - if (spam_eatmsg) - return; // don't proceed if we were supposed to eat the message. - // If it's a CSAY, just CECHO and be done with it. if (flags & HU_CSAY) { diff --git a/src/hu_stuff.h b/src/hu_stuff.h index ca77ed930..07881ce1a 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -79,6 +79,9 @@ void HU_AddChatText(const char *text, boolean playsound); // set true when entering a chat message extern boolean chat_on; +extern UINT8 spam_tokens[MAXPLAYERS]; +extern tic_t spam_tics[MAXPLAYERS]; + extern patch_t *emeraldpics[3][8]; extern patch_t *rflagico; extern patch_t *bflagico; diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 5fee506af..d34dbc4e0 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -1414,6 +1414,83 @@ static void IdleUpdate(void) } } +static void DedicatedIdleUpdate(INT32 *realtics) +{ + const tic_t dedicatedidletime = cv_dedicatedidletime.value * TICRATE; + static tic_t dedicatedidletimeprev = 0; + static tic_t dedicatedidle = 0; + + if (!server || !dedicated || gamestate != GS_LEVEL) + return; + + if (dedicatedidletime > 0) + { + INT32 i; + + boolean empty = true; + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + { + empty = false; + break; + } + + if (empty) + { + if (leveltime == 2) + { + // On next tick... + dedicatedidle = dedicatedidletime - 1; + } + else if (dedicatedidle >= dedicatedidletime) + { + if (D_GetExistingTextcmd(gametic, 0) || D_GetExistingTextcmd(gametic + 1, 0)) + { + CONS_Printf("DEDICATED: Awakening from idle (Netxcmd detected...)\n"); + dedicatedidle = 0; + } + else + { + (*realtics) = 0; + } + } + else + { + dedicatedidle += (*realtics); + + if (dedicatedidle >= dedicatedidletime) + { + const char *idlereason = "at round start"; + if (leveltime > 3) + idlereason = va("for %d seconds", dedicatedidle / TICRATE); + + CONS_Printf("DEDICATED: No players %s, idling...\n", idlereason); + (*realtics) = 0; + dedicatedidle = dedicatedidletime; + } + } + } + else + { + if (dedicatedidle >= dedicatedidletime) + { + CONS_Printf("DEDICATED: Awakening from idle (Player detected...)\n"); + } + dedicatedidle = 0; + } + } + else + { + if (dedicatedidletimeprev > 0 && dedicatedidle >= dedicatedidletimeprev) + { + CONS_Printf("DEDICATED: Awakening from idle (Idle disabled...)\n"); + } + dedicatedidle = 0; + } + + dedicatedidletimeprev = dedicatedidletime; +} + // Handle timeouts to prevent definitive freezes from happenning static void HandleNodeTimeouts(void) { @@ -1490,69 +1567,7 @@ void NetUpdate(void) realtics = 5; } - if (server && dedicated && gamestate == GS_LEVEL) - { - const tic_t dedicatedidletime = cv_dedicatedidletime.value * TICRATE; - static tic_t dedicatedidletimeprev = 0; - static tic_t dedicatedidle = 0; - - if (dedicatedidletime > 0) - { - INT32 i; - - for (i = 1; i < MAXNETNODES; ++i) - if (netnodes[i].ingame) - { - if (dedicatedidle >= dedicatedidletime) - { - CONS_Printf("DEDICATED: Awakening from idle (Node %d detected...)\n", i); - dedicatedidle = 0; - } - break; - } - - if (i == MAXNETNODES) - { - if (leveltime == 2) - { - // On next tick... - dedicatedidle = dedicatedidletime-1; - } - else if (dedicatedidle >= dedicatedidletime) - { - if (D_GetExistingTextcmd(gametic, 0) || D_GetExistingTextcmd(gametic+1, 0)) - { - CONS_Printf("DEDICATED: Awakening from idle (Netxcmd detected...)\n"); - dedicatedidle = 0; - } - else - { - realtics = 0; - } - } - else if ((dedicatedidle += realtics) >= dedicatedidletime) - { - const char *idlereason = "at round start"; - if (leveltime > 3) - idlereason = va("for %d seconds", dedicatedidle/TICRATE); - - CONS_Printf("DEDICATED: No nodes %s, idling...\n", idlereason); - realtics = 0; - dedicatedidle = dedicatedidletime; - } - } - } - else - { - if (dedicatedidletimeprev > 0 && dedicatedidle >= dedicatedidletimeprev) - { - CONS_Printf("DEDICATED: Awakening from idle (Idle disabled...)\n"); - } - dedicatedidle = 0; - } - - dedicatedidletimeprev = dedicatedidletime; - } + DedicatedIdleUpdate(&realtics); gametime = nowtime; diff --git a/src/netcode/d_net.c b/src/netcode/d_net.c index b24409db1..4f21ca984 100644 --- a/src/netcode/d_net.c +++ b/src/netcode/d_net.c @@ -62,9 +62,6 @@ static doomdata_t reboundstore[MAXREBOUND]; static INT16 reboundsize[MAXREBOUND]; static INT32 rebound_head, rebound_tail; -/// \brief bandwith of netgame -INT32 net_bandwidth; - /// \brief max length per packet INT16 hardware_MAXPACKETLENGTH; @@ -1189,10 +1186,7 @@ void D_SetDoomcom(void) { if (doomcom) return; doomcom = Z_Calloc(sizeof (doomcom_t), PU_STATIC, NULL); - doomcom->id = DOOMCOM_ID; doomcom->numslots = doomcom->numnodes = 1; - doomcom->gametype = 0; - doomcom->consoleplayer = 0; doomcom->extratics = 0; } @@ -1217,7 +1211,6 @@ boolean D_CheckNetGame(void) I_NetMakeNodewPort = NULL; hardware_MAXPACKETLENGTH = MAXPACKETLENGTH; - net_bandwidth = 30000; // I_InitNetwork sets doomcom and netgame // check and initialize the network driver multiplayer = false; @@ -1237,7 +1230,6 @@ boolean D_CheckNetGame(void) server = true; // WTF? server always true??? // no! The deault mode is server. Client is set elsewhere // when the client executes connect command. - doomcom->ticdup = 1; if (M_CheckParm("-extratic")) { @@ -1248,21 +1240,6 @@ boolean D_CheckNetGame(void) CONS_Printf(M_GetText("Set extratics to %d\n"), doomcom->extratics); } - if (M_CheckParm("-bandwidth")) - { - if (M_IsNextParm()) - { - net_bandwidth = atoi(M_GetNextParm()); - if (net_bandwidth < 1000) - net_bandwidth = 1000; - if (net_bandwidth > 100000) - hardware_MAXPACKETLENGTH = MAXPACKETLENGTH; - CONS_Printf(M_GetText("Network bandwidth set to %d\n"), net_bandwidth); - } - else - I_Error("usage: -bandwidth "); - } - software_MAXPACKETLENGTH = hardware_MAXPACKETLENGTH; if (M_CheckParm("-packetsize")) { @@ -1282,8 +1259,6 @@ boolean D_CheckNetGame(void) if (netgame) multiplayer = true; - if (doomcom->id != DOOMCOM_ID) - I_Error("Doomcom buffer invalid!"); if (doomcom->numnodes > MAXNETNODES) I_Error("Too many nodes (%d), max:%d", doomcom->numnodes, MAXNETNODES); @@ -1293,7 +1268,7 @@ boolean D_CheckNetGame(void) if (M_CheckParm("-debugfile")) { char filename[21]; - INT32 k = doomcom->consoleplayer - 1; + INT32 k = consoleplayer - 1; if (M_IsNextParm()) k = atoi(M_GetNextParm()) - 1; while (!debugfile && k < MAXPLAYERS) diff --git a/src/netcode/d_netfil.c b/src/netcode/d_netfil.c index a8a10d475..adec1a0e4 100644 --- a/src/netcode/d_netfil.c +++ b/src/netcode/d_netfil.c @@ -1033,7 +1033,6 @@ void FileSendTicker(void) netbuffer->packettype = PT_FILEFRAGMENT; - // (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth) while (packetsent-- && filestosend != 0) { for (i = currentnode, j = 0; j < MAXNETNODES; diff --git a/src/netcode/i_net.h b/src/netcode/i_net.h index 09b842296..6ac4bfc87 100644 --- a/src/netcode/i_net.h +++ b/src/netcode/i_net.h @@ -32,7 +32,6 @@ #define INETPACKETLENGTH 1024 extern INT16 hardware_MAXPACKETLENGTH; -extern INT32 net_bandwidth; // in byte/s #if defined(_MSC_VER) #pragma pack(1) @@ -40,36 +39,17 @@ extern INT32 net_bandwidth; // in byte/s typedef struct { - /// Supposed to be DOOMCOM_ID - INT32 id; - - /// SRB2 executes an INT32 to execute commands. - INT16 intnum; - /// Communication between SRB2 and the driver. - /// Is CMD_SEND or CMD_GET. - INT16 command; /// Is dest for send, set by get (-1 = no packet). INT16 remotenode; - /// Number of bytes in doomdata to be sent INT16 datalength; /// Info common to all nodes. /// Console is always node 0. INT16 numnodes; - /// Flag: 1 = no duplication, 2-5 = dup for slow nets. - INT16 ticdup; /// Flag: 1 = send a backup tic in every packet. INT16 extratics; - /// kind of game - INT16 gametype; - /// Flag: -1 = new game, 0-5 = load savegame - INT16 savegame; - /// currect map - INT16 map; - /// Info specific to this node. - INT16 consoleplayer; /// Number of "slots": the highest player number in use plus one. INT16 numslots; diff --git a/src/netcode/i_tcp.c b/src/netcode/i_tcp.c index 24dfd7ec2..d9af91c04 100644 --- a/src/netcode/i_tcp.c +++ b/src/netcode/i_tcp.c @@ -37,6 +37,7 @@ #endif #include "../doomdef.h" +#include "../z_zone.h" #ifdef USE_WINSOCK1 #include @@ -87,6 +88,10 @@ #undef EHOSTUNREACH #endif #define EHOSTUNREACH WSAEHOSTUNREACH + #ifdef ENETUNREACH + #undef ENETUNREACH + #endif + #define ENETUNREACH WSAENETUNREACH #ifndef IOC_VENDOR #define IOC_VENDOR 0x18000000 #endif @@ -120,8 +125,6 @@ typedef union static boolean UPNP_support = true; #endif // HAVE_MINIUPNC -#define MAXBANS 100 - #include "../i_system.h" #include "i_net.h" #include "d_net.h" @@ -169,8 +172,8 @@ static mysockaddr_t clientaddress[MAXNETNODES+1]; static mysockaddr_t broadcastaddress[MAXNETNODES+1]; static size_t broadcastaddresses = 0; static boolean nodeconnected[MAXNETNODES+1]; -static mysockaddr_t banned[MAXBANS]; -static UINT8 bannedmask[MAXBANS]; +static mysockaddr_t *banned; +static UINT8 *bannedmask; static size_t numbans = 0; static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1? @@ -685,7 +688,6 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr socklen_t d6 = (socklen_t)sizeof(struct sockaddr_in6); #endif socklen_t d, da = (socklen_t)sizeof(mysockaddr_t); - ssize_t status; switch (sockaddr->any.sa_family) { @@ -696,14 +698,11 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr default: d = da; break; } - status = sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d); - if (status == -1) - { - CONS_Alert(CONS_WARNING, "Unable to send packet to %s: %s\n", SOCK_AddrToStr(sockaddr), strerror(errno)); - } - return status; + return sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d); } +#define ALLOWEDERROR(x) ((x) == ECONNREFUSED || (x) == EWOULDBLOCK || (x) == EHOSTUNREACH || (x) == ENETUNREACH) + static void SOCK_Send(void) { ssize_t c = ERRSOCKET; @@ -718,19 +717,25 @@ static void SOCK_Send(void) for (size_t j = 0; j < broadcastaddresses; j++) { if (myfamily[i] == broadcastaddress[j].any.sa_family) - SOCK_SendToAddr(mysockets[i], &broadcastaddress[j]); + { + c = SOCK_SendToAddr(mysockets[i], &broadcastaddress[j]); + if (c == ERRSOCKET && !ALLOWEDERROR(errno)) + break; + } } } - return; } else if (nodesocket[doomcom->remotenode] == (SOCKET_TYPE)ERRSOCKET) { for (size_t i = 0; i < mysocketses; i++) { if (myfamily[i] == clientaddress[doomcom->remotenode].any.sa_family) - SOCK_SendToAddr(mysockets[i], &clientaddress[doomcom->remotenode]); + { + c = SOCK_SendToAddr(mysockets[i], &clientaddress[doomcom->remotenode]); + if (c == ERRSOCKET && !ALLOWEDERROR(errno)) + break; + } } - return; } else { @@ -740,12 +745,14 @@ static void SOCK_Send(void) if (c == ERRSOCKET) { int e = errno; // save error code so it can't be modified later - if (e != ECONNREFUSED && e != EWOULDBLOCK && e != EHOSTUNREACH) + if (!ALLOWEDERROR(e)) I_Error("SOCK_Send, error sending to node %d (%s) #%u: %s", doomcom->remotenode, SOCK_GetNodeAddress(doomcom->remotenode), e, strerror(e)); } } +#undef ALLOWEDERROR + static void SOCK_FreeNodenum(INT32 numnode) { // can't disconnect from self :) @@ -1253,9 +1260,9 @@ static boolean SOCK_Ban(INT32 node) { if (node > MAXNETNODES) return false; - if (numbans == MAXBANS) - return false; + banned = Z_Realloc(banned, sizeof(*banned) * (numbans+1), PU_STATIC, NULL); + bannedmask = Z_Realloc(bannedmask, sizeof(*bannedmask) * (numbans+1), PU_STATIC, NULL); M_Memcpy(&banned[numbans], &clientaddress[node], sizeof (mysockaddr_t)); if (banned[numbans].any.sa_family == AF_INET) { @@ -1278,7 +1285,7 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) struct my_addrinfo *ai, *runp, hints; int gaie; - if (numbans == MAXBANS || !address) + if (!address) return false; memset(&hints, 0x00, sizeof(hints)); @@ -1293,8 +1300,10 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) runp = ai; - while(runp != NULL && numbans != MAXBANS) + while(runp != NULL) { + banned = Z_Realloc(banned, sizeof(*banned) * (numbans+1), PU_STATIC, NULL); + bannedmask = Z_Realloc(bannedmask, sizeof(*bannedmask) * (numbans+1), PU_STATIC, NULL); memcpy(&banned[numbans], runp->ai_addr, runp->ai_addrlen); if (mask) @@ -1324,6 +1333,10 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) static void SOCK_ClearBans(void) { numbans = 0; + Z_Free(banned); + banned = NULL; + Z_Free(bannedmask); + bannedmask = NULL; } boolean I_InitTcpNetwork(void) @@ -1378,7 +1391,6 @@ boolean I_InitTcpNetwork(void) // FIXME: // ??? and now ? // server on a big modem ??? 4*isdn - net_bandwidth = 16000; hardware_MAXPACKETLENGTH = INETPACKETLENGTH; ret = true; @@ -1407,7 +1419,6 @@ boolean I_InitTcpNetwork(void) // so we're on a LAN COM_BufAddText("connect any\n"); - net_bandwidth = 800000; hardware_MAXPACKETLENGTH = MAXPACKETLENGTH; } } diff --git a/src/p_map.c b/src/p_map.c index b79f9d45c..c742e2e85 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1465,13 +1465,13 @@ static unsigned PIT_DoCheckThing(mobj_t *thing) } // check for special pickup - if (thing->flags & MF_SPECIAL) + if (thing->flags & MF_SPECIAL && (tmthing->player || (tmthing->flags & MF_PUSHABLE))) // MF_PUSHABLE added for steam jets { P_TouchSpecialThing(thing, tmthing, true); // can remove thing return CHECKTHING_COLLIDE; } // check again for special pickup - if (tmthing->flags & MF_SPECIAL) + if (tmthing->flags & MF_SPECIAL && (thing->player || (thing->flags & MF_PUSHABLE))) // MF_PUSHABLE added for steam jets { P_TouchSpecialThing(tmthing, thing, true); // can remove thing return CHECKTHING_COLLIDE; diff --git a/src/p_saveg.c b/src/p_saveg.c index 95e729c66..5c2228593 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -35,6 +35,7 @@ #include "p_polyobj.h" #include "lua_script.h" #include "p_slopes.h" +#include "hu_stuff.h" savedata_t savedata; @@ -4854,6 +4855,12 @@ static void P_NetArchiveMisc(save_t *save_p, boolean resending) P_WriteUINT8(save_p, 0x2f); else P_WriteUINT8(save_p, 0x2e); + + for (i = 0; i < MAXPLAYERS; i++) + { + P_WriteUINT8(save_p, spam_tokens[i]); + P_WriteUINT32(save_p, spam_tics[i]); + } } static inline boolean P_NetUnArchiveMisc(save_t *save_p, boolean reloading) @@ -4951,6 +4958,12 @@ static inline boolean P_NetUnArchiveMisc(save_t *save_p, boolean reloading) if (P_ReadUINT8(save_p) == 0x2f) paused = true; + for (i = 0; i < MAXPLAYERS; i++) + { + spam_tokens[i] = P_ReadUINT8(save_p); + spam_tics[i] = P_ReadUINT32(save_p); + } + return true; } diff --git a/src/r_things.c b/src/r_things.c index b32181670..36c35fde8 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -379,7 +379,7 @@ static void MirrorMissingRotations(void) UINT8 baserotation = GetOppositeRotation(rotation, frame->rotate); UINT32 lumpnum = frame->lumppat[baserotation - 1]; - R_InstallSpriteLump(WADFILENUM(lumpnum), LUMPNUM(lumpnum), frame->lumpid[baserotation], framenum, rotation, 1); + R_InstallSpriteLump(WADFILENUM(lumpnum), LUMPNUM(lumpnum), frame->lumpid[baserotation - 1], framenum, rotation, 1); } } } diff --git a/src/st_stuff.c b/src/st_stuff.c index e088a448c..9f1fb6d88 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1042,6 +1042,9 @@ static void ST_drawInput(void) INT32 x = hudinfo[HUD_INPUT].x, y = hudinfo[HUD_INPUT].y; + if (hu_showscores) + return; + if (stplyr->powers[pw_carry] == CR_NIGHTSMODE) y += 8; else if (modeattacking || !LUA_HudEnabled(hud_lives)) diff --git a/src/w_wad.c b/src/w_wad.c index cc7cdc201..97208296a 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1329,10 +1329,10 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum /* SLADE is special and puts a single directory entry. Skip that. */ if (strlen(lump_p->fullname) == name_length) i++; - break; + return i; } } - return i; + return INT16_MAX; } // In a PK3 type of resource file, it looks for the next lumpinfo entry that doesn't share the specified pathfile. @@ -1350,6 +1350,17 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump) return i; } +// Returns 0 if the folder is not empty, 1 if it is empty, -1 if it doesn't exist +INT32 W_IsFolderEmpty(const char *name, UINT16 wad) +{ + UINT16 start = W_CheckNumForFolderStartPK3(name, wad, 0); + if (start == INT16_MAX) + return -1; + + // Unlike W_CheckNumForFolderStartPK3, W_CheckNumForFolderEndPK3 doesn't return INT16_MAX. + return W_CheckNumForFolderEndPK3(name, wad, start) <= start; +} + char *W_GetLumpFolderPathPK3(UINT16 wad, UINT16 lump) { const char *fullname = wadfiles[wad]->lumpinfo[lump].fullname; @@ -1692,7 +1703,7 @@ lumpnum_t W_GetNumForLongName(const char *name) // static UINT16 W_CheckNumForPatchNamePwad(const char *name, UINT16 wad, boolean longname) { - UINT16 i, start, end; + UINT16 i, start = INT16_MAX, end = INT16_MAX; static char uname[8 + 1] = { 0 }; UINT32 hash = 0; lumpinfo_t *lump_p; @@ -1714,8 +1725,11 @@ static UINT16 W_CheckNumForPatchNamePwad(const char *name, UINT16 wad, boolean l // TODO: cache namespace lump IDs if (W_FileHasFolders(wadfiles[wad])) { - start = W_CheckNumForFolderStartPK3("Flats/", wad, 0); - end = W_CheckNumForFolderEndPK3("Flats/", wad, start); + if (!W_IsFolderEmpty("Flats/", wad)) + { + start = W_CheckNumForFolderStartPK3("Flats/", wad, 0); + end = W_CheckNumForFolderEndPK3("Flats/", wad, start); + } } else { diff --git a/src/w_wad.h b/src/w_wad.h index 3dcb9b6e8..4fd7b2c00 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -180,6 +180,7 @@ UINT16 W_CheckNumForMarkerStartPwad(const char *name, UINT16 wad, UINT16 startlu UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump); UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump); UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump); +INT32 W_IsFolderEmpty(const char *name, UINT16 wad); char *W_GetLumpFolderPathPK3(UINT16 wad, UINT16 lump); char *W_GetLumpFolderNamePK3(UINT16 wad, UINT16 lump);