/ source / blood / src / db.cpp
db.cpp
   1  //-------------------------------------------------------------------------
   2  /*
   3  Copyright (C) 2010-2019 EDuke32 developers and contributors
   4  Copyright (C) 2019 Nuke.YKT
   5  
   6  This file is part of NBlood.
   7  
   8  NBlood is free software; you can redistribute it and/or
   9  modify it under the terms of the GNU General Public License version 2
  10  as published by the Free Software Foundation.
  11  
  12  This program is distributed in the hope that it will be useful,
  13  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15  
  16  See the GNU General Public License for more details.
  17  
  18  You should have received a copy of the GNU General Public License
  19  along with this program; if not, write to the Free Software
  20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  21  */
  22  //-------------------------------------------------------------------------
  23  #include "build.h"
  24  #include "editor.h"
  25  #ifdef POLYMER
  26  #include "polymer.h"
  27  #endif
  28  #include "compat.h"
  29  #include "common_game.h"
  30  #include "crc32.h"
  31  #include "md4.h"
  32  
  33  #include "actor.h"
  34  #include "config.h"
  35  #include "globals.h"
  36  #include "blood.h"
  37  #include "db.h"
  38  #include "iob.h"
  39  #include "eventq.h"
  40  #include "levels.h"
  41  #ifdef NOONE_EXTENSIONS
  42  #include "nnexts.h"
  43  #include "view.h"
  44  #endif
  45  
  46  #define kMapHeaderNew 0x7474614D // 'ttaM' signature
  47  #define kMapHeaderOld 0x4D617474 // 'Matt' signature
  48  
  49  #ifdef NOONE_EXTENSIONS
  50  uint8_t gModernMap = false;
  51  #endif // !NOONE_EXTENSIONS
  52  
  53  
  54  unsigned short gStatCount[kMaxStatus + 1];
  55  
  56  XSPRITE xsprite[kMaxXSprites];
  57  XSECTOR xsector[kMaxXSectors];
  58  XWALL xwall[kMaxXWalls];
  59  
  60  SPRITEHIT gSpriteHit[kMaxXSprites];
  61  
  62  int xvel[kMaxSprites], yvel[kMaxSprites], zvel[kMaxSprites];
  63  
  64  #ifdef POLYMER
  65  PolymerLight_t gPolymerLight[kMaxSprites];
  66  #endif
  67  
  68  char qsprite_filler[kMaxSprites], qsector_filler[kMaxSectors];
  69  
  70  int gVisibility;
  71  const char *gItemText[] = {
  72      "Skull Key",
  73      "Eye Key",
  74      "Fire Key",
  75      "Dagger Key",
  76      "Spider Key",
  77      "Moon Key",
  78      "Key 7",
  79      "Doctor's Bag",
  80      "Medicine Pouch",
  81      "Life Essence",
  82      "Life Seed",
  83      "Red Potion",
  84      "Feather Fall",
  85      "Limited Invisibility",
  86      "INVULNERABILITY",
  87      "Boots of Jumping",
  88      "Raven Flight",
  89      "Guns Akimbo",
  90      "Diving Suit",
  91      "Gas mask",
  92      "Clone",
  93      "Crystal Ball",
  94      "Decoy",
  95      "Doppleganger",
  96      "Reflective shots",
  97      "Beast Vision",
  98      "ShadowCloak",
  99      "Rage shroom",
 100      "Delirium Shroom",
 101      "Grow shroom",
 102      "Shrink shroom",
 103      "Death mask",
 104      "Wine Goblet",
 105      "Wine Bottle",
 106      "Skull Grail",
 107      "Silver Grail",
 108      "Tome",
 109      "Black Chest",
 110      "Wooden Chest",
 111      "Asbestos Armor",
 112      "Basic Armor",
 113      "Body Armor",
 114      "Fire Armor",
 115      "Spirit Armor",
 116      "Super Armor",
 117      "Blue Team Base",
 118      "Red Team Base",
 119      "Blue Flag",
 120      "Red Flag",
 121      "DUMMY",
 122      "Level map",
 123  };
 124  
 125  const char *gAmmoText[] = {
 126      "Spray can",
 127      "Bundle of TNT*",
 128      "Bundle of TNT",
 129      "Case of TNT",
 130      "Proximity Detonator",
 131      "Remote Detonator",
 132      "Trapped Soul",
 133      "4 shotgun shells",
 134      "Box of shotgun shells",
 135      "A few bullets",
 136      "Voodoo Doll",
 137      "OBSOLETE",
 138      "Full drum of bullets",
 139      "Tesla Charge",
 140      "OBSOLETE",
 141      "OBSOLETE",
 142      "Flares",
 143      "OBSOLETE",
 144      "OBSOLETE",
 145      "Gasoline Can",
 146      NULL,
 147  };
 148  
 149  const char *gWeaponText[] = {
 150      "RANDOM",
 151      "Sawed-off",
 152      "Tommy Gun",
 153      "Flare Pistol",
 154      "Voodoo Doll",
 155      "Tesla Cannon",
 156      "Napalm Launcher",
 157      "Pitchfork",
 158      "Spray Can",
 159      "Dynamite",
 160      "Life Leech",
 161  };
 162  
 163  
 164  
 165  uint32_t XSPRITE::CalcChecksum(void)
 166  {
 167      // This was originally written to calculate the checksum
 168      // the way OUWB v1.21 does. Therefore, certain bits may
 169      // be skipped or calculated in a different order.
 170      uint32_t sum = 0;
 171      sum += (reference&16383) | ((state&1)<<14) | ((busy&131071)<<15);
 172      sum += (txID&1023) | ((rxID&1023)<<10) | ((command&255)<<20) |
 173             ((triggerOn&1)<<28) | ((triggerOff&1)<<29) | ((wave&3)<<30);
 174      sum += (busyTime&4095) | ((waitTime&4095)<<12) |
 175             ((restState&1)<<24) | ((Interrutable&1)<<25) |
 176             ((unused1&3)<<26) | ((respawnPending&3)<<28) |
 177             ((unused2&1)<<30) | ((lT&1)<<31);
 178      sum += (dropMsg&255) | ((Decoupled&1)<<8) |
 179             ((triggerOnce&1)<<9) | ((isTriggered&1)<<10) |
 180             ((key&7)<<11) | ((Push&1)<<14) | ((Vector&1)<<15) |
 181             ((Impact&1)<<16) | ((Pickup&1)<<17) | ((Touch&1)<<18) |
 182             ((Sight&1)<<19) | ((Proximity&1)<<20) |
 183             ((unused3&3)<<21) | ((lSkill&31)<<23) |
 184             ((lS&1)<<28) | ((lB&1)<<29) | ((lC&1)<<30) | ((DudeLockout&1)<<31);
 185      sum += (data1&65535) | ((data2&65535)<<16);
 186      sum += (data3&65535) | ((goalAng&2047)<<16) | ((dodgeDir&3)<<27) |
 187             ((locked&1)<<29) | ((medium&3)<<30);
 188      sum += (respawn&3) | ((data4&65535)<<2) |
 189             ((unused4&63)<<18) | ((lockMsg&255)<<24);
 190      sum += (health&4095) | ((dudeDeaf&1)<<12) | ((dudeAmbush&1)<<13) |
 191             ((dudeGuard&1)<<14) | ((dudeFlag4&1)<<15) | ((target&65535)<<16);
 192      sum += targetX&0xFFFFFFFF;
 193      sum += targetY&0xFFFFFFFF;
 194      sum += targetZ&0xFFFFFFFF;
 195      sum += (burnTime&65535) | ((burnSource&65535)<<16);
 196      sum += (height&65535) | ((stateTimer&65535)<<16);
 197      // aiState is a state pointer. Exact pointer values depend on exe layout.
 198      // For player sprites, it is apparently always set to 0.
 199      sum += aiState ? (((uintptr_t)aiState)&0xFFFFFFFF) : 0;
 200      return sum;
 201  }
 202  
 203  void dbCrypt(char *pPtr, int nLength, int nKey)
 204  {
 205      for (int i = 0; i < nLength; i++)
 206      {
 207          pPtr[i] = pPtr[i] ^ nKey;
 208          nKey++;
 209      }
 210  }
 211  
 212  #ifdef POLYMER
 213  
 214  void DeleteLight(int32_t s)
 215  {
 216      if (gPolymerLight[s].lightId >= 0)
 217          polymer_deletelight(gPolymerLight[s].lightId);
 218      gPolymerLight[s].lightId = -1;
 219      gPolymerLight[s].lightptr = NULL;
 220  }
 221  
 222  #endif
 223  
 224  
 225  void InsertSpriteSect(int nSprite, int nSector)
 226  {
 227      dassert(nSprite >= 0 && nSprite < kMaxSprites);
 228      dassert(nSector >= 0 && nSector < kMaxSectors);
 229      int nOther = headspritesect[nSector];
 230      if (nOther >= 0)
 231      {
 232          prevspritesect[nSprite] = prevspritesect[nOther];
 233          nextspritesect[nSprite] = -1;
 234          nextspritesect[prevspritesect[nOther]] = nSprite;
 235          prevspritesect[nOther] = nSprite;
 236      }
 237      else
 238      {
 239          prevspritesect[nSprite] = nSprite;
 240          nextspritesect[nSprite] = -1;
 241          headspritesect[nSector] = nSprite;
 242      }
 243      sprite[nSprite].sectnum = nSector;
 244  }
 245  
 246  void RemoveSpriteSect(int nSprite)
 247  {
 248      dassert(nSprite >= 0 && nSprite < kMaxSprites);
 249      int nSector = sprite[nSprite].sectnum;
 250      dassert(nSector >= 0 && nSector < kMaxSectors);
 251      int nOther = nextspritesect[nSprite];
 252      if (nOther < 0)
 253      {
 254          nOther = headspritesect[nSector];
 255      }
 256      prevspritesect[nOther] = prevspritesect[nSprite];
 257      if (headspritesect[nSector] != nSprite)
 258      {
 259          nextspritesect[prevspritesect[nSprite]] = nextspritesect[nSprite];
 260      }
 261      else
 262      {
 263          headspritesect[nSector] = nextspritesect[nSprite];
 264      }
 265      sprite[nSprite].sectnum = -1;
 266  }
 267  
 268  void InsertSpriteStat(int nSprite, int nStat)
 269  {
 270      dassert(nSprite >= 0 && nSprite < kMaxSprites);
 271      dassert(nStat >= 0 && nStat <= kMaxStatus);
 272      int nOther = headspritestat[nStat];
 273      if (nOther >= 0)
 274      {
 275          prevspritestat[nSprite] = prevspritestat[nOther];
 276          nextspritestat[nSprite] = -1;
 277          nextspritestat[prevspritestat[nOther]] = nSprite;
 278          prevspritestat[nOther] = nSprite;
 279      }
 280      else
 281      {
 282          prevspritestat[nSprite] = nSprite;
 283          nextspritestat[nSprite] = -1;
 284          headspritestat[nStat] = nSprite;
 285      }
 286      sprite[nSprite].statnum = nStat;
 287      gStatCount[nStat]++;
 288  }
 289  
 290  void RemoveSpriteStat(int nSprite)
 291  {
 292      dassert(nSprite >= 0 && nSprite < kMaxSprites);
 293      int nStat = sprite[nSprite].statnum;
 294      dassert(nStat >= 0 && nStat <= kMaxStatus);
 295      int nOther = nextspritestat[nSprite];
 296      if (nOther < 0)
 297      {
 298          nOther = headspritestat[nStat];
 299      }
 300      prevspritestat[nOther] = prevspritestat[nSprite];
 301      if (headspritestat[nStat] != nSprite)
 302      {
 303          nextspritestat[prevspritestat[nSprite]] = nextspritestat[nSprite];
 304      }
 305      else
 306      {
 307          headspritestat[nStat] = nextspritestat[nSprite];
 308      }
 309      sprite[nSprite].statnum = kStatNothing;
 310      gStatCount[nStat]--;
 311  }
 312  
 313  void qinitspritelists(void) // Replace
 314  {
 315      for (short i = 0; i <= kMaxSectors; i++)
 316      {
 317          headspritesect[i] = -1;
 318      }
 319      for (short i = 0; i <= kMaxStatus; i++)
 320      {
 321          headspritestat[i] = -1;
 322      }
 323      int const nMaxSprites = VanillaMode() ? 4096 : kMaxSprites;
 324      for (short i = 0; i < nMaxSprites; i++)
 325      {
 326          sprite[i].sectnum = -1;
 327          sprite[i].index = -1;
 328          InsertSpriteStat(i, kMaxStatus);
 329      }
 330      memset(gStatCount, 0, sizeof(gStatCount));
 331      Numsprites = 0;
 332  }
 333  
 334  int InsertSprite(int nSector, int nStat)
 335  {
 336      int nSprite = headspritestat[kMaxStatus];
 337      dassert(nSprite < kMaxSprites);
 338      if (nSprite < 0)
 339      {
 340          return nSprite;
 341      }
 342      RemoveSpriteStat(nSprite);
 343      spritetype *pSprite = &sprite[nSprite];
 344      memset(&sprite[nSprite], 0, sizeof(spritetype));
 345      InsertSpriteStat(nSprite, nStat);
 346      InsertSpriteSect(nSprite, nSector);
 347      pSprite->cstat = 128;
 348      pSprite->clipdist = 32;
 349      pSprite->xrepeat = pSprite->yrepeat = 64;
 350      pSprite->owner = -1;
 351      pSprite->extra = -1;
 352      pSprite->index = nSprite;
 353      xvel[nSprite] = yvel[nSprite] = zvel[nSprite] = 0;
 354      qsprite_filler[nSprite] = 0;
 355  
 356  #ifdef POLYMER
 357      gPolymerLight[nSprite].lightId = -1;
 358  #endif
 359  
 360      Numsprites++;
 361  
 362      return nSprite;
 363  }
 364  
 365  int qinsertsprite(short nSector, short nStat) // Replace
 366  {
 367      return InsertSprite(nSector, nStat);
 368  }
 369  
 370  int DeleteSprite(int nSprite)
 371  {
 372  #ifdef POLYMER
 373      if (gPolymerLight[nSprite].lightptr != NULL && videoGetRenderMode() == REND_POLYMER)
 374          DeleteLight(nSprite);
 375  #endif
 376      if (sprite[nSprite].extra > 0)
 377      {
 378          dbDeleteXSprite(sprite[nSprite].extra);
 379      }
 380      dassert(sprite[nSprite].statnum >= 0 && sprite[nSprite].statnum < kMaxStatus);
 381      RemoveSpriteStat(nSprite);
 382      dassert(sprite[nSprite].sectnum >= 0 && sprite[nSprite].sectnum < kMaxSectors);
 383      RemoveSpriteSect(nSprite);
 384      InsertSpriteStat(nSprite, kMaxStatus);
 385  
 386      Numsprites--;
 387  
 388      return nSprite;
 389  }
 390  
 391  int qdeletesprite(short nSprite) // Replace
 392  {
 393      return DeleteSprite(nSprite);
 394  }
 395  
 396  int ChangeSpriteSect(int nSprite, int nSector)
 397  {
 398      dassert(nSprite >= 0 && nSprite < kMaxSprites);
 399      dassert(nSector >= 0 && nSector < kMaxSectors);
 400      dassert(sprite[nSprite].sectnum >= 0 && sprite[nSprite].sectnum < kMaxSectors);
 401      RemoveSpriteSect(nSprite);
 402      InsertSpriteSect(nSprite, nSector);
 403      return 0;
 404  }
 405  
 406  int qchangespritesect(short nSprite, short nSector)
 407  {
 408      return ChangeSpriteSect(nSprite, nSector);
 409  }
 410  
 411  int ChangeSpriteStat(int nSprite, int nStatus)
 412  {
 413      dassert(nSprite >= 0 && nSprite < kMaxSprites);
 414      dassert(nStatus >= 0 && nStatus < kMaxStatus);
 415      dassert(sprite[nSprite].statnum >= 0 && sprite[nSprite].statnum < kMaxStatus);
 416      dassert(sprite[nSprite].sectnum >= 0 && sprite[nSprite].sectnum < kMaxSectors);
 417      RemoveSpriteStat(nSprite);
 418      InsertSpriteStat(nSprite, nStatus);
 419      return 0;
 420  }
 421  
 422  int qchangespritestat(short nSprite, short nStatus)
 423  {
 424      return ChangeSpriteStat(nSprite, nStatus);
 425  }
 426  
 427  unsigned short nextXSprite[kMaxXSprites];
 428  unsigned short nextXWall[kMaxXWalls];
 429  unsigned short nextXSector[kMaxXSectors];
 430  
 431  void InitFreeList(unsigned short *pList, int nCount)
 432  {
 433      for (int i = 1; i < nCount; i++)
 434      {
 435          pList[i] = i-1;
 436      }
 437      pList[0] = nCount - 1;
 438  }
 439  
 440  void InsertFree(unsigned short *pList, int nIndex)
 441  {
 442      pList[nIndex] = pList[0];
 443      pList[0] = nIndex;
 444  }
 445  
 446  unsigned short dbInsertXSprite(int nSprite)
 447  {
 448      int nXSprite = nextXSprite[0];
 449      nextXSprite[0] = nextXSprite[nXSprite];
 450      if (nXSprite == 0)
 451      {
 452          ThrowError("Out of free XSprites");
 453      }
 454      memset(&xsprite[nXSprite], 0, sizeof(XSPRITE));
 455  #if 0 // this intentionally causes demos to desync like dos (eg: playing BLOOD002.DEM after finishing BLOOD001.DEM) - for notblood we'll disable it
 456      if (!VanillaMode())
 457  #endif
 458          memset(&gSpriteHit[nXSprite], 0, sizeof(SPRITEHIT));
 459      xsprite[nXSprite].reference = nSprite;
 460      sprite[nSprite].extra = nXSprite;
 461      return nXSprite;
 462  }
 463  
 464  void dbDeleteXSprite(int nXSprite)
 465  {
 466      dassert(xsprite[nXSprite].reference >= 0);
 467      dassert(sprite[xsprite[nXSprite].reference].extra == nXSprite);
 468      InsertFree(nextXSprite, nXSprite);
 469      sprite[xsprite[nXSprite].reference].extra = -1;
 470      xsprite[nXSprite].reference = -1;
 471  }
 472  
 473  unsigned short dbInsertXWall(int nWall)
 474  {
 475      int nXWall = nextXWall[0];
 476      nextXWall[0] = nextXWall[nXWall];
 477      if (nXWall == 0)
 478      {
 479          ThrowError("Out of free XWalls");
 480      }
 481      memset(&xwall[nXWall], 0, sizeof(XWALL));
 482      xwall[nXWall].reference = nWall;
 483      wall[nWall].extra = nXWall;
 484      return nXWall;
 485  }
 486  
 487  void dbDeleteXWall(int nXWall)
 488  {
 489      dassert(xwall[nXWall].reference >= 0);
 490      InsertFree(nextXWall, nXWall);
 491      wall[xwall[nXWall].reference].extra = -1;
 492      xwall[nXWall].reference = -1;
 493  }
 494  
 495  unsigned short dbInsertXSector(int nSector)
 496  {
 497      int nXSector = nextXSector[0];
 498      nextXSector[0] = nextXSector[nXSector];
 499      if (nXSector == 0)
 500      {
 501          ThrowError("Out of free XSectors");
 502      }
 503      memset(&xsector[nXSector], 0, sizeof(XSECTOR));
 504      xsector[nXSector].reference = nSector;
 505      sector[nSector].extra = nXSector;
 506      return nXSector;
 507  }
 508  
 509  void dbDeleteXSector(int nXSector)
 510  {
 511      dassert(xsector[nXSector].reference >= 0);
 512      InsertFree(nextXSector, nXSector);
 513      sector[xsector[nXSector].reference].extra = -1;
 514      xsector[nXSector].reference = -1;
 515  }
 516  
 517  void dbXSpriteClean(void)
 518  {
 519      for (int i = 0; i < kMaxSprites; i++)
 520      {
 521          int nXSprite = sprite[i].extra;
 522          if (nXSprite == 0)
 523          {
 524              sprite[i].extra = -1;
 525          }
 526          if (sprite[i].statnum < kMaxStatus && nXSprite > 0)
 527          {
 528              dassert(nXSprite < kMaxXSprites);
 529              if (xsprite[nXSprite].reference != i)
 530              {
 531                  int nXSprite2 = dbInsertXSprite(i);
 532                  memcpy(&xsprite[nXSprite2], &xsprite[nXSprite], sizeof(XSPRITE));
 533                  xsprite[nXSprite2].reference = i;
 534              }
 535          }
 536      }
 537      for (int i = 1; i < kMaxXSprites; i++)
 538      {
 539          int nSprite = xsprite[i].reference;
 540          if (nSprite >= 0)
 541          {
 542              dassert(nSprite < kMaxSprites);
 543              if (sprite[nSprite].statnum >= kMaxStatus || sprite[nSprite].extra != i)
 544              {
 545                  InsertFree(nextXSprite, i);
 546                  xsprite[i].reference = -1;
 547              }
 548          }
 549      }
 550  }
 551  
 552  void dbXWallClean(void)
 553  {
 554      for (int i = 0; i < numwalls; i++)
 555      {
 556          int nXWall = wall[i].extra;
 557          if (nXWall == 0)
 558          {
 559              wall[i].extra = -1;
 560          }
 561          if (nXWall > 0)
 562          {
 563              dassert(nXWall < kMaxXWalls);
 564              if (xwall[nXWall].reference == -1)
 565              {
 566                  wall[i].extra = -1;
 567              }
 568              else
 569              {
 570                  xwall[nXWall].reference = i;
 571              }
 572          }
 573      }
 574      for (int i = 0; i < numwalls; i++)
 575      {
 576          int nXWall = wall[i].extra;
 577          if (nXWall > 0)
 578          {
 579              dassert(nXWall < kMaxXWalls);
 580              if (xwall[nXWall].reference != i)
 581              {
 582                  int nXWall2 = dbInsertXWall(i);
 583                  memcpy(&xwall[nXWall2], &xwall[nXWall], sizeof(XWALL));
 584                  xwall[nXWall2].reference = i;
 585              }
 586          }
 587      }
 588      for (int i = 1; i < kMaxXWalls; i++)
 589      {
 590          int nWall = xwall[i].reference;
 591          if (nWall >= 0)
 592          {
 593              dassert(nWall < kMaxWalls);
 594              if (nWall >= numwalls || wall[nWall].extra != i)
 595              {
 596                  InsertFree(nextXWall, i);
 597                  xwall[i].reference = -1;
 598              }
 599          }
 600      }
 601  }
 602  
 603  void dbXSectorClean(void)
 604  {
 605  
 606  
 607      for (int i = 0; i < numsectors; i++)
 608      {
 609          int nXSector = sector[i].extra;
 610          if (nXSector == 0)
 611          {
 612              sector[i].extra = -1;
 613          }
 614          if (nXSector > 0)
 615          {
 616              dassert(nXSector < kMaxXSectors);
 617              if (xsector[nXSector].reference == -1)
 618              {
 619                  sector[i].extra = -1;
 620              }
 621              else
 622              {
 623                  xsector[nXSector].reference = i;
 624              }
 625          }
 626      }
 627      for (int i = 0; i < numsectors; i++)
 628      {
 629          int nXSector = sector[i].extra;
 630          if (nXSector > 0)
 631          {
 632              dassert(nXSector < kMaxXSectors);
 633              if (xsector[nXSector].reference != i)
 634              {
 635                  int nXSector2 = dbInsertXSector(i);
 636                  memcpy(&xsector[nXSector2], &xsector[nXSector], sizeof(XSECTOR));
 637                  xsector[nXSector2].reference = i;
 638              }
 639          }
 640      }
 641      for (int i = 1; i < kMaxXSectors; i++)
 642      {
 643          int nSector = xsector[i].reference;
 644          if (nSector >= 0)
 645          {
 646              dassert(nSector < kMaxSectors);
 647              if (nSector >= numsectors || sector[nSector].extra != i)
 648              {
 649                  InsertFree(nextXSector, i);
 650                  xsector[i].reference = -1;
 651              }
 652          }
 653      }
 654  }
 655  
 656  void dbInit(void)
 657  {
 658      InitFreeList(nextXSprite, kMaxXSprites);
 659      for (int i = 1; i < kMaxXSprites; i++)
 660      {
 661          xsprite[i].reference = -1;
 662      }
 663      InitFreeList(nextXWall, kMaxXWalls);
 664      for (int i = 1; i < kMaxXWalls; i++)
 665      {
 666          xwall[i].reference = -1;
 667      }
 668      InitFreeList(nextXSector, kMaxXSectors);
 669      for (int i = 1; i < kMaxXSectors; i++)
 670      {
 671          xsector[i].reference = -1;
 672      }
 673      initspritelists();
 674      for (int i = 0; i < kMaxSprites; i++)
 675      {
 676          sprite[i].cstat = 128;
 677      }
 678  }
 679  
 680  void PropagateMarkerReferences(void)
 681  {
 682      int nSprite, nNextSprite;
 683      for (nSprite = headspritestat[kStatMarker]; nSprite != -1; nSprite = nNextSprite) {
 684          
 685          nNextSprite = nextspritestat[nSprite];
 686          
 687          switch (sprite[nSprite].type)  {
 688              case kMarkerOff:
 689              case kMarkerAxis:
 690              case kMarkerWarpDest: {
 691                  int nOwner = sprite[nSprite].owner;
 692                  if (nOwner >= 0 && nOwner < numsectors) {
 693                      int nXSector = sector[nOwner].extra;
 694                      if (nXSector > 0 && nXSector < kMaxXSectors) {
 695                          xsector[nXSector].marker0 = nSprite;
 696                          continue;
 697                      }
 698                  }
 699              }
 700              break;
 701              case kMarkerOn: {
 702                  int nOwner = sprite[nSprite].owner;
 703                  if (nOwner >= 0 && nOwner < numsectors) {
 704                      int nXSector = sector[nOwner].extra;
 705                      if (nXSector > 0 && nXSector < kMaxXSectors) {
 706                          xsector[nXSector].marker1 = nSprite;
 707                          continue;
 708                      }
 709                  }
 710              }
 711              break;
 712          }
 713          
 714          DeleteSprite(nSprite);
 715      }
 716  }
 717  
 718  char dbIsBannedSpriteType(int nType)
 719  {
 720      const unsigned int nBannedType = gGameOptions.uSpriteBannedFlags;
 721      if (nBannedType == BANNED_NONE) // no monsters banned, return
 722          return 0;
 723      char bBanned = 0;
 724  
 725      // monsters
 726      if (!bBanned && (nBannedType&BANNED_BATS))
 727          bBanned = nType == kDudeBat;
 728      if (!bBanned && (nBannedType&BANNED_RATS))
 729          bBanned = nType == kDudeRat;
 730      if (!bBanned && (nBannedType&BANNED_FISH))
 731          bBanned = (nType == kDudeGillBeast) || (nType == kDudeBoneEel);
 732      if (!bBanned && (nBannedType&BANNED_HANDS))
 733          bBanned = nType == kDudeHand;
 734      if (!bBanned && (nBannedType&BANNED_GHOSTS))
 735          bBanned = nType == kDudePhantasm;
 736      if (!bBanned && (nBannedType&BANNED_SPIDERS))
 737          bBanned = (nType == kDudeSpiderBrown) || (nType == kDudeSpiderRed) || (nType == kDudeSpiderBlack);
 738      if (!bBanned && (nBannedType&BANNED_TCALEBS))
 739          bBanned = nType == kDudeTinyCaleb;
 740      if (!bBanned && (nBannedType&BANNED_HHOUNDS))
 741          bBanned = nType == kDudeHellHound;
 742  
 743      // weapons
 744      if (!bBanned && (nBannedType&BANNED_FLARE))
 745          bBanned = (nType == kItemWeaponFlarePistol) || (nType == kItemAmmoFlares);
 746      if (!bBanned && (nBannedType&BANNED_SHOTGUN))
 747          bBanned = (nType == kItemWeaponSawedoff) || (nType == kItemAmmoSawedoffFew) || (nType == kItemAmmoSawedoffBox);
 748      if (!bBanned && (nBannedType&BANNED_TOMMYGUN))
 749          bBanned = (nType == kItemWeaponTommygun) || (nType == kItemAmmoTommygunFew) || (nType == kItemAmmoTommygunDrum);
 750      if (!bBanned && (nBannedType&BANNED_NAPALM))
 751          bBanned = (nType == kItemWeaponNapalmLauncher) || (nType == kItemAmmoGasolineCan);
 752      if (!bBanned && (nBannedType&BANNED_TNT))
 753          bBanned = (nType == kItemWeaponTNT) || (nType == kItemAmmoTNTBundle) || (nType == kItemAmmoTNTBox);
 754      if (!bBanned && (nBannedType&BANNED_SPRAYCAN))
 755          bBanned = (nType == kItemWeaponSprayCan) || (nType == kItemAmmoSprayCan);
 756      if (!bBanned && (nBannedType&BANNED_TESLA))
 757          bBanned = (nType == kItemWeaponTeslaCannon) || (nType == kItemAmmoTeslaCharge);
 758      if (!bBanned && (nBannedType&BANNED_LIFELEECH))
 759          bBanned = (nType == kItemWeaponLifeLeech) || (nType == kItemAmmoTrappedSoul);
 760      if (!bBanned && (nBannedType&BANNED_VOODOO))
 761          bBanned = (nType == kItemWeaponVoodooDoll) || (nType == kItemAmmoVoodooDoll);
 762      if (!bBanned && (nBannedType&BANNED_PROXY))
 763          bBanned = nType == kItemAmmoProxBombBundle;
 764      if (!bBanned && (nBannedType&BANNED_REMOTE))
 765          bBanned = nType == kItemAmmoRemoteBombBundle;
 766  
 767      // items
 768      if (!bBanned && (nBannedType&BANNED_MEDKIT))
 769          bBanned = nType == kItemHealthDoctorBag;
 770      if (!bBanned && (nBannedType&BANNED_LIFEESSENCE)) // life essence, and other misc healing items
 771          bBanned = (nType == kItemHealthMedPouch) || (nType == kItemHealthLifeEssense) || (nType == kItemHealthRedPotion);
 772      if (!bBanned && (nBannedType&BANNED_LIFESEED))
 773          bBanned = nType == kItemHealthLifeSeed;
 774      if (!bBanned && (nBannedType&BANNED_SUPERARMOR))
 775          bBanned = nType == kItemArmorSuper;
 776      if (!bBanned && (nBannedType&BANNED_CRYSTALBALL))
 777          bBanned = nType == kItemCrystalBall;
 778  
 779      // powerups
 780      if (!bBanned && (nBannedType&BANNED_JUMPBOOTS))
 781          bBanned = nType == kItemJumpBoots;
 782      if (!bBanned && (nBannedType&BANNED_CLOAK))
 783          bBanned = nType == kItemShadowCloak;
 784      if (!bBanned && (nBannedType&BANNED_DEATHMASK))
 785          bBanned = nType == kItemDeathMask;
 786      if (!bBanned && (nBannedType&BANNED_AKIMBO))
 787          bBanned = nType == kItemTwoGuns;
 788      if (!bBanned && (nBannedType&BANNED_REFLECT))
 789          bBanned = nType == kItemReflectShots;
 790  
 791      return bBanned;
 792  }
 793  
 794  char dbIsBannedSprite(spritetype *pSprite, XSPRITE* pXSprite)
 795  {
 796      if (gGameOptions.uSpriteBannedFlags == BANNED_NONE) // no sprites banned, return
 797          return 0;
 798      const char bRemove = dbIsBannedSpriteType(pSprite->type);
 799      if (bRemove && IsDudeSprite(pSprite) && pXSprite)
 800      {
 801          if (pXSprite->key > 0) // drop key
 802              actDropObject(pSprite, kItemKeyBase + (pXSprite->key - 1));
 803          if (pXSprite->dropMsg > 0) // drop item
 804              actDropObject(pSprite, pXSprite->dropMsg);
 805      }
 806      return bRemove;
 807  }
 808  
 809  static uint32_t curRandomizerSeed = 0;
 810  static uint32_t curRandomizerSeedDudes = 0;
 811  static uint32_t curRandomizerSeedThings = 0;
 812  
 813  inline int dbRandomizerRNG(const int range)
 814  {
 815      curRandomizerSeed = (214013 * curRandomizerSeed + 2531011);
 816      return mulscale15(((curRandomizerSeed >> 16) & 0x7FFF), range);
 817  }
 818  
 819  inline int dbRandomizerRNGDudes(const int range)
 820  {
 821      curRandomizerSeedDudes = (214013 * curRandomizerSeedDudes + 2531011);
 822      return mulscale15(((curRandomizerSeedDudes >> 16) & 0x7FFF), range);
 823  }
 824  
 825  inline int dbRandomizerRNGThings(const int range)
 826  {
 827      curRandomizerSeedThings = (214013 * curRandomizerSeedThings + 2531011);
 828      return mulscale15(((curRandomizerSeedThings >> 16) & 0x7FFF), range);
 829  }
 830  
 831  void dbRandomizerModeInit(void)
 832  {
 833      // if adding a new seed cheat, ensure that you do the following:
 834      // - add the new entry to the switch statement in dbRandomizerMode()
 835      // - updating range cheat range for dbRandomizerModeScale()
 836      const char randomizerCheats[][sizeof(gzRandomizerSeed)] =
 837      {
 838          "AAAAAAAA", // phantoms only
 839          "BUTCHERS", // butchers only
 840          "SOULSEEK", // hands only
 841          "EPISODE6", // cultists only
 842          "GARGOYLE", // gargoyles only
 843          "FLAMEDOG", // hell hounds only
 844          "CAPYBARA", // rats only
 845          "HURTSCAN", // shotgun/tommy gun cultists only
 846          "HUGEFISH", // gill beasts only
 847          "SHOCKING", // tesla cultists only
 848          "CRUONITA", // boss types only
 849          "BILLYRAY", // shotgun cultists only
 850          "WEED420!", // cultists only but they're green (and make you dizzy on damage)
 851          "BRAAAINS", // zombies only
 852          "OKBOOMER", // tnt cultists only
 853          "OKZOOMER", // tnt/tesla cultists only
 854          "SNEAKYFU", // prone shotgun/tommy gun cultists only
 855      };
 856  
 857      const uint32_t defaultSeed = 0xCA1EB666;
 858      gGameOptions.nRandomizerCheat = -1;
 859      if (gGameOptions.szRandomizerSeed[0] == '\0') // if seed is empty, generate a new one
 860      {
 861          if (gGameOptions.nGameType != kGameTypeSinglePlayer) // if in multiplayer, use a failsafe seed
 862              curRandomizerSeed = defaultSeed;
 863          else // in single-player
 864              curRandomizerSeed = QRandom2(defaultSeed);
 865          curRandomizerSeedThings = curRandomizerSeedDudes = curRandomizerSeed;
 866          return;
 867      }
 868  
 869      for (int curCheat = 0; curCheat < (int)ARRAY_SSIZE(randomizerCheats); curCheat++) // compare seed against any cheat seed entries
 870      {
 871          for (int i = 0; i < (int)sizeof(gzRandomizerSeed); i++)
 872          {
 873              const char charSeed = toupper(gGameOptions.szRandomizerSeed[i]);
 874              const char charSeedCheat = toupper(randomizerCheats[curCheat][i]);
 875              if (charSeed != charSeedCheat) // if encountered a non-matching character, stop and check next entry
 876              {
 877                  gGameOptions.nRandomizerCheat = -1;
 878                  break;
 879              }
 880              else if ((charSeedCheat == '\0') || ((i+1) == (int)sizeof(gzRandomizerSeed))) // if matched cheat seed completely and reached end of string
 881              {
 882                  gGameOptions.nRandomizerCheat = curCheat; // set to seed cheat index
 883                  break;
 884              }
 885          }
 886          if (gGameOptions.nRandomizerCheat != -1) // if found a match, stop searching
 887              break;
 888      }
 889  
 890      if ((gGameOptions.nRandomizerCheat != -1) && (gGameOptions.nGameType == kGameTypeSinglePlayer)) // if seed cheat is active and in single-player
 891          curRandomizerSeed = qrand(); // always re-roll random seed when cheat seed mode is active
 892      else
 893          curRandomizerSeed = defaultSeed; // reset seed
 894      for (int i = 0; i < (int)sizeof(gGameOptions.szRandomizerSeed); i++) // shitty seed system, but if it works for the N64's CIC then who am I to judge
 895      {
 896          if (gGameOptions.szRandomizerSeed[i] == '\0')
 897              break;
 898          curRandomizerSeed += ~(toupper(gGameOptions.szRandomizerSeed[i]));
 899      }
 900      curRandomizerSeedThings = curRandomizerSeedDudes = curRandomizerSeed;
 901  }
 902  
 903  void dbRandomizerMode(spritetype *pSprite)
 904  {
 905      if (pSprite == NULL) // invalid sprite, don't bother processing
 906          return;
 907      if (!spriRangeIsFine(pSprite->index))
 908          return;
 909  
 910      if ((gGameOptions.nRandomizerMode >= 2) && (pSprite->type == kItemBeastVision)) // always replace beast vision if pickups or enemies+pickups mode
 911      {
 912          const int pickupsrngtype[] = {kItemArmorBasic, kItemArmorBody, kItemArmorFire, kItemWeaponFlarePistol, kItemWeaponSawedoff, kItemWeaponTommygun, kItemAmmoTNTBox, kItemAmmoSawedoffBox, kItemAmmoTommygunDrum};
 913          const int pickupsrngpicnum[] = {2628, 2586, 2578, 524, 559, 558, 809, 812, 817};
 914          const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
 915          pSprite->type = pickupsrngtype[rng];
 916          pSprite->picnum = pickupsrngpicnum[rng];
 917          if ((pSprite->type < kThingBase) || (pSprite->type >= kThingMax)) // update sprite type
 918              changespritestat(pSprite->index, kStatItem);
 919      }
 920  
 921      if ((gGameOptions.nRandomizerMode & 1) && (gGameOptions.nRandomizerCheat != -1)) // if enemies or enemies+weapons mode, and seed cheat is active, replace enemy
 922      {
 923          const int type = pSprite->type;
 924  #ifdef NOONE_EXTENSIONS
 925          if (type == kDudeModernCustom) // ignore custom dude types
 926              return;
 927  #endif
 928          if ((type >= kDudeCultistTommy) && (type <= kDudeBurningBeast) && !(type >= kDudePlayer1 && type <= kDudePlayer8) && (type != kDudeCultistReserved) && (type != kDudeBeast) && (type != kDudeCultistBeast) && (type != kDudeGargoyleStone) && (type != kDudeTchernobog) && (type != kDudeCerberusTwoHead) && (type != kDudeCerberusOneHead) && (type != kDudeSpiderMother)) // filter problematic enemy types
 929          {
 930              pSprite->flags |= kPhysGravity;
 931              switch (gGameOptions.nRandomizerCheat) // replace enemy according to cheat type
 932              {
 933              case  0: // "AAAAAAAA" - phantoms only
 934                  pSprite->type = kDudePhantasm;
 935                  break;
 936              case  1: // "BUTCHERS" - butchers only
 937                  pSprite->type = kDudeZombieButcher;
 938                  break;
 939              case  2: // "SOULSEEK" - hands only
 940                  pSprite->type = kDudeHand;
 941                  break;
 942              case  3: // "EPISODE6" - cultists only
 943              {
 944                  const int enemiesrng[] = {kDudeCultistTommy, kDudeCultistShotgun, kDudeCultistTommyProne, kDudeCultistShotgunProne, kDudeCultistTesla, kDudeCultistTNT};
 945                  pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
 946                  break;
 947              }
 948              case  4: // "GARGOYLE" - gargoyles only
 949                  pSprite->type = dbRandomizerRNGDudes(20) ? kDudeGargoyleFlesh : kDudeGargoyleStone;
 950                  pSprite->flags &= ~kPhysGravity;
 951                  break;
 952              case  5: // "FLAMEDOG" - hell hounds only
 953                  pSprite->type = dbRandomizerRNGDudes(20) ? kDudeHellHound : kDudeCerberusOneHead;
 954                  break;
 955              case  6: // "CAPYBARA" - rats only
 956                  pSprite->type = kDudeRat;
 957                  break;
 958              case  7: // "HURTSCAN" - shotgun/tommy gun cultists only
 959              {
 960                  const int enemiesrng[] = {kDudeCultistTommy, kDudeCultistShotgun, kDudeCultistTommyProne, kDudeCultistShotgunProne};
 961                  pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
 962                  break;
 963              }
 964              case  8: // "HUGEFISH" - gill beasts only
 965                  pSprite->type = kDudeGillBeast;
 966                  break;
 967              case  9: // "SHOCKING" - tesla cultists only
 968                  pSprite->type = kDudeCultistTesla;
 969                  break;
 970              case 10: // "CRUONITA" - boss types only
 971              {
 972                  const int enemiesrng[] = {kDudeCultistBeast, kDudeTchernobog, kDudeCerberusTwoHead, kDudeSpiderMother, kDudeGargoyleStone};
 973                  pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
 974                  break;
 975              }
 976              case 11: // "BILLYRAY" - shotgun cultists only
 977              {
 978                  const int enemiesrng[] = {kDudeCultistShotgun, kDudeCultistShotgunProne};
 979                  pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
 980                  break;
 981              }
 982              case 12: // "WEED420!" - cultists only but they're green (and make you dizzy on damage)
 983              {
 984                  const int enemiesrng[] = {kDudeCultistShotgun, kDudeCultistShotgunProne, kDudeCultistTNT};
 985                  pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
 986                  pSprite->pal = 4; // DUDE WEED XDDD 420 bro
 987                  break;
 988              }
 989              case 13: // "BRAAAINS" - zombies only
 990                  pSprite->type = kDudeZombieAxeNormal;
 991                  break;
 992              case 14: // "OKBOOMER" - tnt cultists only
 993                  pSprite->type = kDudeCultistTNT;
 994                  break;
 995              case 15: // "OKZOOMER" - tnt/tesla cultists only
 996              {
 997                  const int enemiesrng[] = {kDudeCultistTesla, kDudeCultistTNT};
 998                  pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
 999                  break;
1000              }
1001              case 16: // "SNEAKYFU" - prone shotgun/tommy gun cultists only
1002              {
1003                  const int enemiesrng[] = {kDudeCultistShotgunProne, kDudeCultistTommyProne};
1004                  pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1005                  break;
1006              }
1007              default: // unknown cheat id, don't do anything
1008              {
1009                  static char bShownError = 0;
1010                  if (!bShownError) // only show once per session
1011                      LOG_F(WARNING, "Error invalid cheat seed %s (%d)\nAdd seed cheat effect to func dbRandomizerMode()", gGameOptions.szRandomizerSeed, gGameOptions.nRandomizerCheat);
1012                  bShownError = 1;
1013                  break;
1014              }
1015              }
1016              if (gGameOptions.nRandomizerCheat != 12)
1017                  pSprite->pal = 0;
1018              pSprite->cstat &= ~CSTAT_SPRITE_YFLIP;
1019              return;
1020          }
1021      }
1022  
1023      if (!dbRandomizerRNG(5)) return;
1024  
1025      if (gGameOptions.nRandomizerMode & 1) // if enemies or enemies+weapons mode, randomize enemy
1026      {
1027          switch (pSprite->type)
1028          {
1029          case kDudeInnocent:
1030          {
1031              const int enemiesrng[] = {kDudeBat, kDudeRat, kDudeZombieAxeNormal, kDudeGillBeast, kDudeHand, kDudeCultistShotgunProne};
1032              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1033              break;
1034          }
1035          case kDudeBat:
1036          {
1037              const int enemiesrng[] = {kDudeRat, kDudeZombieAxeLaying, kDudeHand, kDudeInnocent, kDudeCultistTNT, kDudePhantasm};
1038              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1039              pSprite->cstat &= ~CSTAT_SPRITE_YFLIP;
1040              break;
1041          }
1042          case kDudeRat:
1043          {
1044              const int enemiesrng[] = {kDudeBat, kDudeZombieAxeBuried, kDudeHand, kDudeTinyCaleb, kDudeSpiderBrown, kDudeSpiderRed};
1045              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1046              break;
1047          }
1048          case kDudeZombieAxeBuried:
1049          {
1050              const int enemiesrng[] = {kDudeSpiderRed, kDudeZombieAxeLaying, kDudeGillBeast, kDudeZombieButcher, kDudeCultistShotgun, kDudeCultistTommy, kDudeCultistTNT};
1051              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1052              break;
1053          }
1054          case kDudeZombieAxeLaying:
1055          {
1056              const int enemiesrng[] = {kDudeSpiderRed, kDudeZombieAxeBuried, kDudeGillBeast, kDudeZombieButcher, kDudeCultistShotgunProne, kDudeCultistTNT};
1057              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1058              break;
1059          }
1060          case kDudeZombieAxeNormal:
1061          {
1062              const int enemiesrng[] = {kDudeZombieAxeBuried, kDudeZombieAxeLaying, kDudeGillBeast, kDudeZombieButcher, kDudeCultistShotgun, kDudeCultistTommy, kDudeTinyCaleb, kDudeCultistTNT};
1063              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1064              break;
1065          }
1066          case kDudeBoneEel:
1067          {
1068              const int enemiesrng[] = {kDudeZombieAxeNormal, kDudeGillBeast, kDudeHand, kDudeCultistShotgunProne, kDudeInnocent, kDudeCultistTNT};
1069              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1070              break;
1071          }
1072          case kDudeGillBeast:
1073          {
1074              const int enemiesrng[] = {kDudeZombieAxeNormal, kDudeZombieAxeLaying, kDudeZombieButcher, kDudeCultistShotgun, kDudeCultistTommy, kDudeCultistTNT, kDudeGargoyleFlesh, kDudeCultistShotgunProne, kDudeCultistTesla};
1075              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1076              break;
1077          }
1078          case kDudeZombieButcher:
1079          {
1080              const int enemiesrng[] = {kDudeZombieAxeNormal, kDudeZombieAxeLaying, kDudeGillBeast, kDudeCultistShotgun, kDudeCultistTommy, kDudeCultistTNT, kDudeGargoyleFlesh, kDudePhantasm, kDudeCultistShotgunProne, kDudeHellHound, kDudeCultistTommyProne, kDudeCultistTesla};
1081              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1082              break;
1083          }
1084          case kDudeSpiderBrown:
1085          case kDudeSpiderRed:
1086          {
1087              const int enemiesrng[] = {kDudeHand, kDudeTinyCaleb, kDudeCultistTNT, kDudeHellHound, kDudeCultistTommyProne, kDudeCultistShotgunProne};
1088              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1089              break;
1090          }
1091          case kDudeSpiderBlack:
1092          {
1093              const int enemiesrng[] = {kDudeCultistTNT, kDudeGargoyleFlesh, kDudeCultistShotgunProne, kDudeHellHound, kDudeCultistTommyProne, kDudeCultistTesla, kDudeSpiderMother};
1094              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1095              break;
1096          }
1097          case kDudePhantasm:
1098          {
1099              const int enemiesrng[] = {kDudeBat, kDudeHellHound, kDudeZombieButcher, kDudeCultistTNT, kDudeCultistTommyProne, kDudeGillBeast};
1100              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1101              break;
1102          }
1103          case kDudeHand:
1104          case kDudeTinyCaleb:
1105          {
1106              const int enemiesrng[] = {kDudeSpiderBrown, kDudeSpiderRed, kDudeCultistTNT, kDudePhantasm, kDudeHellHound, kDudeCultistTommyProne, kDudeCultistShotgunProne, kDudeGillBeast, kDudeZombieAxeLaying};
1107              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1108              break;
1109          }
1110          case kDudeTentacleGreen:
1111          case kDudeTentacleFire:
1112          {
1113              const int enemiesrng[] = {kDudePodFire, kDudePodGreen, kDudeCultistShotgunProne, kDudeCultistTommyProne, kDudeZombieAxeBuried};
1114              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1115              break;
1116          }
1117          case kDudeCultistShotgun:
1118          case kDudeCultistTommy:
1119          {
1120              const int enemiesrng[] = {kDudeInnocent, kDudeZombieAxeBuried, kDudeZombieAxeNormal, kDudeGillBeast, kDudeCultistTesla, kDudeCultistTNT, kDudeCultistShotgun, kDudeCultistShotgunProne, kDudeCultistTommyProne};
1121              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1122              break;
1123          }
1124          case kDudeCultistShotgunProne:
1125          case kDudeCultistTommyProne:
1126          {
1127              const int enemiesrng[] = {kDudeGillBeast, kDudeZombieAxeLaying, kDudeSpiderBlack, kDudeCultistShotgun, kDudeCultistTommy, kDudeCultistTNT, kDudeHellHound, kDudeCultistTesla};
1128              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1129              break;
1130          }
1131          case kDudeCultistTNT:
1132          {
1133              const int enemiesrng[] = {kDudeZombieAxeNormal, kDudeGillBeast, kDudeZombieAxeLaying, kDudeCultistTommy, kDudeCultistShotgunProne, kDudeHellHound, kDudeCultistTommyProne, kDudeCultistTesla};
1134              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1135              break;
1136          }
1137          case kDudeGargoyleFlesh:
1138          {
1139              const int enemiesrng[] = {kDudeBat, kDudeCultistTNT, kDudeHellHound, kDudeCultistTommyProne, kDudeCultistTesla};
1140              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1141              break;
1142          }
1143          case kDudeHellHound:
1144          {
1145              const int enemiesrng[] = {kDudeCultistTNT, kDudeCultistTommyProne, kDudeCultistTesla, kDudeGargoyleFlesh};
1146              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1147              break;
1148          }
1149          case kDudeCultistTesla:
1150          {
1151              const int enemiesrng[] = {kDudeCultistTesla, kDudeCultistTesla, kDudeCultistTesla, kDudeGargoyleStone, kDudeCerberusOneHead, kDudeCultistTommyProne, kDudeCultistTNT};
1152              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1153              break;
1154          }
1155          case kDudeCerberusOneHead:
1156          case kDudeCerberusTwoHead:
1157          {
1158              const int enemiesrng[] = {kDudeCerberusTwoHead, kDudeGargoyleStone, kDudeTchernobog, kDudeHellHound, kDudeBeast};
1159              pSprite->type = enemiesrng[dbRandomizerRNGDudes(ARRAY_SSIZE(enemiesrng))];
1160              break;
1161          }
1162          default:
1163              break;
1164          }
1165          if (pSprite->type == kDudeGargoyleFlesh || pSprite->type == kDudeGargoyleStone || pSprite->type == kDudeBat) // set the correct flags needed for flying enemies
1166              pSprite->flags &= ~kPhysGravity;
1167          else
1168              pSprite->flags |= kPhysGravity;
1169          pSprite->pal = 0;
1170      }
1171      if (gGameOptions.nRandomizerMode >= 2) // if pickups or enemies+pickups mode, randomize pickup
1172      {
1173          const int nPicnumOrig = pSprite->picnum;
1174          int nTopOrig, nBottomOrig;
1175          GetSpriteExtents(pSprite, &nTopOrig, &nBottomOrig);
1176  
1177          switch (pSprite->type)
1178          {
1179          case kItemWeaponFlarePistol:
1180          {
1181              const int pickupsrngtype[] = {kItemAmmoSprayCan, kItemWeaponTommygun, kItemWeaponTNT, kItemHealthLifeEssense};
1182              const int pickupsrngpicnum[] = {618, 558, 589, 2169};
1183              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1184              pSprite->type = pickupsrngtype[rng];
1185              pSprite->picnum = pickupsrngpicnum[rng];
1186              break;
1187          }
1188          case kItemWeaponSawedoff:
1189          {
1190              const int pickupsrngtype[] = {kItemWeaponTommygun, kItemAmmoSprayCan, kItemAmmoTNTBox};
1191              const int pickupsrngpicnum[] = {558, 618, 809};
1192              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1193              pSprite->type = pickupsrngtype[rng];
1194              pSprite->picnum = pickupsrngpicnum[rng];
1195              break;
1196          }
1197          case kItemWeaponTommygun:
1198          {
1199              const int pickupsrngtype[] = {kItemWeaponSawedoff, kItemWeaponFlarePistol, kItemAmmoTNTBox};
1200              const int pickupsrngpicnum[] = {559, 524, 809};
1201              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1202              pSprite->type = pickupsrngtype[rng];
1203              pSprite->picnum = pickupsrngpicnum[rng];
1204              break;
1205          }
1206          case kItemAmmoTNTBox:
1207          {
1208              const int pickupsrngtype[] = {kItemAmmoSprayCan, kItemAmmoRemoteBombBundle, kItemAmmoTNTBox};
1209              const int pickupsrngpicnum[] = {618, 810, 809};
1210              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1211              pSprite->type = pickupsrngtype[rng];
1212              pSprite->picnum = pickupsrngpicnum[rng];
1213              break;
1214          }
1215          case kItemWeaponTNT:
1216          case kItemAmmoTNTBundle:
1217          case kItemAmmoProxBombBundle:
1218          case kItemAmmoRemoteBombBundle:
1219          {
1220              const int pickupsrngtype[] = {kItemAmmoSprayCan, kItemAmmoSawedoffFew, kItemAmmoTommygunFew};
1221              const int pickupsrngpicnum[] = {618, 619, 813};
1222              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1223              pSprite->type = pickupsrngtype[rng];
1224              pSprite->picnum = pickupsrngpicnum[rng];
1225              break;
1226          }
1227          case kItemWeaponSprayCan:
1228          {
1229              const int pickupsrngtype[] = {kItemWeaponSawedoff, kItemAmmoTommygunFew};
1230              const int pickupsrngpicnum[] = {559, 813};
1231              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1232              pSprite->type = pickupsrngtype[rng];
1233              pSprite->picnum = pickupsrngpicnum[rng];
1234              break;
1235          }
1236          case kItemWeaponVoodooDoll:
1237          case kItemAmmoTrappedSoul:
1238          {
1239              const int pickupsrngtype[] = {kItemWeaponLifeLeech, kItemWeaponNapalmLauncher, kItemWeaponSawedoff, kItemHealthLifeSeed, kItemTwoGuns, kItemReflectShots, kItemWeaponTeslaCannon};
1240              const int pickupsrngpicnum[] = {800, 526, 559, 2433, 829, 2428, 539};
1241              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1242              pSprite->type = pickupsrngtype[rng];
1243              pSprite->picnum = pickupsrngpicnum[rng];
1244              break;
1245          }
1246          case kItemWeaponTeslaCannon:
1247          {
1248              const int pickupsrngtype[] = {kItemWeaponTommygun, kItemHealthLifeSeed, kItemShadowCloak, kItemReflectShots, kItemWeaponVoodooDoll};
1249              const int pickupsrngpicnum[] = {558, 2433, 896, 2428, 525};
1250              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1251              pSprite->type = pickupsrngtype[rng];
1252              pSprite->picnum = pickupsrngpicnum[rng];
1253              break;
1254          }
1255          case kItemWeaponNapalmLauncher:
1256          case kItemWeaponLifeLeech:
1257          case kItemArmorSuper:
1258          {
1259              const int pickupsrngtype[] = {kItemTwoGuns, kItemReflectShots, kItemWeaponVoodooDoll};
1260              const int pickupsrngpicnum[] = {829, 2428, 525};
1261              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1262              pSprite->type = pickupsrngtype[rng];
1263              pSprite->picnum = pickupsrngpicnum[rng];
1264              break;
1265          }
1266          case kItemHealthDoctorBag:
1267          {
1268              const int pickupsrngtype[] = {kItemHealthLifeEssense, kItemHealthLifeSeed, kItemAmmoSawedoffBox, kItemWeaponTeslaCannon};
1269              const int pickupsrngpicnum[] = {2169, 2433, 812, 539};
1270              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1271              pSprite->type = pickupsrngtype[rng];
1272              pSprite->picnum = pickupsrngpicnum[rng];
1273              break;
1274          }
1275          case kItemAmmoFlares:
1276          {
1277              const int pickupsrngtype[] = {kItemAmmoSprayCan, kItemAmmoRemoteBombBundle, kItemAmmoTNTBundle, kItemAmmoSprayCan, kItemAmmoTNTBox, kItemAmmoGasolineCan};
1278              const int pickupsrngpicnum[] = {618, 810, 589, 618, 809, 801};
1279              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1280              pSprite->type = pickupsrngtype[rng];
1281              pSprite->picnum = pickupsrngpicnum[rng];
1282              break;
1283          }
1284          case kItemAmmoSawedoffBox:
1285          {
1286              const int pickupsrngtype[] = {kItemAmmoTeslaCharge, kItemWeaponSawedoff, kItemAmmoSawedoffBox};
1287              const int pickupsrngpicnum[] = {548, 559, 812};
1288              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1289              pSprite->type = pickupsrngtype[rng];
1290              pSprite->picnum = pickupsrngpicnum[rng];
1291              break;
1292          }
1293          case kItemAmmoSawedoffFew:
1294          {
1295              const int pickupsrngtype[] = {kItemAmmoRemoteBombBundle, kItemAmmoTeslaCharge, kItemAmmoTommygunDrum};
1296              const int pickupsrngpicnum[] = {810, 548, 817};
1297              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1298              pSprite->type = pickupsrngtype[rng];
1299              pSprite->picnum = pickupsrngpicnum[rng];
1300              break;
1301          }
1302          case kItemAmmoTommygunFew:
1303          {
1304              const int pickupsrngtype[] = {kItemAmmoRemoteBombBundle, kItemAmmoTNTBundle, kItemAmmoSawedoffFew};
1305              const int pickupsrngpicnum[] = {810, 589, 619};
1306              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1307              pSprite->type = pickupsrngtype[rng];
1308              pSprite->picnum = pickupsrngpicnum[rng];
1309              break;
1310          }
1311          case kItemArmorBasic:
1312          case kItemArmorBody:
1313          case kItemArmorFire:
1314          case kItemArmorSpirit:
1315          {
1316              const int pickupsrngtype[] = {kItemArmorBasic, kItemArmorBody, kItemArmorFire, kItemArmorSpirit, kItemArmorSuper, kItemHealthLifeEssense, kItemHealthLifeSeed, kItemTwoGuns, kItemReflectShots, kItemShadowCloak, kItemHealthDoctorBag};
1317              const int pickupsrngpicnum[] = {2628, 2586, 2578, 2602, 2594, 2169, 2433, 829, 2428, 896, 519};
1318              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1319              pSprite->type = pickupsrngtype[rng];
1320              pSprite->picnum = pickupsrngpicnum[rng];
1321              break;
1322          }
1323          case kItemTwoGuns:
1324          case kItemReflectShots:
1325          {
1326              const int pickupsrngtype[] = {kItemTwoGuns, kItemReflectShots};
1327              const int pickupsrngpicnum[] = {829, 2428};
1328              const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1329              pSprite->type = pickupsrngtype[rng];
1330              pSprite->picnum = pickupsrngpicnum[rng];
1331              break;
1332          }
1333          case kThingObjectGib: // generic breakable objects
1334          {
1335              if (pSprite->statnum != kStatThing) // unexpected type, don't replace
1336                  break;
1337              if (xspriRangeIsFine(pSprite->extra))
1338              {
1339                  XSPRITE *pXSprite = &xsprite[pSprite->extra];
1340                  if (pXSprite->key > 0) // sprite has key attached, don't replace
1341                      break;
1342                  if (pXSprite->dropMsg > 0) // item has item attached, don't replace
1343                      break;
1344              }
1345              switch (pSprite->picnum)
1346              {
1347                  case 520: // skull cup
1348                  case 521: // wine glass (half-empty)
1349                  case 574: // wine glass (empty)
1350                  case 759: // wine bottle
1351                  {
1352                      const int pickupsrngtype[] = {kItemAmmoSawedoffFew, kItemAmmoTommygunFew, kItemAmmoTNTBundle};
1353                      const int pickupsrngpicnum[] = {619, 813, 589};
1354                      const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1355                      pSprite->type = pickupsrngtype[rng];
1356                      pSprite->picnum = pickupsrngpicnum[rng];
1357                      changespritestat(pSprite->index, kStatItem);
1358                      break;
1359                  }
1360                  case 605: // glass bottle
1361                  case 606: // glass bottle
1362                  case 616: // glass jug
1363                  {
1364                      const int pickupsrngtype[] = {kItemAmmoSawedoffFew, kItemAmmoTommygunFew, kItemAmmoTommygunDrum, kItemAmmoSawedoffBox, kItemAmmoTNTBox};
1365                      const int pickupsrngpicnum[] = {619, 813, 817, 812, 809};
1366                      const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1367                      pSprite->type = pickupsrngtype[rng];
1368                      pSprite->picnum = pickupsrngpicnum[rng];
1369                      changespritestat(pSprite->index, kStatItem);
1370                      break;
1371                  }
1372                  case 834: // montana jar
1373                  case 75: // brain in jar
1374                  case 76: // brain in jar
1375                  {
1376                      if (dbRandomizerRNGThings(2)) // lower chance of replacing object
1377                          break;
1378                      const int pickupsrngtype[] = {kItemHealthLifeEssense, kItemAmmoTommygunDrum, kItemAmmoSawedoffBox, kItemAmmoTNTBox};
1379                      const int pickupsrngpicnum[] = {2169, 817, 812, 809};
1380                      const int rng = dbRandomizerRNGThings(ARRAY_SSIZE(pickupsrngtype));
1381                      pSprite->type = pickupsrngtype[rng];
1382                      pSprite->picnum = pickupsrngpicnum[rng];
1383                      changespritestat(pSprite->index, kStatItem);
1384                      break;
1385                  }
1386                  case 563: // zombie barrel
1387                  case 201: // toxic waste
1388                  {
1389                      if (dbRandomizerRNGThings(5)) // lower chance of replacing object
1390                          break;
1391                      pSprite->type = kThingTNTBarrel; // replace with tnt barrel
1392                      pSprite->picnum = 907;
1393                      changespritestat(pSprite->index, kStatThing);
1394                      break;
1395                  }
1396                  default:
1397                      break;
1398              }
1399          }
1400          default:
1401              break;
1402          }
1403  
1404          if (pSprite->picnum != nPicnumOrig) // if changed sprite type and picnum, readjust position
1405          {
1406              int top, bottom;
1407              GetSpriteExtents(pSprite, &top, &bottom);
1408              if (bottom >= nBottomOrig)
1409                  pSprite->z -= bottom - nBottomOrig;
1410          }
1411      }
1412  }
1413  
1414  void dbRandomizerModeScale(spritetype *pSprite, XSPRITE* pXSprite)
1415  {
1416      const bool bRandomCheatActive = ((gGameOptions.nGameType != kGameTypeSinglePlayer || gRandomizerScaleMode) && (gGameOptions.nRandomizerCheat >= 0) && (gGameOptions.nRandomizerCheat <= 15)) || (gGameOptions.nGameType == kGameTypeSinglePlayer && gRandomizerScaleMode == 2); // only randomize enemy sizes if seed cheats 0-15 are active
1417      if (!bRandomCheatActive)
1418          return;
1419      if (!pXSprite->scale && !dbRandomizerRNGDudes(3)) { // randomly change enemy scale (only if enemy does not have scale set already)
1420          switch (pSprite->type) { // make enemies randomly huge
1421              case kDudeRat:
1422                  pXSprite->scale = ClipRange(dbRandomizerRNGDudes(2048-128)+128, 128, 2048);
1423                  break;
1424              case kDudeHand:
1425                  pXSprite->scale = ClipRange(dbRandomizerRNGDudes(1024-128)+128, 128, 1024);
1426                  break;
1427              case kDudeHellHound:
1428                  pXSprite->scale = ClipRange(dbRandomizerRNGDudes(512-128)+128, 128, 512);
1429                  break;
1430              case kDudeCultistBeast: // boss types
1431              case kDudeTchernobog:
1432              case kDudeCerberusTwoHead:
1433              case kDudeCerberusOneHead:
1434              case kDudeSpiderMother:
1435              case kDudeGargoyleStone:
1436                  pXSprite->scale = ClipRange(dbRandomizerRNGDudes(512-32)+32, 32, 512);
1437                  break;
1438              case kDudeGargoyleFlesh:
1439                  pXSprite->scale = ClipRange(dbRandomizerRNGDudes(512-32)+32, 32, 512);
1440                  break;
1441              case kDudeCultistShotgun: // regular enemies
1442              case kDudeCultistTommy:
1443              case kDudeCultistTommyProne:
1444              case kDudeCultistShotgunProne:
1445                  pXSprite->scale = ClipRange(dbRandomizerRNGDudes(384-128)+128, 128, 384);
1446                  break;
1447              case kDudeCultistTNT:
1448                  pXSprite->scale = ClipRange(dbRandomizerRNGDudes(384-96)+96, 96, 256);
1449                  break;
1450              case kDudeZombieButcher:
1451              case kDudeGillBeast:
1452                  pXSprite->scale = ClipRange(dbRandomizerRNGDudes(384-64)+64, 64, 384);
1453                  break;
1454              default:
1455                  break;
1456          }
1457      }
1458  }
1459  
1460  inline int dbShuffleEnemyList(spritetype **pSpriteList = NULL)
1461  {
1462      int nSprites = 0;
1463      for (int i = headspritestat[kStatDude]; i >= 0; i = nextspritestat[i])
1464      {
1465          const int type = sprite[i].type;
1466          if (!((type >= kDudeBase) && (type < kDudeVanillaMax))) // not an enemy sprite, skip
1467              continue;
1468          switch (type) // filter problematic enemy types
1469          {
1470          case kDudeBurningBeast:
1471          case kDudeBurningInnocent:
1472          case kDudeBurningCultist:
1473          case kDudeBurningZombieAxe:
1474          case kDudeBurningZombieButcher:
1475          case kDudeBurningTinyCaleb:
1476          case kDudeCultistReserved:
1477          case kDudeBeast:
1478          case kDudeCultistBeast:
1479          case kDudeGargoyleStone:
1480          case kDudeTchernobog:
1481          case kDudeCerberusTwoHead:
1482          case kDudeCerberusOneHead:
1483          case kDudeSpiderMother:
1484          case kDudeBoneEel:
1485          case kDudePlayer1:
1486          case kDudePlayer2:
1487          case kDudePlayer3:
1488          case kDudePlayer4:
1489          case kDudePlayer5:
1490          case kDudePlayer6:
1491          case kDudePlayer7:
1492          case kDudePlayer8:
1493              continue;
1494          default:
1495              break;
1496          }
1497          if (pSpriteList)
1498              pSpriteList[nSprites] = &sprite[i];
1499          nSprites++;
1500      }
1501      return nSprites;
1502  }
1503  
1504  void dbShuffleEnemy(void)
1505  {
1506      int nSprites = dbShuffleEnemyList(); // get total enemies to shuffle
1507      if (nSprites < 2) // only two enemies in the level, abort
1508          return;
1509  
1510      nSprites = ClipRange(nSprites, 0, kMaxSprites-1);
1511      spritetype **pSprite = (spritetype **)Bmalloc((nSprites+1) * sizeof(spritetype));
1512      if (!pSprite)
1513          return;
1514      dbShuffleEnemyList(pSprite); // assign sprites to pointer array
1515  
1516      for (int i = 0; i < nSprites; i++) // shuffle enemies
1517      {
1518          int j = qrand() % nSprites;
1519          if (i == j)
1520          {
1521              j = qrand() % nSprites; // re-roll
1522              if (i == j) // bah! just our luck...
1523                  continue;
1524          }
1525  
1526          const int16_t tempType = pSprite[j]->type;
1527          pSprite[j]->type = pSprite[i]->type;
1528          pSprite[i]->type = tempType;
1529  
1530          const int16_t tempPicnum = pSprite[j]->picnum;
1531          pSprite[j]->picnum = pSprite[i]->picnum;
1532          pSprite[i]->picnum = tempPicnum;
1533  
1534          const uint8_t tempPal = pSprite[j]->pal;
1535          pSprite[j]->pal = pSprite[i]->pal;
1536          pSprite[i]->pal = tempPal;
1537  
1538          const uint16_t tempCstat = pSprite[j]->cstat;
1539          pSprite[j]->cstat = pSprite[i]->cstat;
1540          pSprite[i]->cstat = tempCstat;
1541  
1542          const int16_t tempAng = pSprite[j]->ang;
1543          pSprite[j]->ang = pSprite[i]->ang;
1544          pSprite[i]->ang = tempAng;
1545  
1546          const int16_t tempOwner = pSprite[j]->owner;
1547          pSprite[j]->owner = pSprite[i]->owner;
1548          pSprite[i]->owner = tempOwner;
1549  
1550          const int16_t tempInittype = pSprite[j]->inittype;
1551          pSprite[j]->inittype = pSprite[i]->inittype;
1552          pSprite[i]->inittype = tempInittype;
1553      }
1554      Bfree(pSprite);
1555  }
1556  
1557  bool byte_1A76C6, byte_1A76C7, byte_1A76C8;
1558  
1559  MAPHEADER2 byte_19AE44;
1560  
1561  unsigned int dbReadMapCRC(const char *pPath)
1562  {
1563      char name2[BMAX_PATH];
1564      byte_1A76C7 = 0;
1565      byte_1A76C8 = 0;
1566  
1567      int const bakpathsearchmode = pathsearchmode;
1568      pathsearchmode = 1;
1569  
1570      Bstrncpy(name2, pPath, BMAX_PATH);
1571      Bstrupr(name2);
1572      DICTNODE* pNode = *gSysRes.Probe(name2, "MAP");
1573      if (pNode && pNode->flags & DICT_EXTERNAL)
1574      {
1575          gSysRes.RemoveNode(pNode);
1576      }
1577      pNode = gSysRes.Lookup(pPath, "MAP");
1578      if (!pNode)
1579      {
1580          char name2[BMAX_PATH];
1581          Bstrncpy(name2, pPath, BMAX_PATH);
1582          ChangeExtension(name2, "");
1583          pNode = gSysRes.Lookup(name2, "MAP");
1584      }
1585  
1586      if (!pNode)
1587      {
1588          LOG_F(ERROR, "Error opening map file %s", pPath);
1589          pathsearchmode = bakpathsearchmode;
1590          return -1;
1591      }
1592      char *pData = (char*)gSysRes.Lock(pNode);
1593      pathsearchmode = bakpathsearchmode;
1594  
1595      int nSize = pNode->size;
1596      MAPSIGNATURE header;
1597      IOBuffer(nSize, pData).Read(&header, 6);
1598  #if B_BIG_ENDIAN == 1
1599      header.version = B_LITTLE16(header.version);
1600  #endif
1601      if (memcmp(header.signature, "BLM\x1a", 4))
1602      {
1603          ThrowError("Map file corrupted");
1604      }
1605      if ((header.version & 0xff00) == 0x600)
1606      {
1607      }
1608      else if ((header.version & 0xff00) == 0x700)
1609      {
1610          byte_1A76C8 = 1;
1611      }
1612      else
1613      {
1614          ThrowError("Map file is wrong version");
1615      }
1616      unsigned int nCRC = *(unsigned int*)(pData+nSize-4);
1617      gSysRes.Unlock(pNode);
1618      return nCRC;
1619  }
1620  
1621  int gMapRev, gSongId, gSkyCount;
1622  //char byte_19AE44[128];
1623  const int nXSectorSize = 60;
1624  const int nXSpriteSize = 56;
1625  const int nXWallSize = 24;
1626  
1627  int dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, short *pSector, unsigned int *pCRC) {
1628      char name2[BMAX_PATH]; int16_t tpskyoff[256];
1629  
1630      memset(show2dsector,0,sizeof(show2dsector));
1631      memset(show2dwall,0,sizeof(show2dwall));
1632      memset(show2dsprite,0,sizeof(show2dsprite));
1633      memset(spriteext,0,kMaxSprites*sizeof(spriteext_t));
1634  
1635      memset(xvel,0,sizeof(xvel));
1636      memset(yvel,0,sizeof(yvel));
1637      memset(zvel,0,sizeof(zvel));
1638      memset(xsprite,0,kMaxXSprites*sizeof(XSPRITE));
1639      memset(sprite,0,kMaxSprites*sizeof(spritetype));
1640  
1641      memset(qsprite_filler,0,sizeof(qsprite_filler));
1642      memset(qsector_filler,0,sizeof(qsector_filler));
1643  
1644      #ifdef NOONE_EXTENSIONS
1645      gModernMap = false;
1646      #endif
1647  
1648  #ifdef USE_OPENGL
1649      Polymost_prepare_loadboard();
1650  #endif
1651  
1652      int const bakpathsearchmode = pathsearchmode;
1653      pathsearchmode = 1;
1654  
1655      Bstrncpy(name2, pPath, BMAX_PATH);
1656      Bstrupr(name2);
1657      DICTNODE* pNode = *gSysRes.Probe(name2, "MAP");
1658      if (pNode && pNode->flags & DICT_EXTERNAL)
1659      {
1660          gSysRes.RemoveNode(pNode);
1661      }
1662  
1663      pNode = gSysRes.Lookup(pPath, "MAP");
1664      if (!pNode)
1665      {
1666          char name2[BMAX_PATH];
1667          Bstrncpy(name2, pPath, BMAX_PATH);
1668          ChangeExtension(name2, "");
1669          pNode = gSysRes.Lookup(name2, "MAP");
1670      }
1671  
1672      if (!pNode)
1673      {
1674          LOG_F(ERROR, "Error opening map file %s", pPath);
1675          pathsearchmode = bakpathsearchmode;
1676          return -1;
1677      }
1678      char *pData = (char*)gSysRes.Lock(pNode);
1679      pathsearchmode = bakpathsearchmode;
1680      int nSize = pNode->size;
1681      MAPSIGNATURE header;
1682      IOBuffer IOBuffer1 = IOBuffer(nSize, pData);
1683      IOBuffer1.Read(&header, 6);
1684  #if B_BIG_ENDIAN == 1
1685      header.version = B_LITTLE16(header.version);
1686  #endif
1687      if (memcmp(header.signature, "BLM\x1a", 4))
1688      {
1689          LOG_F(ERROR, "Map file corrupted");
1690          gSysRes.Unlock(pNode);
1691          return -1;
1692      }
1693      byte_1A76C8 = 0;
1694      if ((header.version & 0xff00) == 0x700) {
1695          byte_1A76C8 = 1;
1696          
1697          #ifdef NOONE_EXTENSIONS
1698          // indicate if the map requires modern features to work properly
1699          // for maps wich created in PMAPEDIT BETA13 or higher versions. Since only minor version changed,
1700          // the map is still can be loaded with vanilla BLOOD / MAPEDIT and should work in other ports too.
1701          int tmp = (header.version & 0x00ff);
1702          
1703          // get the modern features revision
1704          switch (tmp) {
1705              case 0x001:
1706                  gModernMap = 1;
1707                  break;
1708              case 0x002:
1709                  gModernMap = 2;
1710                  break;
1711          }
1712          #endif
1713  
1714      } else {
1715          LOG_F(ERROR, "Map file is wrong version");
1716          gSysRes.Unlock(pNode);
1717          return -1;
1718      }
1719  
1720      MAPHEADER mapHeader;
1721      IOBuffer1.Read(&mapHeader,37/* sizeof(mapHeader)*/);
1722      if (mapHeader.at16 != 0 && mapHeader.at16 != kMapHeaderNew && mapHeader.at16 != kMapHeaderOld) {
1723          dbCrypt((char*)&mapHeader, sizeof(mapHeader), kMapHeaderNew);
1724          byte_1A76C7 = 1;
1725      }
1726  
1727  #if B_BIG_ENDIAN == 1
1728      mapHeader.at0 = B_LITTLE32(mapHeader.at0);
1729      mapHeader.at4 = B_LITTLE32(mapHeader.at4);
1730      mapHeader.at8 = B_LITTLE32(mapHeader.at8);
1731      mapHeader.atc = B_LITTLE16(mapHeader.atc);
1732      mapHeader.ate = B_LITTLE16(mapHeader.ate);
1733      mapHeader.at10 = B_LITTLE16(mapHeader.at10);
1734      mapHeader.at12 = B_LITTLE32(mapHeader.at12);
1735      mapHeader.at16 = B_LITTLE32(mapHeader.at16);
1736      mapHeader.at1b = B_LITTLE32(mapHeader.at1b);
1737      mapHeader.at1f = B_LITTLE16(mapHeader.at1f);
1738      mapHeader.at21 = B_LITTLE16(mapHeader.at21);
1739      mapHeader.at23 = B_LITTLE16(mapHeader.at23);
1740  #endif
1741  
1742      psky_t *pSky = tileSetupSky(0);
1743      pSky->horizfrac = 65536;
1744  
1745      *pX = mapHeader.at0;
1746      *pY = mapHeader.at4;
1747      *pZ = mapHeader.at8;
1748      *pAngle = mapHeader.atc;
1749      *pSector = mapHeader.ate;
1750      pSky->lognumtiles = mapHeader.at10;
1751      gVisibility = g_visibility = mapHeader.at12;
1752      gSongId = mapHeader.at16;
1753      if (byte_1A76C8)
1754      {
1755          if (mapHeader.at16 == kMapHeaderNew || mapHeader.at16 == kMapHeaderOld)
1756          {
1757              byte_1A76C6 = 1;
1758          }
1759          else if (!mapHeader.at16)
1760          {
1761              byte_1A76C6 = 0;
1762          }
1763          else
1764          {
1765              LOG_F(ERROR, "Corrupted Map file");
1766              gSysRes.Unlock(pNode);
1767              return -1;
1768          }
1769      }
1770      else if (mapHeader.at16)
1771      {
1772          LOG_F(ERROR, "Corrupted Map file");
1773          gSysRes.Unlock(pNode);
1774          return -1;
1775      }
1776      parallaxtype = mapHeader.at1a;
1777      gMapRev = mapHeader.at1b;
1778      numsectors = mapHeader.at1f;
1779      numwalls = mapHeader.at21;
1780      dbInit();
1781      if (gGameOptions.nRandomizerMode && !VanillaMode())
1782      {
1783          dbRandomizerModeInit(); // seed enemy/pickup randomizer
1784      }
1785      if (byte_1A76C8)
1786      {
1787          IOBuffer1.Read(&byte_19AE44, 128);
1788          dbCrypt((char*)&byte_19AE44, 128, numwalls);
1789  #if B_BIG_ENDIAN == 1
1790          byte_19AE44.at40 = B_LITTLE32(byte_19AE44.at40);
1791          byte_19AE44.at44 = B_LITTLE32(byte_19AE44.at44);
1792          byte_19AE44.at48 = B_LITTLE32(byte_19AE44.at48);
1793  #endif
1794      }
1795      else
1796      {
1797          memset(&byte_19AE44, 0, 128);
1798      }
1799      gSkyCount = 1<<pSky->lognumtiles;
1800      IOBuffer1.Read(tpskyoff, gSkyCount*sizeof(tpskyoff[0]));
1801      if (byte_1A76C8)
1802      {
1803          dbCrypt((char*)tpskyoff, gSkyCount*sizeof(tpskyoff[0]), gSkyCount*2);
1804      }
1805      for (int i = 0; i < ClipHigh(gSkyCount, MAXPSKYTILES); i++)
1806      {
1807          pSky->tileofs[i] = B_LITTLE16(tpskyoff[i]);
1808      }
1809      for (int i = 0; i < numsectors; i++)
1810      {
1811          sectortype *pSector = &sector[i];
1812          IOBuffer1.Read(pSector, sizeof(sectortype));
1813          if (byte_1A76C8)
1814          {
1815              dbCrypt((char*)pSector, sizeof(sectortype), gMapRev*sizeof(sectortype));
1816          }
1817  #if B_BIG_ENDIAN == 1
1818          pSector->wallptr = B_LITTLE16(pSector->wallptr);
1819          pSector->wallnum = B_LITTLE16(pSector->wallnum);
1820          pSector->ceilingz = B_LITTLE32(pSector->ceilingz);
1821          pSector->floorz = B_LITTLE32(pSector->floorz);
1822          pSector->ceilingstat = B_LITTLE16(pSector->ceilingstat);
1823          pSector->floorstat = B_LITTLE16(pSector->floorstat);
1824          pSector->ceilingpicnum = B_LITTLE16(pSector->ceilingpicnum);
1825          pSector->ceilingheinum = B_LITTLE16(pSector->ceilingheinum);
1826          pSector->floorpicnum = B_LITTLE16(pSector->floorpicnum);
1827          pSector->floorheinum = B_LITTLE16(pSector->floorheinum);
1828          pSector->type = B_LITTLE16(pSector->type);
1829          pSector->hitag = B_LITTLE16(pSector->hitag);
1830          pSector->extra = B_LITTLE16(pSector->extra);
1831  #endif
1832          qsector_filler[i] = pSector->fogpal;
1833          pSector->fogpal = 0;
1834          if (sector[i].extra > 0)
1835          {
1836              char pBuffer[nXSectorSize];
1837              int nXSector = dbInsertXSector(i);
1838              XSECTOR *pXSector = &xsector[nXSector];
1839              memset(pXSector, 0, sizeof(XSECTOR));
1840              int nCount;
1841              if (!byte_1A76C8)
1842              {
1843                  nCount = nXSectorSize;
1844              }
1845              else
1846              {
1847                  nCount = byte_19AE44.at48;
1848              }
1849              dassert(nCount <= nXSectorSize);
1850              IOBuffer1.Read(pBuffer, nCount);
1851              BitReader bitReader(pBuffer, nCount);
1852              pXSector->reference = bitReader.readSigned(14);
1853              pXSector->state = bitReader.readUnsigned(1);
1854              pXSector->busy = bitReader.readUnsigned(17);
1855              pXSector->data = bitReader.readUnsigned(16);
1856              pXSector->txID = bitReader.readUnsigned(10);
1857              pXSector->busyWaveA = bitReader.readUnsigned(3);
1858              pXSector->busyWaveB = bitReader.readUnsigned(3);
1859              pXSector->rxID = bitReader.readUnsigned(10);
1860              pXSector->command = bitReader.readUnsigned(8);
1861              pXSector->triggerOn = bitReader.readUnsigned(1);
1862              pXSector->triggerOff = bitReader.readUnsigned(1);
1863              pXSector->busyTimeA = bitReader.readUnsigned(12);
1864              pXSector->waitTimeA = bitReader.readUnsigned(12);
1865              pXSector->restState = bitReader.readUnsigned(1);
1866              pXSector->interruptable = bitReader.readUnsigned(1);
1867              pXSector->amplitude = bitReader.readSigned(8);
1868              pXSector->freq = bitReader.readUnsigned(8);
1869              pXSector->reTriggerA = bitReader.readUnsigned(1);
1870              pXSector->reTriggerB = bitReader.readUnsigned(1);
1871              pXSector->phase = bitReader.readUnsigned(8);
1872              pXSector->wave = bitReader.readUnsigned(4);
1873              pXSector->shadeAlways = bitReader.readUnsigned(1);
1874              pXSector->shadeFloor = bitReader.readUnsigned(1);
1875              pXSector->shadeCeiling = bitReader.readUnsigned(1);
1876              pXSector->shadeWalls = bitReader.readUnsigned(1);
1877              pXSector->shade = bitReader.readSigned(8);
1878              pXSector->panAlways = bitReader.readUnsigned(1);
1879              pXSector->panFloor = bitReader.readUnsigned(1);
1880              pXSector->panCeiling = bitReader.readUnsigned(1);
1881              pXSector->Drag = bitReader.readUnsigned(1);
1882              pXSector->Underwater = bitReader.readUnsigned(1);
1883              pXSector->Depth = bitReader.readUnsigned(3);
1884              pXSector->panVel = bitReader.readUnsigned(8);
1885              pXSector->panAngle = bitReader.readUnsigned(11);
1886              pXSector->unused1 = bitReader.readUnsigned(1);
1887              pXSector->decoupled = bitReader.readUnsigned(1);
1888              pXSector->triggerOnce = bitReader.readUnsigned(1);
1889              pXSector->isTriggered = bitReader.readUnsigned(1);
1890              pXSector->Key = bitReader.readUnsigned(3);
1891              pXSector->Push = bitReader.readUnsigned(1);
1892              pXSector->Vector = bitReader.readUnsigned(1);
1893              pXSector->Reserved = bitReader.readUnsigned(1);
1894              pXSector->Enter = bitReader.readUnsigned(1);
1895              pXSector->Exit = bitReader.readUnsigned(1);
1896              pXSector->Wallpush = bitReader.readUnsigned(1);
1897              pXSector->color = bitReader.readUnsigned(1);
1898              pXSector->unused2 = bitReader.readUnsigned(1);
1899              pXSector->busyTimeB = bitReader.readUnsigned(12);
1900              pXSector->waitTimeB = bitReader.readUnsigned(12);
1901              pXSector->stopOn = bitReader.readUnsigned(1);
1902              pXSector->stopOff = bitReader.readUnsigned(1);
1903              pXSector->ceilpal = bitReader.readUnsigned(4);
1904              pXSector->offCeilZ = bitReader.readSigned(32);
1905              pXSector->onCeilZ = bitReader.readSigned(32);
1906              pXSector->offFloorZ = bitReader.readSigned(32);
1907              pXSector->onFloorZ = bitReader.readSigned(32);
1908              pXSector->marker0 = bitReader.readUnsigned(16);
1909              pXSector->marker1 = bitReader.readUnsigned(16);
1910              pXSector->Crush = bitReader.readUnsigned(1);
1911              pXSector->ceilXPanFrac = bitReader.readUnsigned(8);
1912              pXSector->ceilYPanFrac = bitReader.readUnsigned(8);
1913              pXSector->floorXPanFrac = bitReader.readUnsigned(8);
1914              pXSector->damageType = bitReader.readUnsigned(3);
1915              pXSector->floorpal = bitReader.readUnsigned(4);
1916              pXSector->floorYPanFrac = bitReader.readUnsigned(8);
1917              pXSector->locked = bitReader.readUnsigned(1);
1918              pXSector->windVel = bitReader.readUnsigned(10);
1919              pXSector->windAng = bitReader.readUnsigned(11);
1920              pXSector->windAlways = bitReader.readUnsigned(1);
1921              pXSector->dudeLockout = bitReader.readUnsigned(1);
1922              pXSector->bobTheta = bitReader.readUnsigned(11);
1923              pXSector->bobZRange = bitReader.readUnsigned(5);
1924              pXSector->bobSpeed = bitReader.readSigned(12);
1925              pXSector->bobAlways = bitReader.readUnsigned(1);
1926              pXSector->bobFloor = bitReader.readUnsigned(1);
1927              pXSector->bobCeiling = bitReader.readUnsigned(1);
1928              pXSector->bobRotate = bitReader.readUnsigned(1);
1929              xsector[sector[i].extra].reference = i;
1930              xsector[sector[i].extra].busy = xsector[sector[i].extra].state<<16;
1931  
1932          }
1933      }
1934      for (int i = 0; i < numwalls; i++)
1935      {
1936          walltype *pWall = &wall[i];
1937          IOBuffer1.Read(pWall, sizeof(walltype));
1938          if (byte_1A76C8)
1939          {
1940              dbCrypt((char*)pWall, sizeof(walltype), (gMapRev*sizeof(sectortype)) | kMapHeaderNew);
1941          }
1942  #if B_BIG_ENDIAN == 1
1943          pWall->x = B_LITTLE32(pWall->x);
1944          pWall->y = B_LITTLE32(pWall->y);
1945          pWall->point2 = B_LITTLE16(pWall->point2);
1946          pWall->nextwall = B_LITTLE16(pWall->nextwall);
1947          pWall->nextsector = B_LITTLE16(pWall->nextsector);
1948          pWall->cstat = B_LITTLE16(pWall->cstat);
1949          pWall->picnum = B_LITTLE16(pWall->picnum);
1950          pWall->overpicnum = B_LITTLE16(pWall->overpicnum);
1951          pWall->type = B_LITTLE16(pWall->type);
1952          pWall->hitag = B_LITTLE16(pWall->hitag);
1953          pWall->extra = B_LITTLE16(pWall->extra);
1954  #endif
1955          if (wall[i].extra > 0)
1956          {
1957              char pBuffer[nXWallSize];
1958              int nXWall = dbInsertXWall(i);
1959              XWALL *pXWall = &xwall[nXWall];
1960              memset(pXWall, 0, sizeof(XWALL));
1961              int nCount;
1962              if (!byte_1A76C8)
1963              {
1964                  nCount = nXWallSize;
1965              }
1966              else
1967              {
1968                  nCount = byte_19AE44.at44;
1969              }
1970              dassert(nCount <= nXWallSize);
1971              IOBuffer1.Read(pBuffer, nCount);
1972              BitReader bitReader(pBuffer, nCount);
1973              pXWall->reference = bitReader.readSigned(14);
1974              pXWall->state = bitReader.readUnsigned(1);
1975              pXWall->busy = bitReader.readUnsigned(17);
1976              pXWall->data = bitReader.readSigned(16);
1977              pXWall->txID = bitReader.readUnsigned(10);
1978              pXWall->unused1 = bitReader.readUnsigned(6);
1979              pXWall->rxID = bitReader.readUnsigned(10);
1980              pXWall->command = bitReader.readUnsigned(8);
1981              pXWall->triggerOn = bitReader.readUnsigned(1);
1982              pXWall->triggerOff = bitReader.readUnsigned(1);
1983              pXWall->busyTime = bitReader.readUnsigned(12);
1984              pXWall->waitTime = bitReader.readUnsigned(12);
1985              pXWall->restState = bitReader.readUnsigned(1);
1986              pXWall->interruptable = bitReader.readUnsigned(1);
1987              pXWall->panAlways = bitReader.readUnsigned(1);
1988              pXWall->panXVel = bitReader.readSigned(8);
1989              pXWall->panYVel = bitReader.readSigned(8);
1990              pXWall->decoupled = bitReader.readUnsigned(1);
1991              pXWall->triggerOnce = bitReader.readUnsigned(1);
1992              pXWall->isTriggered = bitReader.readUnsigned(1);
1993              pXWall->key = bitReader.readUnsigned(3);
1994              pXWall->triggerPush = bitReader.readUnsigned(1);
1995              pXWall->triggerVector = bitReader.readUnsigned(1);
1996              pXWall->triggerTouch = bitReader.readUnsigned(1);
1997              pXWall->unused2 = bitReader.readUnsigned(2);
1998              pXWall->xpanFrac = bitReader.readUnsigned(8);
1999              pXWall->ypanFrac = bitReader.readUnsigned(8);
2000              pXWall->locked = bitReader.readUnsigned(1);
2001              pXWall->dudeLockout = bitReader.readUnsigned(1);
2002              pXWall->unused3 = bitReader.readUnsigned(4);
2003              pXWall->unused4 = bitReader.readUnsigned(32);
2004              xwall[wall[i].extra].reference = i;
2005              xwall[wall[i].extra].busy = xwall[wall[i].extra].state << 16;
2006  
2007          }
2008      }
2009      initspritelists();
2010      for (int i = 0; i < mapHeader.at23; i++)
2011      {
2012          RemoveSpriteStat(i);
2013          spritetype *pSprite = &sprite[i];
2014          IOBuffer1.Read(pSprite, sizeof(spritetype));
2015          if (byte_1A76C8)
2016          {
2017              dbCrypt((char*)pSprite, sizeof(spritetype), (gMapRev*sizeof(spritetype)) | kMapHeaderNew);
2018          }
2019  #if B_BIG_ENDIAN == 1
2020          pSprite->x = B_LITTLE32(pSprite->x);
2021          pSprite->y = B_LITTLE32(pSprite->y);
2022          pSprite->z = B_LITTLE32(pSprite->z);
2023          pSprite->cstat = B_LITTLE16(pSprite->cstat);
2024          pSprite->picnum = B_LITTLE16(pSprite->picnum);
2025          pSprite->sectnum = B_LITTLE16(pSprite->sectnum);
2026          pSprite->statnum = B_LITTLE16(pSprite->statnum);
2027          pSprite->ang = B_LITTLE16(pSprite->ang);
2028          pSprite->owner = B_LITTLE16(pSprite->owner);
2029          pSprite->index = B_LITTLE16(pSprite->index);
2030          pSprite->yvel = B_LITTLE16(pSprite->yvel);
2031          pSprite->inittype = B_LITTLE16(pSprite->inittype);
2032          pSprite->type = B_LITTLE16(pSprite->type);
2033          pSprite->flags = B_LITTLE16(pSprite->hitag);
2034          pSprite->extra = B_LITTLE16(pSprite->extra);
2035  #endif
2036          InsertSpriteSect(i, sprite[i].sectnum);
2037          InsertSpriteStat(i, sprite[i].statnum);
2038          Numsprites++;
2039          sprite[i].index = i;
2040          qsprite_filler[i] = pSprite->blend;
2041          pSprite->blend = 0;
2042          if (sprite[i].extra > 0)
2043          {
2044              char pBuffer[nXSpriteSize];
2045              int nXSprite = dbInsertXSprite(i);
2046              XSPRITE *pXSprite = &xsprite[nXSprite];
2047              memset(pXSprite, 0, sizeof(XSPRITE));
2048              int nCount;
2049              if (!byte_1A76C8)
2050              {
2051                  nCount = nXSpriteSize;
2052              }
2053              else
2054              {
2055                  nCount = byte_19AE44.at40;
2056              }
2057              dassert(nCount <= nXSpriteSize);
2058              IOBuffer1.Read(pBuffer, nCount);
2059              BitReader bitReader(pBuffer, nCount);
2060              pXSprite->reference = bitReader.readSigned(14);
2061              pXSprite->state = bitReader.readUnsigned(1);
2062              pXSprite->busy = bitReader.readUnsigned(17);
2063              pXSprite->txID = bitReader.readUnsigned(10);
2064              pXSprite->rxID = bitReader.readUnsigned(10);
2065              pXSprite->command = bitReader.readUnsigned(8);
2066              pXSprite->triggerOn = bitReader.readUnsigned(1);
2067              pXSprite->triggerOff = bitReader.readUnsigned(1);
2068              pXSprite->wave = bitReader.readUnsigned(2);
2069              pXSprite->busyTime = bitReader.readUnsigned(12);
2070              pXSprite->waitTime = bitReader.readUnsigned(12);
2071              pXSprite->restState = bitReader.readUnsigned(1);
2072              pXSprite->Interrutable = bitReader.readUnsigned(1);
2073              pXSprite->unused1 = bitReader.readUnsigned(2);
2074              pXSprite->respawnPending = bitReader.readUnsigned(2);
2075              pXSprite->unused2 = bitReader.readUnsigned(1);
2076              pXSprite->lT = bitReader.readUnsigned(1);
2077              pXSprite->dropMsg = bitReader.readUnsigned(8);
2078              pXSprite->Decoupled = bitReader.readUnsigned(1);
2079              pXSprite->triggerOnce = bitReader.readUnsigned(1);
2080              pXSprite->isTriggered = bitReader.readUnsigned(1);
2081              pXSprite->key = bitReader.readUnsigned(3);
2082              pXSprite->Push = bitReader.readUnsigned(1);
2083              pXSprite->Vector = bitReader.readUnsigned(1);
2084              pXSprite->Impact = bitReader.readUnsigned(1);
2085              pXSprite->Pickup = bitReader.readUnsigned(1);
2086              pXSprite->Touch = bitReader.readUnsigned(1);
2087              pXSprite->Sight = bitReader.readUnsigned(1);
2088              pXSprite->Proximity = bitReader.readUnsigned(1);
2089              pXSprite->unused3 = bitReader.readUnsigned(2);
2090              pXSprite->lSkill = bitReader.readUnsigned(5);
2091              pXSprite->lS = bitReader.readUnsigned(1);
2092              pXSprite->lB = bitReader.readUnsigned(1);
2093              pXSprite->lC = bitReader.readUnsigned(1);
2094              pXSprite->DudeLockout = bitReader.readUnsigned(1);
2095              pXSprite->data1 = bitReader.readSigned(16);
2096              pXSprite->data2 = bitReader.readSigned(16);
2097              pXSprite->data3 = bitReader.readSigned(16);
2098              pXSprite->goalAng = bitReader.readUnsigned(11);
2099              pXSprite->dodgeDir = bitReader.readSigned(2);
2100              pXSprite->locked = bitReader.readUnsigned(1);
2101              pXSprite->medium = bitReader.readUnsigned(2);
2102              pXSprite->respawn = bitReader.readUnsigned(2);
2103              pXSprite->data4 = bitReader.readUnsigned(16);
2104              pXSprite->unused4 = bitReader.readUnsigned(6);
2105              pXSprite->lockMsg = bitReader.readUnsigned(8);
2106              pXSprite->health = bitReader.readUnsigned(12);
2107              pXSprite->dudeDeaf = bitReader.readUnsigned(1);
2108              pXSprite->dudeAmbush = bitReader.readUnsigned(1);
2109              pXSprite->dudeGuard = bitReader.readUnsigned(1);
2110              pXSprite->dudeFlag4 = bitReader.readUnsigned(1);
2111              pXSprite->target = bitReader.readSigned(16);
2112              pXSprite->targetX = bitReader.readSigned(32);
2113              pXSprite->targetY = bitReader.readSigned(32);
2114              pXSprite->targetZ = bitReader.readSigned(32);
2115              pXSprite->burnTime = bitReader.readUnsigned(16);
2116              pXSprite->burnSource = bitReader.readSigned(16);
2117              pXSprite->height = bitReader.readUnsigned(16);
2118              pXSprite->stateTimer = bitReader.readUnsigned(16);
2119              pXSprite->aiState = NULL;
2120              bitReader.skipBits(32);
2121              xsprite[sprite[i].extra].reference = i;
2122              xsprite[sprite[i].extra].busy = xsprite[sprite[i].extra].state << 16;
2123              if (!byte_1A76C8) {
2124                  xsprite[sprite[i].extra].lT = xsprite[sprite[i].extra].lB;
2125              }
2126  
2127              #ifdef NOONE_EXTENSIONS
2128              // indicate if the map requires modern features to work properly
2129              // for maps wich created in different editors (include vanilla MAPEDIT) or in PMAPEDIT version below than BETA13
2130              if (!gModernMap && pXSprite->rxID == pXSprite->txID && pXSprite->command == kCmdModernFeaturesEnable)
2131              {
2132                  // get the modern features revision
2133                  switch (pXSprite->txID) {
2134                  case kChannelMapModernRev1:
2135                      gModernMap = 1;
2136                      break;
2137                  case kChannelMapModernRev2:
2138                      gModernMap = 2;
2139                      break;
2140                  }
2141              }
2142              #endif
2143          }
2144  #if 0
2145          if ((sprite[i].cstat & 0x30) == 0x30)
2146          {
2147              sprite[i].cstat &= ~0x30;
2148          }
2149  #endif
2150      }
2151      unsigned int nCRC;
2152      IOBuffer1.Read(&nCRC, 4);
2153  #if B_BIG_ENDIAN == 1
2154      nCRC = B_LITTLE32(nCRC);
2155  #endif
2156      md4once((unsigned char*)pData, nSize, g_loadedMapHack.md4);
2157      if (Bcrc32(pData, nSize-4, 0) != nCRC)
2158      {
2159          LOG_F(ERROR, "Map File does not match CRC");
2160          gSysRes.Unlock(pNode);
2161          return -1;
2162      }
2163      if (pCRC)
2164          *pCRC = nCRC;
2165      gSysRes.Unlock(pNode);
2166      PropagateMarkerReferences();
2167      if (byte_1A76C8)
2168      {
2169          if (gSongId == kMapHeaderNew || gSongId == kMapHeaderOld)
2170          {
2171              byte_1A76C6 = 1;
2172          }
2173          else if (!gSongId)
2174          {
2175              byte_1A76C6 = 0;
2176          }
2177          else
2178          {
2179              LOG_F(ERROR, "Corrupted Map file");
2180              return -1;
2181          }
2182      }
2183      else if (gSongId != 0)
2184      {
2185          LOG_F(ERROR, "Corrupted Map file");
2186          return -1;
2187      }
2188  
2189  #ifdef NOONE_EXTENSIONS
2190      if (VanillaMode() && gModernMap)
2191      {
2192          viewSetMessage("Warning: Modern levels are not compatible with vanilla mode");
2193          viewSetMessage("Please disable vanilla mode and restart level");
2194      }
2195  #endif
2196  
2197  #ifdef POLYMER
2198      if (videoGetRenderMode() == REND_POLYMER)
2199          polymer_loadboard();
2200  #endif
2201  
2202      if ((header.version & 0xff00) == 0x600)
2203      {
2204          switch (header.version&0xff)
2205          {
2206          case 0:
2207              for (int i = 0; i < numsectors; i++)
2208              {
2209                  sectortype *pSector = &sector[i];
2210                  if (pSector->extra > 0)
2211                  {
2212                      XSECTOR *pXSector = &xsector[pSector->extra];
2213                      pXSector->busyTimeB = pXSector->busyTimeA;
2214                      if (pXSector->busyTimeA > 0)
2215                      {
2216                          if (!pXSector->restState)
2217                          {
2218                              pXSector->reTriggerA = 1;
2219                          }
2220                          else
2221                          {
2222                              pXSector->waitTimeB = pXSector->busyTimeA;
2223                              pXSector->waitTimeA = 0;
2224                              pXSector->reTriggerB = 1;
2225                          }
2226                      }
2227                  }
2228              }
2229              fallthrough__;
2230          case 1:
2231              for (int i = 0; i < numsectors; i++)
2232              {
2233                  sectortype *pSector = &sector[i];
2234                  if (pSector->extra > 0)
2235                  {
2236                      XSECTOR *pXSector = &xsector[pSector->extra];
2237                      pXSector->freq >>= 1;
2238                  }
2239              }
2240              fallthrough__;
2241          case 2:
2242              for (int i = 0; i < kMaxSprites; i++)
2243              {
2244              }
2245              break;
2246              
2247          }
2248      }
2249  
2250  #ifdef YAX_ENABLE
2251      yax_update((header.version & 0xff00) > 0x700 ? 0 : 1);
2252      if (editstatus)
2253          yax_updategrays(*pZ);
2254  #endif
2255  
2256      calc_sector_reachability();
2257  
2258      g_loadedMapVersion = 7;
2259  
2260      return 0;
2261  }
2262  
2263  int dbSaveMap(const char *pPath, int nX, int nY, int nZ, short nAngle, short nSector)
2264  {
2265      char sMapExt[BMAX_PATH];
2266      //char sBakExt[BMAX_PATH];
2267      int16_t tpskyoff[256];
2268      int nSpriteNum;
2269      psky_t *pSky = tileSetupSky(0);
2270      gSkyCount = 1<<pSky->lognumtiles;
2271      gMapRev++;
2272      nSpriteNum = 0;
2273      strcpy(sMapExt, pPath);
2274      ChangeExtension(sMapExt, ".MAP");
2275      int nSize = sizeof(MAPSIGNATURE)+sizeof(MAPHEADER);
2276      if (byte_1A76C8)
2277      {
2278          nSize += sizeof(MAPHEADER2);
2279      }
2280      for (int i = 0; i < gSkyCount; i++)
2281          tpskyoff[i] = pSky->tileofs[i];
2282      nSize += gSkyCount*sizeof(tpskyoff[0]);
2283      nSize += sizeof(sectortype)*numsectors;
2284      for (int i = 0; i < numsectors; i++)
2285      {
2286          if (sector[i].extra > 0)
2287          {
2288              nSize += nXSectorSize;
2289          }
2290      }
2291      nSize += sizeof(walltype)*numwalls;
2292      for (int i = 0; i < numwalls; i++)
2293      {
2294          if (wall[i].extra > 0)
2295          {
2296              nSize += nXWallSize;
2297          }
2298      }
2299      for (int i = 0; i < kMaxSprites; i++)
2300      {
2301          if (sprite[i].statnum < kMaxStatus)
2302          {
2303              nSpriteNum++;
2304              if (sprite[i].extra > 0)
2305              {
2306                  nSize += nXSpriteSize;
2307              }
2308          }
2309      }
2310      nSize += sizeof(spritetype)*nSpriteNum;
2311      nSize += 4;
2312      char *pData = (char*)Xmalloc(nSize);
2313      IOBuffer IOBuffer1 = IOBuffer(nSize, pData);
2314      MAPSIGNATURE header;
2315      memcpy(&header, "BLM\x1a", 4);
2316      if (byte_1A76C8)
2317      {
2318          header.version = 0x700;
2319          byte_1A76C7 = 1;
2320      }
2321      else
2322      {
2323          header.version = 0x603;
2324          byte_1A76C7 = 0;
2325      }
2326      IOBuffer1.Write(&header, sizeof(header));
2327      MAPHEADER mapheader;
2328      mapheader.at0 = B_LITTLE32(nX);
2329      mapheader.at4 = B_LITTLE32(nY);
2330      mapheader.at8 = B_LITTLE32(nZ);
2331      mapheader.atc = B_LITTLE16(nAngle);
2332      mapheader.ate = B_LITTLE16(nSector);
2333      mapheader.at10 = B_LITTLE16(pSky->lognumtiles);
2334      mapheader.at12 = B_LITTLE32(gVisibility);
2335      if (byte_1A76C6)
2336      {
2337          gSongId = kMapHeaderNew;
2338      }
2339      else
2340      {
2341          gSongId = 0;
2342      }
2343      mapheader.at16 = B_LITTLE32(gSongId);
2344      mapheader.at1a = parallaxtype;
2345      mapheader.at1b = gMapRev;
2346      mapheader.at1f = B_LITTLE16(numsectors);
2347      mapheader.at21 = B_LITTLE16(numwalls);
2348      mapheader.at23 = B_LITTLE16(nSpriteNum);
2349      if (byte_1A76C7)
2350      {
2351          dbCrypt((char*)&mapheader, sizeof(MAPHEADER), kMapHeaderNew);
2352      }
2353      IOBuffer1.Write(&mapheader, sizeof(MAPHEADER));
2354      if (byte_1A76C8)
2355      {
2356          Bstrcpy(byte_19AE44.at0, AppProperName);
2357          byte_19AE44.at48 = nXSectorSize;
2358          byte_19AE44.at44 = nXWallSize;
2359          byte_19AE44.at40 = nXSpriteSize;
2360          dbCrypt((char*)&byte_19AE44, sizeof(MAPHEADER2), numwalls);
2361          IOBuffer1.Write(&byte_19AE44, sizeof(MAPHEADER2));
2362          dbCrypt((char*)&byte_19AE44, sizeof(MAPHEADER2), numwalls);
2363      }
2364      if (byte_1A76C8)
2365      {
2366          dbCrypt((char*)tpskyoff, gSkyCount*sizeof(tpskyoff[0]), gSkyCount*sizeof(tpskyoff[0]));
2367      }
2368      IOBuffer1.Write(tpskyoff, gSkyCount*sizeof(tpskyoff[0]));
2369      if (byte_1A76C8)
2370      {
2371          dbCrypt((char*)tpskyoff, gSkyCount*sizeof(tpskyoff[0]), gSkyCount*sizeof(tpskyoff[0]));
2372      }
2373      for (int i = 0; i < numsectors; i++)
2374      {
2375          if (byte_1A76C8)
2376          {
2377              dbCrypt((char*)&sector[i], sizeof(sectortype), gMapRev*sizeof(sectortype));
2378          }
2379          IOBuffer1.Write(&sector[i], sizeof(sectortype));
2380          if (byte_1A76C8)
2381          {
2382              dbCrypt((char*)&sector[i], sizeof(sectortype), gMapRev*sizeof(sectortype));
2383          }
2384          if (sector[i].extra > 0)
2385          {
2386              char pBuffer[nXSectorSize];
2387              BitWriter bitWriter(pBuffer, nXSectorSize);
2388              XSECTOR* pXSector = &xsector[sector[i].extra];
2389              bitWriter.write(pXSector->reference, 14);
2390              bitWriter.write(pXSector->state, 1);
2391              bitWriter.write(pXSector->busy, 17);
2392              bitWriter.write(pXSector->data, 16);
2393              bitWriter.write(pXSector->txID, 10);
2394              bitWriter.write(pXSector->busyWaveA, 3);
2395              bitWriter.write(pXSector->busyWaveB, 3);
2396              bitWriter.write(pXSector->rxID, 10);
2397              bitWriter.write(pXSector->command, 8);
2398              bitWriter.write(pXSector->triggerOn, 1);
2399              bitWriter.write(pXSector->triggerOff, 1);
2400              bitWriter.write(pXSector->busyTimeA, 12);
2401              bitWriter.write(pXSector->waitTimeA, 12);
2402              bitWriter.write(pXSector->restState, 1);
2403              bitWriter.write(pXSector->interruptable, 1);
2404              bitWriter.write(pXSector->amplitude, 8);
2405              bitWriter.write(pXSector->freq, 8);
2406              bitWriter.write(pXSector->reTriggerA, 1);
2407              bitWriter.write(pXSector->reTriggerB, 1);
2408              bitWriter.write(pXSector->phase, 8);
2409              bitWriter.write(pXSector->wave, 4);
2410              bitWriter.write(pXSector->shadeAlways, 1);
2411              bitWriter.write(pXSector->shadeFloor, 1);
2412              bitWriter.write(pXSector->shadeCeiling, 1);
2413              bitWriter.write(pXSector->shadeWalls, 1);
2414              bitWriter.write(pXSector->shade, 8);
2415              bitWriter.write(pXSector->panAlways, 1);
2416              bitWriter.write(pXSector->panFloor, 1);
2417              bitWriter.write(pXSector->panCeiling, 1);
2418              bitWriter.write(pXSector->Drag, 1);
2419              bitWriter.write(pXSector->Underwater, 1);
2420              bitWriter.write(pXSector->Depth, 3);
2421              bitWriter.write(pXSector->panVel, 8);
2422              bitWriter.write(pXSector->panAngle, 11);
2423              bitWriter.write(pXSector->unused1, 1);
2424              bitWriter.write(pXSector->decoupled, 1);
2425              bitWriter.write(pXSector->triggerOnce, 1);
2426              bitWriter.write(pXSector->isTriggered, 1);
2427              bitWriter.write(pXSector->Key, 3);
2428              bitWriter.write(pXSector->Push, 1);
2429              bitWriter.write(pXSector->Vector, 1);
2430              bitWriter.write(pXSector->Reserved, 1);
2431              bitWriter.write(pXSector->Enter, 1);
2432              bitWriter.write(pXSector->Exit, 1);
2433              bitWriter.write(pXSector->Wallpush, 1);
2434              bitWriter.write(pXSector->color, 1);
2435              bitWriter.write(pXSector->unused2, 1);
2436              bitWriter.write(pXSector->busyTimeB, 12);
2437              bitWriter.write(pXSector->waitTimeB, 12);
2438              bitWriter.write(pXSector->stopOn, 1);
2439              bitWriter.write(pXSector->stopOff, 1);
2440              bitWriter.write(pXSector->ceilpal, 4);
2441              bitWriter.write(pXSector->offCeilZ, 32);
2442              bitWriter.write(pXSector->onCeilZ, 32);
2443              bitWriter.write(pXSector->offFloorZ, 32);
2444              bitWriter.write(pXSector->onFloorZ, 32);
2445              bitWriter.write(pXSector->marker0, 16);
2446              bitWriter.write(pXSector->marker1, 16);
2447              bitWriter.write(pXSector->Crush, 1);
2448              bitWriter.write(pXSector->ceilXPanFrac, 8);
2449              bitWriter.write(pXSector->ceilYPanFrac, 8);
2450              bitWriter.write(pXSector->floorXPanFrac, 8);
2451              bitWriter.write(pXSector->damageType, 3);
2452              bitWriter.write(pXSector->floorpal, 4);
2453              bitWriter.write(pXSector->floorYPanFrac, 8);
2454              bitWriter.write(pXSector->locked, 1);
2455              bitWriter.write(pXSector->windVel, 10);
2456              bitWriter.write(pXSector->windAng, 11);
2457              bitWriter.write(pXSector->windAlways, 1);
2458              bitWriter.write(pXSector->dudeLockout, 1);
2459              bitWriter.write(pXSector->bobTheta, 11);
2460              bitWriter.write(pXSector->bobZRange, 5);
2461              bitWriter.write(pXSector->bobSpeed, 12);
2462              bitWriter.write(pXSector->bobAlways, 1);
2463              bitWriter.write(pXSector->bobFloor, 1);
2464              bitWriter.write(pXSector->bobCeiling, 1);
2465              bitWriter.write(pXSector->bobRotate, 1);
2466              IOBuffer1.Write(pBuffer, nXSectorSize);
2467          }
2468      }
2469      for (int i = 0; i < numwalls; i++)
2470      {
2471          if (byte_1A76C8)
2472          {
2473              dbCrypt((char*)&wall[i], sizeof(walltype), gMapRev*sizeof(sectortype) | kMapHeaderNew);
2474          }
2475          IOBuffer1.Write(&wall[i], sizeof(walltype));
2476          if (byte_1A76C8)
2477          {
2478              dbCrypt((char*)&wall[i], sizeof(walltype), gMapRev*sizeof(sectortype) | kMapHeaderNew);
2479          }
2480          if (wall[i].extra > 0)
2481          {
2482              char pBuffer[nXWallSize];
2483              BitWriter bitWriter(pBuffer, nXWallSize);
2484              XWALL* pXWall = &xwall[wall[i].extra];
2485              bitWriter.write(pXWall->reference, 14);
2486              bitWriter.write(pXWall->state, 1);
2487              bitWriter.write(pXWall->busy, 17);
2488              bitWriter.write(pXWall->data, 16);
2489              bitWriter.write(pXWall->txID, 10);
2490              bitWriter.write(pXWall->unused1, 6);
2491              bitWriter.write(pXWall->rxID, 10);
2492              bitWriter.write(pXWall->command, 8);
2493              bitWriter.write(pXWall->triggerOn, 1);
2494              bitWriter.write(pXWall->triggerOff, 1);
2495              bitWriter.write(pXWall->busyTime, 12);
2496              bitWriter.write(pXWall->waitTime, 12);
2497              bitWriter.write(pXWall->restState, 1);
2498              bitWriter.write(pXWall->interruptable, 1);
2499              bitWriter.write(pXWall->panAlways, 1);
2500              bitWriter.write(pXWall->panXVel, 8);
2501              bitWriter.write(pXWall->panYVel, 8);
2502              bitWriter.write(pXWall->decoupled, 1);
2503              bitWriter.write(pXWall->triggerOnce, 1);
2504              bitWriter.write(pXWall->isTriggered, 1);
2505              bitWriter.write(pXWall->key, 3);
2506              bitWriter.write(pXWall->triggerPush, 1);
2507              bitWriter.write(pXWall->triggerVector, 1);
2508              bitWriter.write(pXWall->triggerTouch, 1);
2509              bitWriter.write(pXWall->unused2, 2);
2510              bitWriter.write(pXWall->xpanFrac, 8);
2511              bitWriter.write(pXWall->ypanFrac, 8);
2512              bitWriter.write(pXWall->locked, 1);
2513              bitWriter.write(pXWall->dudeLockout, 1);
2514              bitWriter.write(pXWall->unused3, 4);
2515              bitWriter.write(pXWall->unused4, 32);
2516              IOBuffer1.Write(pBuffer, nXWallSize);
2517          }
2518      }
2519      for (int i = 0; i < kMaxSprites; i++)
2520      {
2521          if (sprite[i].statnum < kMaxStatus)
2522          {
2523              if (byte_1A76C8)
2524              {
2525                  dbCrypt((char*)&sprite[i], sizeof(spritetype), gMapRev*sizeof(spritetype) | kMapHeaderNew);
2526              }
2527              IOBuffer1.Write(&sprite[i], sizeof(spritetype));
2528              if (byte_1A76C8)
2529              {
2530                  dbCrypt((char*)&sprite[i], sizeof(spritetype), gMapRev*sizeof(spritetype) | kMapHeaderNew);
2531              }
2532              if (sprite[i].extra > 0)
2533              {
2534                  char pBuffer[nXSpriteSize];
2535                  BitWriter bitWriter(pBuffer, nXSpriteSize);
2536                  XSPRITE* pXSprite = &xsprite[sprite[i].extra];
2537                  bitWriter.write(pXSprite->reference, 14);
2538                  bitWriter.write(pXSprite->state, 1);
2539                  bitWriter.write(pXSprite->busy, 17);
2540                  bitWriter.write(pXSprite->txID, 10);
2541                  bitWriter.write(pXSprite->rxID, 10);
2542                  bitWriter.write(pXSprite->command, 8);
2543                  bitWriter.write(pXSprite->triggerOn, 1);
2544                  bitWriter.write(pXSprite->triggerOff, 1);
2545                  bitWriter.write(pXSprite->wave, 2);
2546                  bitWriter.write(pXSprite->busyTime, 12);
2547                  bitWriter.write(pXSprite->waitTime, 12);
2548                  bitWriter.write(pXSprite->restState, 1);
2549                  bitWriter.write(pXSprite->Interrutable, 1);
2550                  bitWriter.write(pXSprite->unused1, 2);
2551                  bitWriter.write(pXSprite->respawnPending, 2);
2552                  bitWriter.write(pXSprite->unused2, 1);
2553                  bitWriter.write(pXSprite->lT, 1);
2554                  bitWriter.write(pXSprite->dropMsg, 8);
2555                  bitWriter.write(pXSprite->Decoupled, 1);
2556                  bitWriter.write(pXSprite->triggerOnce, 1);
2557                  bitWriter.write(pXSprite->isTriggered, 1);
2558                  bitWriter.write(pXSprite->key, 3);
2559                  bitWriter.write(pXSprite->Push, 1);
2560                  bitWriter.write(pXSprite->Vector, 1);
2561                  bitWriter.write(pXSprite->Impact, 1);
2562                  bitWriter.write(pXSprite->Pickup, 1);
2563                  bitWriter.write(pXSprite->Touch, 1);
2564                  bitWriter.write(pXSprite->Sight, 1);
2565                  bitWriter.write(pXSprite->Proximity, 1);
2566                  bitWriter.write(pXSprite->unused3, 2);
2567                  bitWriter.write(pXSprite->lSkill, 5);
2568                  bitWriter.write(pXSprite->lS, 1);
2569                  bitWriter.write(pXSprite->lB, 1);
2570                  bitWriter.write(pXSprite->lC, 1);
2571                  bitWriter.write(pXSprite->DudeLockout, 1);
2572                  bitWriter.write(pXSprite->data1, 16);
2573                  bitWriter.write(pXSprite->data2, 16);
2574                  bitWriter.write(pXSprite->data3, 16);
2575                  bitWriter.write(pXSprite->goalAng, 11);
2576                  bitWriter.write(pXSprite->dodgeDir, 2);
2577                  bitWriter.write(pXSprite->locked, 1);
2578                  bitWriter.write(pXSprite->medium, 2);
2579                  bitWriter.write(pXSprite->respawn, 2);
2580                  bitWriter.write(pXSprite->data4, 16);
2581                  bitWriter.write(pXSprite->unused4, 6);
2582                  bitWriter.write(pXSprite->lockMsg, 8);
2583                  bitWriter.write(pXSprite->health, 12);
2584                  bitWriter.write(pXSprite->dudeDeaf, 1);
2585                  bitWriter.write(pXSprite->dudeAmbush, 1);
2586                  bitWriter.write(pXSprite->dudeGuard, 1);
2587                  bitWriter.write(pXSprite->dudeFlag4, 1);
2588                  bitWriter.write(pXSprite->target, 16);
2589                  bitWriter.write(pXSprite->targetX, 32);
2590                  bitWriter.write(pXSprite->targetY, 32);
2591                  bitWriter.write(pXSprite->targetZ, 32);
2592                  bitWriter.write(pXSprite->burnTime, 16);
2593                  bitWriter.write(pXSprite->burnSource, 16);
2594                  bitWriter.write(pXSprite->height, 16);
2595                  bitWriter.write(pXSprite->stateTimer, 16);
2596                  IOBuffer1.Write(pBuffer, nXSpriteSize);
2597              }
2598          }
2599      }
2600      unsigned int nCRC = Bcrc32(pData, nSize-4, 0);
2601      IOBuffer1.Write(&nCRC, 4);
2602      int nHandle = Bopen(sMapExt, BO_BINARY|BO_TRUNC|BO_CREAT|BO_WRONLY, BS_IREAD|BS_IWRITE);
2603      if (nHandle == -1)
2604      {
2605          LOG_F(ERROR, "Couldn't open \"%s\" for writing: %s", sMapExt, strerror(errno));
2606          Xfree(pData);
2607          return -1;
2608      }
2609      if (Bwrite(nHandle, pData, nSize) != nSize)
2610      {
2611          LOG_F(ERROR, "Couldn't write to \"%s\": %s", sMapExt, strerror(errno));
2612          Bclose(nHandle);
2613          Xfree(pData);
2614          return -1;
2615      }
2616      Bclose(nHandle);
2617      Xfree(pData);
2618      return 0;
2619  #if 0
2620      char *pExt = strchr(sMapExt, '.');
2621      if (pExt)
2622      {
2623          *pExt = 0;
2624      }
2625      gSysRes.AddExternalResource(sMapExt, "MAP", nSize);
2626      DICTNODE *hMap = gSysRes.Lookup(sMapExt, "MAP");
2627      dassert(hMap != NULL);
2628  #endif
2629  }
2630  
2631  int32_t qloadboard(const char* filename, char flags, vec3_t* dapos, int16_t* daang, int16_t* dacursectnum)
2632  {
2633      // NUKE-TODO: implement flags, see mapedit.cpp
2634      UNREFERENCED_PARAMETER(flags);
2635      return dbLoadMap(filename, &dapos->x, &dapos->y, &dapos->z, (short*)daang, (short*)dacursectnum, NULL);
2636  }
2637  
2638  int32_t qsaveboard(const char* filename, const vec3_t* dapos, int16_t daang, int16_t dacursectnum)
2639  {
2640      // NUKE-TODO: see mapedit.cpp
2641      byte_1A76C6 = byte_1A76C8 = byte_1A76C7 = 1;
2642      return dbSaveMap(filename, dapos->x, dapos->y, dapos->z, daang, dacursectnum);
2643  }