pkgsrc-WIP-changes archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

doomlegacy-devel: Partial UMAPINFO support



Module Name:	pkgsrc-wip
Committed By:	Michael Baeuerle <micha%NetBSD.org@localhost>
Pushed By:	micha
Date:		Mon Jan 23 11:45:19 2023 +0100
Changeset:	9e5dd03bcf1b83678b510581284349538d073ce0

Modified Files:
	doomlegacy-devel/TODO
	doomlegacy-devel/distinfo
	doomlegacy-devel/files/umapinfo.c
	doomlegacy-devel/files/umapinfo.h
	doomlegacy-devel/patches/patch-make__options__nix
Added Files:
	doomlegacy-devel/patches/patch-src_d__player.h
	doomlegacy-devel/patches/patch-src_doomstat.h
	doomlegacy-devel/patches/patch-src_g__game.c
	doomlegacy-devel/patches/patch-src_p__info.c
	doomlegacy-devel/patches/patch-src_w__wad.h

Log Message:
doomlegacy-devel: Partial UMAPINFO support

Build works now.

UMAPINFO keys 'label', "levelname', 'next' and 'nextsecret' should work.
(the default prefix for 'levelname' does not work yet).

To see a diff of this commit:
https://wip.pkgsrc.org/cgi-bin/gitweb.cgi?p=pkgsrc-wip.git;a=commitdiff;h=9e5dd03bcf1b83678b510581284349538d073ce0

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

diffstat:
 doomlegacy-devel/TODO                             |   9 +-
 doomlegacy-devel/distinfo                         |   7 +-
 doomlegacy-devel/files/umapinfo.c                 | 549 +++++++++++++++++++---
 doomlegacy-devel/files/umapinfo.h                 |  54 ++-
 doomlegacy-devel/patches/patch-make__options__nix |   2 +-
 doomlegacy-devel/patches/patch-src_d__player.h    |  29 ++
 doomlegacy-devel/patches/patch-src_doomstat.h     |  28 ++
 doomlegacy-devel/patches/patch-src_g__game.c      | 148 ++++++
 doomlegacy-devel/patches/patch-src_p__info.c      |  68 +++
 doomlegacy-devel/patches/patch-src_w__wad.h       |  17 +
 10 files changed, 818 insertions(+), 93 deletions(-)

diffs:
diff --git a/doomlegacy-devel/TODO b/doomlegacy-devel/TODO
index 434fb263c0..b8387400cc 100644
--- a/doomlegacy-devel/TODO
+++ b/doomlegacy-devel/TODO
@@ -1,13 +1,16 @@
 
-Part 24: Mid-texture rendering
-==============================
-[ ] Some mid-textures are rendered wrong in Pagodia
+Part 24: Middle-texture rendering
+=================================
+[ ] Some middle-textures are rendered wrong in Pagodia
     => Reported upstream in ticket #687
 
 
 Part 25: Add UMAPINFO support
 =============================
 [ ] Based on libdoom-umapinfo
+    => Module to parse and import data added
+    => Hooked in keys 'label', "levelname', 'next' and 'nextsecret'
+       The default prefix for 'levelname' does not work yet
 
 
 EOF
diff --git a/doomlegacy-devel/distinfo b/doomlegacy-devel/distinfo
index 284b22dfe3..b2081ff970 100644
--- a/doomlegacy-devel/distinfo
+++ b/doomlegacy-devel/distinfo
@@ -3,7 +3,12 @@ $NetBSD$
 BLAKE2s (doomlegacy_1.48.12_common.zip) = de8830301f433bc091804e8eaa0452fc574b99bc1d7e0021a71ba81aa6d90b76
 SHA512 (doomlegacy_1.48.12_common.zip) = 955dd60058a4676c5d16b8058754ed9a5e33fe36d7605ed09518ce7c8c675ca6345f793db40f0328e8c78bfbe707880970f442b3a0c355c507ae7cf8a5f908be
 Size (doomlegacy_1.48.12_common.zip) = 1055689 bytes
-SHA1 (patch-make__options__nix) = d8f7f3bd351e66e9cafe3b8e6093878d959fbc51
+SHA1 (patch-make__options__nix) = 666e4242f07d4d3e42da3b395cf95f07ed3b4c70
 SHA1 (patch-src_Makefile) = 7921393b6c4941fd02d9644836991c30b0fb3188
 SHA1 (patch-src_am__map.c) = 14b3c8b70c63778ad043827ab2f0b6f4fe07bcde
+SHA1 (patch-src_d__player.h) = 5975ea8a714aeb10dcecc7fd42fffcb8f9a2e51b
+SHA1 (patch-src_doomstat.h) = baa7387b6271d3064c12e24aaadf40cdb383e395
+SHA1 (patch-src_g__game.c) = bd31af700dd5ba7ffee84bec1a7caf7d37ab2d84
+SHA1 (patch-src_p__info.c) = e492f67a4c8f04779b267d6ef3b7faa75ef7779c
 SHA1 (patch-src_w__wad.c) = 237489b5d19e89e36dbf6720467d910cd7b72aa9
+SHA1 (patch-src_w__wad.h) = 683283a32222e96a6472f103d3dc51e7229e4753
diff --git a/doomlegacy-devel/files/umapinfo.c b/doomlegacy-devel/files/umapinfo.c
index 37c054441f..cf52841985 100644
--- a/doomlegacy-devel/files/umapinfo.c
+++ b/doomlegacy-devel/files/umapinfo.c
@@ -17,7 +17,7 @@
 //
 //----------------------------------------------------------------------------
 
-// [MB] 2023-01-20: Support for Rev 2.2 added
+// [MB] 2023-01-21: Support for Rev 2.2 added
 //      Description of UMAPINFO lump format:
 //      https://doomwiki.org/wiki/UMAPINFO
 
@@ -27,6 +27,8 @@
 
 #include <assert.h>
 #include <stddef.h>
+#include <stdio.h>
+#include <string.h>
 
 #include "libdoom-umapinfo-0/doom_umi.h"
 
@@ -48,11 +50,12 @@ static void *UMI_Malloc(size_t memsize)
 
 static void UMI_Free(void *ptr)
 {
-    Z_Free(ptr);
+    if (NULL != ptr)
+        Z_Free(ptr);
 }
 
 
-static void UMI_InitMapEntry(umapinfo_t *entry,
+static void UMI_InitMapEntry(mapentry_t *entry,
                              unsigned int episode, unsigned int map)
 {
     entry->next              = NULL;
@@ -74,57 +77,95 @@ static void UMI_InitMapEntry(umapinfo_t *entry,
     entry->enterpic          = NULL;
     entry->emenu             = NULL;
     entry->bossactions       = NULL;
+    entry->endgame           = unchanged;
     entry->episode           = episode;
     entry->map               = map;
     entry->partime           = 0;
     entry->emenu_clear       = false;
     entry->bossactions_clear = false;
     entry->nointermission    = false;
+    entry->endbunny          = false;
+    entry->endcast           = false;
 }
 
 
-static void UMI_DestroyMapEntry(umapinfo_t *entry)
-{
-    emenu_t      *e = entry->emenu;
-    bossaction_t *b = entry->bossactions;
-
-    UMI_Free((void*) entry->author);
-    UMI_Free((void*) entry->label);
-    UMI_Free((void*) entry->levelname);
-    UMI_Free((void*) entry->intertext);
-    UMI_Free((void*) entry->intertextsecret);
-    UMI_Free((void*) entry->interbackdrop);
-    UMI_Free((void*) entry->intermusic);
-    UMI_Free((void*) entry->nextmap);
-    UMI_Free((void*) entry->nextsecret);
-    UMI_Free((void*) entry->music);
-    UMI_Free((void*) entry->skytexture);
-    UMI_Free((void*) entry->levelpic);
-    UMI_Free((void*) entry->endpic);
-    UMI_Free((void*) entry->exitpic);
-    UMI_Free((void*) entry->enterpic);
-    while (NULL != e)
+static void UMI_InitBossActionEntry(bossaction_t *entry)
+{
+    entry->next    = NULL;
+
+    entry->thing   = 0;
+    entry->special = 0;
+    entry->tag     = 0;
+}
+
+
+static void UMI_InitEpisodeMenuEntry(emenu_t *entry)
+{
+    entry->next  = NULL;
+
+    entry->patch = NULL;
+    entry->name  = NULL;
+    entry->key   = NULL;
+}
+
+
+static void UMI_DestroyEpisodeMenu(emenu_t *entry)
+{
+    while (NULL != entry)
     {
-        emenu_t *tmp = e->next;
+        emenu_t *tmp = entry->next;
 
-        UMI_Free((void*) e->patch);
-        UMI_Free((void*) e->name);
-        UMI_Free((void*) e->key);
-        UMI_Free(e);
-        e = tmp;
+        UMI_Free((void*) entry->patch);
+        UMI_Free((void*) entry->name);
+        UMI_Free((void*) entry->key);
+        UMI_Free(entry);
+        entry = tmp;
     }
-    while (NULL != b)
+}
+
+
+static void UMI_DestroyBossActions(bossaction_t *entry)
+{
+    while (NULL != entry)
     {
-        bossaction_t *tmp = b->next;
+        bossaction_t *tmp = entry->next;
 
-        UMI_Free(b);
-        b = tmp
+        UMI_Free(entry);
+        entry = tmp;
     }
+}
+
 
-    UMI_Free(entry);
+static void UMI_DestroyMaps(mapentry_t *entry)
+{
+    while (NULL != entry)
+    {
+        mapentry_t *tmp = entry->next;
+
+        UMI_Free((void*) entry->author);
+        UMI_Free((void*) entry->label);
+        UMI_Free((void*) entry->levelname);
+        UMI_Free((void*) entry->intertext);
+        UMI_Free((void*) entry->intertextsecret);
+        UMI_Free((void*) entry->interbackdrop);
+        UMI_Free((void*) entry->intermusic);
+        UMI_Free((void*) entry->nextmap);
+        UMI_Free((void*) entry->nextsecret);
+        UMI_Free((void*) entry->music);
+        UMI_Free((void*) entry->skytexture);
+        UMI_Free((void*) entry->levelpic);
+        UMI_Free((void*) entry->endpic);
+        UMI_Free((void*) entry->exitpic);
+        UMI_Free((void*) entry->enterpic);
+        UMI_DestroyEpisodeMenu(entry->emenu);
+        UMI_DestroyBossActions(entry->bossactions);
+        UMI_Free(entry);
+        entry = tmp;
+    }
 }
 
 
+// On error, 0 is returned
 static unsigned int UMI_GetNumber(doom_umi0_ts_state state)
 {
     unsigned int number = 0;
@@ -140,67 +181,340 @@ static unsigned int UMI_GetNumber(doom_umi0_ts_state state)
 }
 
 
-// Accepts only printable ASCII characters, others are replaced with '?'
-static void UMI_ConvertToASCII(char *t, const unsigned char *s, size_t len)
+// On error, NULL is returned (and nothing is written via 'len')
+// On success a pointer to the string is returned
+// A termination with NUL is appended, but not counted for 'len'
+// Attention: libdoom-umapinfo supports arbitrary encodings for quoted strings,
+// the result must be post-processed!
+static char *UMI_GetQString(doom_umi0_ts_state state, size_t *len)
 {
-    size_t i = 0;
+    char                *result  = NULL;
+    size_t               length  = 0;
+    const unsigned char *qstring = NULL;
+    int                  retval  = DOOM_UMI0_ERROR_NOTFOUND;
 
-    for (i = 0; len > i; ++i)
+    retval = doom_umi0_ts_value_read_qstring(state, &length, &qstring);
+    if (0 > retval || 0 == length + 1u)
+        GenPrintf(EMSG_warn, "UMAPINFO: Error while reading value (qstring)\n");
+    else
     {
-        if (0x20u > s[i] || 0x7Eu < s[i])
-            t[i] = '?';
+        result = UMI_Malloc(length + 1u);
+
+        if (NULL == result)
+            GenPrintf(EMSG_warn, "UMAPINFO: Not enough memory for string\n");
         else
-            t[i] = s[i];
+        {
+            memcpy(result, qstring, length);
+            result[length] = 0;
+            *len           = length;
+        }
+    }
+
+    return result;
+}
+
+
+// Accept only printable ASCII characters. Others are replaced with '?'
+// If parameter 'multiline' is true, LF control characters are accepted too
+static void UMI_ConvertToASCII(char *str, size_t length, boolean multiline)
+{
+    size_t i = 0;
+
+    for (i = 0; length > i; ++i)
+    {
+       if (multiline && 0x0A != str[i])
+           continue;
+
+       if (0x20u > str[i] || 0x7Eu < str[i])
+            str[i] = '?';
     }
 }
 
 
+// Control characters (e.g. line breaks) are not allowed
 static void UMI_ReplaceString(doom_umi0_ts_state state, const char** str)
 {
-    size_t               length  = 0;
-    const unsigned char *qstring = NULL;
-    int                  retval  = DOOM_UMI0_ERROR_NOTFOUND;
+    size_t  length = 0;
+    char   *string = UMI_GetQString(state, &length);
 
-    retval = doom_umi0_ts_value_read_qstring(state, &length, &qstring);
-    if (0 > retval || length + 1u)
-        GenPrintf(EMSG_warn, "UMAPINFO: Error while reading value (qstring)\n");
+    if (NULL != string)
+    {
+        UMI_ConvertToASCII(string, length, false);
+        UMI_Free((void*) *str);
+        *str = string;
+    }
+}
+
+
+static const char* UMI_CreateEmptyString()
+{
+    char *string = UMI_Malloc(1);
+
+    if (NULL == string)
+        GenPrintf(EMSG_warn, "UMAPINFO: Not enough memory for empty string\n");
     else
+        string[0] = 0;
+
+    return string;
+}
+
+
+// Same as UMI_ReplaceString(), but 'clear' identifier gives empty string
+static void UMI_ReplaceStringClear(doom_umi0_ts_state state, const char** str)
+{
+    int type   = DOOM_UMI0_TYPE_INVALID;
+    int retval = doom_umi0_ts_value_type(state, &type);
+
+    if (0 <= retval && DOOM_UMI0_TYPE_CLEAR == type)
     {
-        char *s = UMI_Malloc(length + 1u);
+        const char *string = UMI_CreateEmptyString();
 
-        if (NULL == s)
-            GenPrintf(EMSG_warn, "UMAPINFO: Not enough memory for string\n");
-        else
+        if (NULL != string)
+            *str = string;
+    }
+    else
+        UMI_ReplaceString(state, str);
+}
+
+
+// It is allowed that multiple lines are contained in one value
+static void UMI_ReplaceMultiString(doom_umi0_ts_state state,
+                                   const char** str, size_t valcount)
+{
+    char   *multi        = NULL;
+    size_t  multi_length = 0;
+    size_t  val          = 0;
+
+    for (val = 0; valcount > val; ++val)
+    {
+        size_t  length = 0;
+        char   *string = UMI_GetQString(state, &length);
+        int     retval = DOOM_UMI0_ERROR_NOTFOUND;
+
+        if (NULL == string)
+            break;
+
+        if (NULL == multi)
+        {
+            multi        = string;
+            multi_length = length;
+        }
+        else  // Append
         {
-            // libdoom-umapinfo supports arbitrary encoding for quoted strings
-            UMI_ConvertToASCII(s, qstring, length);
-            s[length] = 0;
+            char   *tmp        = NULL;
+            size_t  tmp_length = multi_length + length;
+
+            if (multi_length > tmp_length || 0 == tmp_length + 1u)
+                break;
+            tmp = UMI_Malloc(tmp_length + 1u);
+            if (NULL == tmp)
+                break;
+            memcpy(tmp, multi, multi_length);
+            memcpy(&tmp[multi_length], string, length + 1u);
+            UMI_Free(multi);
+            multi        = tmp;
+            multi_length = tmp_length;
+        }
 
-            UMI_Free((void*) *str);
-            *str = s;
+        if (valcount - 1u != val)
+        {
+            retval = doom_umi0_ts_value_next(state);
+            if (0 > retval)
+                break;
         }
     }
+
+    if (0 == valcount || valcount != val)
+        GenPrintf(EMSG_warn, "UMAPINFO: Incomplete multi-line string\n");
+
+    if (NULL != multi)
+    {
+        UMI_ConvertToASCII(multi, multi_length, true);
+        UMI_Free((void*) *str);
+        *str = multi;
+    }
 }
 
 
-static void UMI_ReplaceMultiString(doom_umi0_ts_state state,
-                                   const char** str, size_t valcount)
+// Same as UMI_ReplaceMultiString(), but 'clear' identifier gives empty string
+static void UMI_ReplaceMultiStringClear(doom_umi0_ts_state state,
+                                        const char** str, size_t valcount)
 {
-    /* TBD */
+    int type   = DOOM_UMI0_TYPE_INVALID;
+    int retval = doom_umi0_ts_value_type(state, &type);
+
+    if (0 <= retval && DOOM_UMI0_TYPE_CLEAR == type)
+    {
+        const char *string = UMI_CreateEmptyString();
+
+        if (NULL != string)
+            *str = string;
+    }
+    else
+        UMI_ReplaceMultiString(state, str, valcount);
 }
 
 
-static void UMI_AppendMenuEntry(doom_umi0_ts_state state,
-                                emenu_t** em, size_t valcount)
+// Returns true on success
+static boolean UMI_PopulateEpisodeMenuEntry(doom_umi0_ts_state state,
+                                            emenu_t *entry)
 {
-    /* TBD */
+    boolean  result = false;
+    size_t   length = 0;
+    int      retval = DOOM_UMI0_ERROR_NOTFOUND;
+    char    *tmp    = NULL;
+
+    tmp = UMI_GetQString(state, &length);
+    if (NULL != tmp)
+    {
+        UMI_ConvertToASCII(tmp, length, false);
+        entry->patch = tmp;
+        retval = doom_umi0_ts_value_next(state);
+    }
+
+    if (0 <= retval)
+    {
+        tmp = UMI_GetQString(state, &length);
+        if (NULL != tmp)
+        {
+            UMI_ConvertToASCII(tmp, length, false);
+            entry->name = tmp;
+            retval = doom_umi0_ts_value_next(state);
+        }
+    }
+
+    if (0 <= retval)
+    {
+        tmp = UMI_GetQString(state, &length);
+        if (NULL != tmp)
+        {
+            UMI_ConvertToASCII(tmp, length, false);
+            entry->key = tmp;
+            result = true;
+        }
+    }
+
+    return result;
+}
+
+
+// Returns true if identifier 'clear' was detected
+static boolean UMI_AppendEpisodeMenuEntry(doom_umi0_ts_state state,
+                                          emenu_t** em, size_t valcount)
+{
+    boolean  result = false;
+    int      type   = DOOM_UMI0_TYPE_INVALID;
+    int      retval = doom_umi0_ts_value_type(state, &type);
+
+    if (0 <= retval && DOOM_UMI0_TYPE_CLEAR == type)
+    {
+        UMI_DestroyEpisodeMenu(*em);
+        *em    = NULL;
+        result = true;
+    }
+    else
+    {
+        emenu_t *entry = UMI_Malloc(sizeof(emenu_t));
+
+        if (NULL != entry)
+        {
+            UMI_InitEpisodeMenuEntry(entry);
+            if (false == UMI_PopulateEpisodeMenuEntry(state, entry))
+            {
+                GenPrintf(EMSG_warn,
+                          "UMAPINFO: Error while reading episode menu entry\n");
+                UMI_DestroyEpisodeMenu(entry);
+            }
+            else
+            {
+                if (NULL == *em)
+                    *em = entry;
+                else
+                {
+                    emenu_t *tmp = *em;
+
+                    while (NULL != tmp->next)
+                        tmp = tmp->next;
+                    tmp->next = entry;
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+
+// Returns true on success
+static boolean UMI_PopulateBossActionEntry(doom_umi0_ts_state state,
+                                           bossaction_t *entry)
+{
+    boolean result      = false;
+    size_t  thing_index = 0;
+    int     retval      = DOOM_UMI0_ERROR_NOTFOUND;
+
+    retval = doom_umi0_ts_value_read_thing(state, NULL, &thing_index);
+    if (0 <= retval && (size_t) INT_MAX >= thing_index)
+    {
+        entry->thing = thing_index;
+        retval = doom_umi0_ts_value_next(state);
+        if (0 <= retval)
+            retval = doom_umi0_ts_value_read_number(state, &entry->special);
+        if (0 <= retval)
+            retval = doom_umi0_ts_value_next(state);
+        if (0 <= retval)
+            retval = doom_umi0_ts_value_read_number(state, &entry->tag);
+        if (0 <= retval)
+            result = true;
+    }
+
+    return result;
 }
 
 
-static void UMI_MergeBossAction(doom_umi0_ts_state state,
-                                bossaction_t** ba, size_t valcount)
+static boolean UMI_MergeBossAction(doom_umi0_ts_state state,
+                                   bossaction_t** ba, size_t valcount)
 {
-    /* TBD */
+    boolean  result = false;
+    int      type   = DOOM_UMI0_TYPE_INVALID;
+    int      retval = doom_umi0_ts_value_type(state, &type);
+
+    if (0 <= retval && DOOM_UMI0_TYPE_CLEAR == type)
+    {
+        UMI_DestroyBossActions(*ba);
+        *ba    = NULL;
+        result = true;
+    }
+    else if (DOOM_UMI0_TYPE_THING == type)
+    {
+        bossaction_t *entry = UMI_Malloc(sizeof(bossaction_t));
+
+        if (NULL != entry)
+        {
+            UMI_InitBossActionEntry(entry);
+            if (false == UMI_PopulateBossActionEntry(state, entry))
+            {
+                GenPrintf(EMSG_warn,
+                          "UMAPINFO: Error while reading boss action\n");
+                UMI_DestroyBossActions(entry);
+            }
+            else
+            {
+                if (NULL == *ba)
+                    *ba = entry;
+                else
+                {
+                    bossaction_t *tmp = *ba;
+
+                    while (NULL != tmp->next)
+                        tmp = tmp->next;
+                    tmp->next = entry;
+                }
+            }
+        }
+    }
+
+    return result;
 }
 
 
@@ -229,16 +543,17 @@ static void UMI_StoreKeyData(doom_umi0_ts_state state, mapentry_t *entry)
             UMI_ReplaceString(state, &entry->author);
             break;
         case DOOM_UMI0_KEY_LABEL:
-            UMI_ReplaceString(state, &entry->label);
+            UMI_ReplaceStringClear(state, &entry->label);
             break;
         case DOOM_UMI0_KEY_LEVELNAME:
             UMI_ReplaceString(state, &entry->levelname);
             break;
         case DOOM_UMI0_KEY_INTERTEXT:
-            UMI_ReplaceMultiString(state, &entry->intertext, valcount);
+            UMI_ReplaceMultiStringClear(state, &entry->intertext, valcount);
             break;
         case DOOM_UMI0_KEY_INTERTEXTSECRET:
-            UMI_ReplaceMultiString(state, &entry->intertextsecret, valcount);
+            UMI_ReplaceMultiStringClear(state,
+                                        &entry->intertextsecret, valcount);
             break;
         case DOOM_UMI0_KEY_INTERBACKDROP:
             UMI_ReplaceString(state, &entry->interbackdrop);
@@ -246,7 +561,7 @@ static void UMI_StoreKeyData(doom_umi0_ts_state state, mapentry_t *entry)
         case DOOM_UMI0_KEY_INTERMUSIC:
             UMI_ReplaceString(state, &entry->intermusic);
             break;
-        case DOOM_UMI0_KEY_NEXTMAP:
+        case DOOM_UMI0_KEY_NEXT:
             UMI_ReplaceString(state, &entry->nextmap);
             break;
         case DOOM_UMI0_KEY_NEXTSECRET:
@@ -271,12 +586,15 @@ static void UMI_StoreKeyData(doom_umi0_ts_state state, mapentry_t *entry)
             UMI_ReplaceString(state, &entry->enterpic);
             break;
         case DOOM_UMI0_KEY_EPISODE:
-            UMI_AppendMenuEntry(state, &entry->emenu, valcount);
-            entry->emenu_clear = false;
+            entry->emenu_clear =
+                UMI_AppendEpisodeMenuEntry(state, &entry->emenu, valcount);
             break;
         case DOOM_UMI0_KEY_BOSSACTION:
-            UMI_MergeBossAction(&entry->bossactions, valcount);
-            entry->bossactions_clear = false;
+            entry->bossactions_clear =
+                UMI_MergeBossAction(state, &entry->bossactions, valcount);
+            break;
+        case DOOM_UMI0_KEY_ENDGAME:
+            entry->endgame = UMI_GetNumber(state) ? enabled : disabled;
             break;
         case DOOM_UMI0_KEY_PARTIME:
             entry->partime = UMI_GetNumber(state);
@@ -284,6 +602,12 @@ static void UMI_StoreKeyData(doom_umi0_ts_state state, mapentry_t *entry)
         case DOOM_UMI0_KEY_NOINTERMISSION:
             entry->nointermission = UMI_GetNumber(state) ? true : false;
             break;
+        case DOOM_UMI0_KEY_ENDBUNNY:
+            entry->endbunny = UMI_GetNumber(state) ? true : false;
+            break;
+        case DOOM_UMI0_KEY_ENDCAST:
+            entry->endcast = UMI_GetNumber(state) ? true : false;
+            break;
         default:
             GenPrintf(EMSG_warn, "UMAPINFO: Unknown key ignored\n");
             break;
@@ -325,7 +649,7 @@ static mapentry_t *UMI_GetMapEntry(unsigned int episode, unsigned int map)
 
 static void UMI_MergeMapEntry(doom_umi0_ts_state state)
 {
-    mapentry_t   *entry   = NULL
+    mapentry_t   *entry   = NULL;
     unsigned int  episode = 0;
     unsigned int  map     = 0;
     int           retval  = doom_umi0_ts_map_read(state, &episode, &map);
@@ -354,7 +678,7 @@ static void UMI_MergeMapEntry(doom_umi0_ts_state state)
 }
 
 
-static void UMI_ImportUMapInfo(umapinfo *umi, doom_umi0_handle data)
+static void UMI_ImportUMapInfo(umapinfo_t *umi, doom_umi0_handle data)
 {
     doom_umi0_ts_state state    = NULL;
     size_t             mapcount = 0;  // Number of toplevel map entries
@@ -410,6 +734,58 @@ void UMI_LoadUMapInfoLump(lumpnum_t lumpnum)
 }
 
 
+void UMI_DestroyUMapInfo(void)
+{
+    UMI_DestroyMaps(umapinfo.entry);
+    umapinfo.entry = NULL;
+}
+
+
+boolean UMI_ParseMapName(const char *name, byte *episode, byte *map)
+{
+    boolean      result = false;
+    int          retval = 0;
+    unsigned int e      = 0;
+    unsigned int m      = 0;
+
+    retval = sscanf(name, "%*[Mm]%*[Aa]%*[Pp]%u", &m);
+    if (1 == retval && 255u >= m)
+        result = true;
+
+    if (false == result)
+    {
+        retval = sscanf(name, "%*[Ee]%u%*[Mm]%u", &e, &m);
+        if (2 == retval && 255u >= e && 255u >= m)
+            result = true;
+    }
+
+    if (true == result)
+    {
+        *episode = e;
+        *map     = m;
+    }
+
+    return result;
+}
+
+
+// DooM Legacy uses an unsigned type for 'byte'
+mapentry_t *UMI_LookupUMapInfo(byte episode, byte map)
+{
+    mapentry_t   *entry = umapinfo.entry;
+
+    while (NULL != entry)
+    {
+        // Entries with numbers beyond the range of 'byte' will never match
+        if (entry->episode == episode && entry->map == map)
+            break;
+        entry = entry->next;
+    }
+
+    return entry;
+}
+
+
 #else  // HAVE_LIBDOOM_UMAPINFO
 
 
@@ -423,4 +799,25 @@ void UMI_LoadUMapInfoLump(lumpnum_t lumpnum)
 }
 
 
+// Stub if libdoom-umapinfo is not available
+void UMI_DestroyUMapInfo(void)
+{
+    return;
+}
+
+
+// Stub if libdoom-umapinfo is not available
+boolean UMI_ParseMapName(const char *mapname, byte *episode, byte * map)
+{
+    return false;
+}
+
+
+// Stub if libdoom-umapinfo is not available
+mapentry_t *UMI_LookupUMapinfo(byte episode, byte map)
+{
+    return NULL;
+}
+
+
 #endif  // HAVE_LIBDOOM_UMAPINFO
diff --git a/doomlegacy-devel/files/umapinfo.h b/doomlegacy-devel/files/umapinfo.h
index 482ce4b68f..b0087b744b 100644
--- a/doomlegacy-devel/files/umapinfo.h
+++ b/doomlegacy-devel/files/umapinfo.h
@@ -24,28 +24,40 @@
 
 
 // Entry for episode menu
-typedef struct
+typedef struct emenu_t emenu_t;
+struct emenu_t
 {
     emenu_t    *next;
 
     const char *patch;  // Used only if valid for all menu entries
     const char *name;   // Episode name (used with HUD font without patch)
     const char *key;    // Keyboard key
-} emenu_t;
+};
 
 
 // Usable only for monsters that call A_BossDeath
-typedef struct
+typedef struct bossaction_t bossaction_t;
+struct bossaction_t
 {
     bossaction_t *next;
 
-    int           thing;    // Thing type
-    int           special;  // Line special type
-    int           tag;      // Sector tag
-} bossaction_t;
+    unsigned int  thing;    // Thing type (index for table of specification)
+    unsigned int  special;  // Line special type
+    unsigned int  tag;      // Sector tag
+};
 
 
-typedef struct
+typedef enum tristate_t tristate_t;
+enum tristate_t
+{
+    disabled,  // Similar to false
+    enabled,   // Similar to true
+    unchanged  // Default behaviour
+};
+
+
+typedef struct mapentry_t mapentry_t;
+struct mapentry_t
 {
     mapentry_t   *next;
 
@@ -66,13 +78,16 @@ typedef struct
     const char   *enterpic;
     emenu_t      *emenu;              // Linked list
     bossaction_t *bossactions;        // Linked list
+    tristate_t    endgame;            // Can be undefined, false or true
     unsigned int  episode;
     unsigned int  map;
     unsigned int  partime;
     boolean       emenu_clear;        // Clear all default episode menu entries
     boolean       bossactions_clear;  // Clear all default boss actions
     boolean       nointermission;     // Skip the 'level finished' screen
-} mapentry_t;
+    boolean       endbunny;           // End game after level, show bunny
+    boolean       endcast;            // End game after lavel, show cast call
+};
 
 
 typedef struct
@@ -81,13 +96,28 @@ typedef struct
 } umapinfo_t;
 
 
-// Current UMAPINFO data
+// Current data
 extern umapinfo_t umapinfo;
 
 
-// Import UMAPINFO lump
-// If some keys are already present in current data, they are overwritten
+// Import and merge UMAPINFO lump into current data
+// If parts of the new data are already present, they overwrite the current data
 void UMI_LoadUMapInfoLump(lumpnum_t lumpnum);
 
 
+// Destory current data
+void UMI_DestroyUMapInfo(void);
+
+
+// Extract episode and map numbers from map name
+// For Doom 2 map names zero is returned for episode
+// Returns true on success (numbers are valid)
+boolean UMI_ParseMapName(const char *mapname, byte *episode, byte * map);
+
+
+// Search for UMAPINFO map entry that matches episode and map parameters
+// NULL is returned if nothing was found
+mapentry_t *UMI_LookupUMapInfo(byte episode, byte map);
+
+
 #endif  // UMAPINFO_H
diff --git a/doomlegacy-devel/patches/patch-make__options__nix b/doomlegacy-devel/patches/patch-make__options__nix
index 4aaf9840d6..89130aefc8 100644
--- a/doomlegacy-devel/patches/patch-make__options__nix
+++ b/doomlegacy-devel/patches/patch-make__options__nix
@@ -9,7 +9,7 @@ Add support for libdoom-umapinfo.
  # HAVE_LIBZIP=3
  
 +# Support for UMAPINFO lump needs libdoom-umapinfo.
-+HAVE_LIBDOOM_UMAPINFO=0
++HAVE_LIBDOOM_UMAPINFO=1
 +
  # Extended nodes require zlib.
  HAVE_ZLIB=1
diff --git a/doomlegacy-devel/patches/patch-src_d__player.h b/doomlegacy-devel/patches/patch-src_d__player.h
new file mode 100644
index 0000000000..3908846199
--- /dev/null
+++ b/doomlegacy-devel/patches/patch-src_d__player.h
@@ -0,0 +1,29 @@
+$NetBSD$
+
+Add support for UMAPINFO.
+
+--- src/d_player.h.orig	2023-01-10 10:38:38.000000000 +0000
++++ src/d_player.h
+@@ -63,6 +63,10 @@
+ 
+ #include "b_bot.h"	//added by AC for acbot
+ 
++// [MB] 2023-01-22: Support for UMAPINFO added
++#include "umapinfo.h"
++
++
+ //
+ // Player states.
+ //
+@@ -266,6 +270,11 @@ typedef struct
+     int         lev_prev;
+     int         lev_next;
+ 
++    // [MB] 2023-01-22: Support for UMAPINFO added
++    int         epsd_next;     // Progression may cross into another episode
++    mapentry_t  *lastmapinfo;
++    mapentry_t  *nextmapinfo;
++
+     int         maxkills;
+     int         maxitems;
+     int         maxsecret;
diff --git a/doomlegacy-devel/patches/patch-src_doomstat.h b/doomlegacy-devel/patches/patch-src_doomstat.h
new file mode 100644
index 0000000000..e7f0e14be1
--- /dev/null
+++ b/doomlegacy-devel/patches/patch-src_doomstat.h
@@ -0,0 +1,28 @@
+$NetBSD$
+
+Add support for UMAPINFO.
+
+--- src/doomstat.h.orig	2023-01-22 09:51:41.258749604 +0000
++++ src/doomstat.h
+@@ -75,6 +75,9 @@
+ #include "d_player.h"
+ #include "d_clisrv.h"
+ 
++// [MB] 2023-01-22: Support for UMAPINFO added
++#include "umapinfo.h"
++
+ 
+ // Game mode handling - identify IWAD version,
+ //  handle IWAD dependend animations etc.
+@@ -234,6 +237,11 @@ extern  skill_e         gameskill;	// ea
+ extern  byte            gameepisode;	// Doom episode, 1..4
+ extern  byte            gamemap;	// level 1..32
+ 
++// [MB] 2023-01-22: Support for UMAPINFO added
++// If this pointer is NULL, no additional UMAPINFO data is available
++// Otherwise it points to a UMAPINFO map entry structure
++extern  mapentry_t    * gamemapinfo;
++
+ // Nightmare mode flag, single player.
+ // extern  boolean         respawnmonsters;
+ 
diff --git a/doomlegacy-devel/patches/patch-src_g__game.c b/doomlegacy-devel/patches/patch-src_g__game.c
new file mode 100644
index 0000000000..53a3135fd1
--- /dev/null
+++ b/doomlegacy-devel/patches/patch-src_g__game.c
@@ -0,0 +1,148 @@
+$NetBSD$
+
+Add support for UMAPINFO.
+
+--- src/g_game.c.orig	2023-01-10 10:38:23.000000000 +0000
++++ src/g_game.c
+@@ -225,6 +225,7 @@ uint16_t        demoversion_rev;  // dem
+ skill_e         gameskill;
+ byte            gameepisode;  // current game episode number  1..4
+ byte            gamemap;      // current game map number 1..31
++mapentry_t    * gamemapinfo;  // [MB] 2023-01-22: Support for UMAPINFO added
+ char            game_map_filename[MAX_WADPATH];      // an external wad filename
+ 
+ 
+@@ -2384,10 +2385,69 @@ void G_DoCompleted (void)
+     automapactive = false;
+ }
+ 
++
++// [MB] 2023-01-22: Support for UMAPINFO added
++// Returns true if default setup should be skipped
++static boolean G_DoUMapInfo(void)
++{
++    boolean result = false;
++
++    wminfo.epsd_next   = wminfo.epsd;
++    wminfo.lastmapinfo = gamemapinfo;
++    wminfo.nextmapinfo = NULL;
++
++    if (gamemapinfo)
++    {
++        const char *mapname = NULL;
++
++        // 'nextmap' or 'nextsecret' key overrides default behaviour
++        if (secretexit && gamemapinfo->nextsecret)
++            mapname = gamemapinfo->nextsecret;
++        else if (gamemapinfo->nextmap)
++            mapname = gamemapinfo->nextmap;
++
++        if (mapname)
++        {
++            result = true;
++
++            {
++                byte e = 0;
++                byte m = 0;
++
++                UMI_ParseMapName(mapname, &e, &m);
++                wminfo.epsd_next = e;
++                wminfo.lev_next = m;
++            }
++            wminfo.epsd_next--;
++            wminfo.lev_next--;
++
++            if (wminfo.epsd_next != wminfo.epsd)
++            {
++                // Jump to different episode
++                int  i;
++
++                for (i = 0; MAXPLAYERS > i; ++i)
++                    players[i].didsecret = false;
++            }
++        }
++    }
++
++    return result;
++}
++
++
+ void G_Start_Intermission( void )
+ {
+     int  i;
+ 
++    // [MB] 2023-01-22: Support for UMAPINFO added
++    {
++        boolean skip = G_DoUMapInfo();
++
++        if (skip)
++            goto beyond_default_setup;
++    }
++
+     if (gamemode != doom2_commercial)
+     {
+         switch(gamemap)
+@@ -2436,7 +2496,7 @@ void G_Start_Intermission( void )
+     // go to next level
+     // wminfo.lev_next is 0 biased, unlike gamemap
+     wminfo.lev_next = gamemap;
+-    
++
+     // overwrite next level in some cases
+     if (gamemode == doom2_commercial)
+     {
+@@ -2490,6 +2550,21 @@ void G_Start_Intermission( void )
+                 wminfo.lev_next = 0; // wrap around in deathmatch
+         }
+     }
++beyond_default_setup:
++
++    // [MB] 2023-01-22: Support for UMAPINFO added
++    {
++        int e = wminfo.epsd_next + 1;
++        int m = wminfo.lev_next + 1;
++
++        if (0 <= e && 256 > e && 0 < m && 256 > m)
++        {
++            byte epsd = e;
++            byte map  = m;
++
++            wminfo.nextmapinfo = UMI_LookupUMapInfo(epsd, map);
++        }
++    }
+ 
+     wminfo.maxkills = totalkills;
+     wminfo.maxitems = totalitems;
+@@ -2566,6 +2641,22 @@ void G_NextLevel (void)
+ 
+ void G_DoWorldDone (void)
+ {
++    // [MB] 2023-01-22: Support for UMAPINFO added
++    {
++        int e = wminfo.epsd_next + 1;
++        int m = wminfo.lev_next + 1;
++
++        if (0 <= e && 256 > e && 0 < m && 256 > m)
++        {
++            byte epsd = e;
++            byte map  = m;
++
++            gameepisode = epsd;
++            gamemap     = map;
++            gamemapinfo = UMI_LookupUMapInfo(gameepisode, gamemap);
++        }
++    }
++
+     if( demoversion<129 )
+     {
+         gamemap = wminfo.lev_next+1;
+@@ -2880,6 +2971,9 @@ void G_InitNew (skill_e skill, const cha
+     playerdeadview = false;
+     automapactive  = false;
+ 
++    // [MB] 2023-01-22: Support for UMAPINFO added
++    gamemapinfo    = UMI_LookupUMapInfo(gameepisode, gamemap);
++
+     G_DoLoadLevel (resetplayer);
+ }
+ 
diff --git a/doomlegacy-devel/patches/patch-src_p__info.c b/doomlegacy-devel/patches/patch-src_p__info.c
new file mode 100644
index 0000000000..51e1899d8e
--- /dev/null
+++ b/doomlegacy-devel/patches/patch-src_p__info.c
@@ -0,0 +1,68 @@
+$NetBSD$
+
+Add support for UMAPINFO.
+
+--- src/p_info.c.orig	2023-01-10 10:38:38.000000000 +0000
++++ src/p_info.c
+@@ -471,11 +471,50 @@ static char * levelname;
+ // Called by P_Load_LevelInfo
+ void P_FindLevelName(void)
+ {
++  // [MB] 2023-01-22: Common buffer
++  //      Size is unchanged, but only 44 characters are displayed in worst case
++#define P_NEWLEVELSTR_SIZE 50u
++  static char newlevelstr[P_NEWLEVELSTR_SIZE];
++
+   // Determine the level name.
+   // There are a number of sources from which it can come from,
+   // getting the right one is the tricky bit =).
+-  
+-  if(*info_levelname)
++
++  // [MB] 2023-01-22: Support for UMAPINFO added
++  if (gamemapinfo && gamemapinfo->levelname)
++  {
++      size_t remaining = P_NEWLEVELSTR_SIZE - 1;  // -1 for NUL-termination
++
++      newlevelstr[0] = 0;
++      if (gamemapinfo->label)
++      {
++          // The key 'label' contains a prefix for key 'levelname'
++          size_t len_label = strlen(gamemapinfo->label);
++          size_t len_level = strlen(gamemapinfo->levelname);
++
++          if (remaining < len_label)
++              len_label = remaining;
++          remaining -= len_label;
++          if (remaining < len_level)
++              len_level = remaining;
++
++          memcpy(newlevelstr, gamemapinfo->label, len_label);
++          memcpy(&newlevelstr[len_label], gamemapinfo->levelname, len_level);
++          newlevelstr[len_label + len_level] = 0;
++      }
++      else
++      {
++          size_t len_level = strlen(gamemapinfo->levelname);
++
++          if (remaining < len_level)
++              len_level = remaining;
++
++          memcpy(newlevelstr, gamemapinfo->levelname, len_level);
++          newlevelstr[len_level] = 0;
++      }
++      levelname = newlevelstr;
++  }
++  else if(*info_levelname)
+   {
+       // info level name from level lump (p_info.c) ?
+       levelname = info_levelname;
+@@ -516,8 +555,6 @@ void P_FindLevelName(void)
+   }
+   else        //  otherwise just put "new level"
+   {
+-      static char newlevelstr[50];
+-
+       sprintf(newlevelstr, "%s: new level", level_mapname);
+       levelname = newlevelstr;
+   }
diff --git a/doomlegacy-devel/patches/patch-src_w__wad.h b/doomlegacy-devel/patches/patch-src_w__wad.h
new file mode 100644
index 0000000000..8ce0e32fad
--- /dev/null
+++ b/doomlegacy-devel/patches/patch-src_w__wad.h
@@ -0,0 +1,17 @@
+$NetBSD$
+
+Add support for UMAPINFO.
+
+--- src/w_wad.h.orig	2023-01-10 10:38:38.000000000 +0000
++++ src/w_wad.h
+@@ -308,8 +308,9 @@ typedef struct {
+   int         firstlump;
+   int         numlumps;
+ } lumplist_t;
+-                    
++
+ void    W_Load_DehackedLumps( int wadnum );
++void    W_Load_UMapInfoLumps( int wadnum );
+ 
+ 
+ 


Home | Main Index | Thread Index | Old Index