From fdfcb728a970113548f6aafb425fc77eacecdfad Mon Sep 17 00:00:00 2001 From: Shawn Walker Date: Tue, 13 May 2014 22:50:39 -0700 Subject: [PATCH 1/7] - make it easier to spot patch definition errors in console log --- src/textures/multipatchtexture.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp index 124add5db9..d72391341f 100644 --- a/src/textures/multipatchtexture.cpp +++ b/src/textures/multipatchtexture.cpp @@ -47,6 +47,7 @@ #include "colormatcher.h" #include "v_palette.h" #include "v_video.h" +#include "v_text.h" #include "m_fixed.h" #include "textures/textures.h" #include "r_data/colormaps.h" @@ -275,7 +276,7 @@ FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchl Parts[i].Texture = patchlookup[LittleShort(mpatch.d->patch)].Texture; if (Parts[i].Texture == NULL) { - Printf ("Unknown patch %s in texture %s\n", patchlookup[LittleShort(mpatch.d->patch)].Name, Name); + Printf (TEXTCOLOR_RED "Unknown patch %s in texture %s\n", patchlookup[LittleShort(mpatch.d->patch)].Name, Name); NumParts--; i--; } @@ -1018,7 +1019,7 @@ void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part, bool silent, i } if (part.Texture == NULL) { - if (!silent) Printf("Unknown patch '%s' in texture '%s'\n", sc.String, Name); + if (!silent) Printf(TEXTCOLOR_RED "Unknown patch '%s' in texture '%s'\n", sc.String, Name); } sc.MustGetStringName(","); sc.MustGetNumber(); From 59885b856deb34fe11928a3ca1277ad8315af028 Mon Sep 17 00:00:00 2001 From: Shawn Walker Date: Sun, 18 May 2014 15:38:46 -0700 Subject: [PATCH 2/7] - remove texture name length limits for udmf maps --- src/p_doors.cpp | 2 +- src/r_data/sprites.cpp | 6 +- src/textures/buildtexture.cpp | 2 +- src/textures/canvastexture.cpp | 3 +- src/textures/imgztexture.cpp | 1 - src/textures/multipatchtexture.cpp | 2666 ++++++++++++++-------------- src/textures/texture.cpp | 12 +- src/textures/texturemanager.cpp | 46 +- src/textures/textures.h | 6 +- src/textures/tgatexture.cpp | 1 - src/w_wad.cpp | 12 +- src/w_wad.h | 3 +- 12 files changed, 1383 insertions(+), 1377 deletions(-) diff --git a/src/p_doors.cpp b/src/p_doors.cpp index b530f688dd..7c6952d7f2 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -297,7 +297,7 @@ void DDoor::DoorSound(bool raise, DSeqNode *curseq) const continue; FTexture *tex = TexMan[line->sidedef[0]->GetTexture(side_t::top)]; - texname = tex? tex->Name : NULL; + texname = tex ? tex->Name.GetChars() : NULL; if (texname != NULL && texname[0] == 'D' && texname[1] == 'O' && texname[2] == 'R') { switch (texname[3]) diff --git a/src/r_data/sprites.cpp b/src/r_data/sprites.cpp index 012abb2f66..6046194035 100644 --- a/src/r_data/sprites.cpp +++ b/src/r_data/sprites.cpp @@ -249,6 +249,8 @@ static void R_InstallSprite (int num) // letter/number appended. // The rotation character can be 0 to signify no rotations. // +#define TEX_DWNAME(tex) MAKE_ID(tex->Name[0], tex->Name[1], tex->Name[2], tex->Name[3]) + void R_InitSpriteDefs () { struct Hasher @@ -272,7 +274,7 @@ void R_InitSpriteDefs () FTexture *tex = TexMan.ByIndex(i); if (tex->UseType == FTexture::TEX_Sprite && strlen(tex->Name) >= 6) { - size_t bucket = tex->dwName % smax; + size_t bucket = TEX_DWNAME(tex) % smax; hashes[i].Next = hashes[bucket].Head; hashes[bucket].Head = i; } @@ -352,7 +354,7 @@ void R_InitSpriteDefs () while (hash != -1) { FTexture *tex = TexMan[hash]; - if (tex->dwName == intname) + if (TEX_DWNAME(tex) == intname) { bool res = R_InstallSpriteLump (FTextureID(hash), tex->Name[4] - 'A', tex->Name[5], false); diff --git a/src/textures/buildtexture.cpp b/src/textures/buildtexture.cpp index e534ca9c81..ee5f66fab8 100644 --- a/src/textures/buildtexture.cpp +++ b/src/textures/buildtexture.cpp @@ -78,7 +78,7 @@ FBuildTexture::FBuildTexture (int tilenum, const BYTE *pixels, int width, int he LeftOffset = left; TopOffset = top; CalcBitSize (); - mysnprintf (Name, countof(Name), "BTIL%04d", tilenum); + Name.Format("BTIL%04d", tilenum); UseType = TEX_Build; } diff --git a/src/textures/canvastexture.cpp b/src/textures/canvastexture.cpp index a6e1918dfc..8bfd1946b7 100644 --- a/src/textures/canvastexture.cpp +++ b/src/textures/canvastexture.cpp @@ -41,8 +41,7 @@ FCanvasTexture::FCanvasTexture (const char *name, int width, int height) { - strncpy (Name, name, 8); - Name[8] = 0; + Name = name; Width = width; Height = height; LeftOffset = TopOffset = 0; diff --git a/src/textures/imgztexture.cpp b/src/textures/imgztexture.cpp index 36f954b95e..153880d67d 100644 --- a/src/textures/imgztexture.cpp +++ b/src/textures/imgztexture.cpp @@ -106,7 +106,6 @@ FIMGZTexture::FIMGZTexture (int lumpnum, WORD w, WORD h, SWORD l, SWORD t) : FTexture(NULL, lumpnum), Pixels(0), Spans(0) { Wads.GetLumpName (Name, lumpnum); - Name[8] = 0; Width = w; Height = h; LeftOffset = l; diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp index d72391341f..c51f301687 100644 --- a/src/textures/multipatchtexture.cpp +++ b/src/textures/multipatchtexture.cpp @@ -1,1332 +1,1336 @@ -/* -** multipatchtexture.cpp -** Texture class for standard Doom multipatch textures -** -**--------------------------------------------------------------------------- -** Copyright 2004-2006 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -** -*/ - -#include -#include "doomtype.h" -#include "files.h" -#include "w_wad.h" -#include "i_system.h" -#include "gi.h" -#include "st_start.h" -#include "sc_man.h" -#include "templates.h" -#include "r_data/r_translate.h" -#include "bitmap.h" -#include "colormatcher.h" -#include "v_palette.h" -#include "v_video.h" -#include "v_text.h" -#include "m_fixed.h" -#include "textures/textures.h" -#include "r_data/colormaps.h" - -// On the Alpha, accessing the shorts directly if they aren't aligned on a -// 4-byte boundary causes unaligned access warnings. Why it does this at -// all and only while initing the textures is beyond me. - -#ifdef ALPHA -#define SAFESHORT(s) ((short)(((BYTE *)&(s))[0] + ((BYTE *)&(s))[1] * 256)) -#else -#define SAFESHORT(s) LittleShort(s) -#endif - - - -//-------------------------------------------------------------------------- -// -// Data structures for the TEXTUREx lumps -// -//-------------------------------------------------------------------------- - -// -// Each texture is composed of one or more patches, with patches being lumps -// stored in the WAD. The lumps are referenced by number, and patched into -// the rectangular texture space using origin and possibly other attributes. -// -struct mappatch_t -{ - SWORD originx; - SWORD originy; - SWORD patch; - SWORD stepdir; - SWORD colormap; -}; - -// -// A wall texture is a list of patches which are to be combined in a -// predefined order. -// -struct maptexture_t -{ +/* +** multipatchtexture.cpp +** Texture class for standard Doom multipatch textures +** +**--------------------------------------------------------------------------- +** Copyright 2004-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** +*/ + +#include +#include "doomtype.h" +#include "files.h" +#include "w_wad.h" +#include "i_system.h" +#include "gi.h" +#include "st_start.h" +#include "sc_man.h" +#include "templates.h" +#include "r_data/r_translate.h" +#include "bitmap.h" +#include "colormatcher.h" +#include "v_palette.h" +#include "v_video.h" +#include "v_text.h" +#include "m_fixed.h" +#include "textures/textures.h" +#include "r_data/colormaps.h" + +// On the Alpha, accessing the shorts directly if they aren't aligned on a +// 4-byte boundary causes unaligned access warnings. Why it does this at +// all and only while initing the textures is beyond me. + +#ifdef ALPHA +#define SAFESHORT(s) ((short)(((BYTE *)&(s))[0] + ((BYTE *)&(s))[1] * 256)) +#else +#define SAFESHORT(s) LittleShort(s) +#endif + + + +//-------------------------------------------------------------------------- +// +// Data structures for the TEXTUREx lumps +// +//-------------------------------------------------------------------------- + +// +// Each texture is composed of one or more patches, with patches being lumps +// stored in the WAD. The lumps are referenced by number, and patched into +// the rectangular texture space using origin and possibly other attributes. +// +struct mappatch_t +{ + SWORD originx; + SWORD originy; + SWORD patch; + SWORD stepdir; + SWORD colormap; +}; + +// +// A wall texture is a list of patches which are to be combined in a +// predefined order. +// +struct maptexture_t +{ BYTE name[8]; - WORD Flags; // [RH] Was unused - BYTE ScaleX; // [RH] Scaling (8 is normal) - BYTE ScaleY; // [RH] Same as above - SWORD width; - SWORD height; - BYTE columndirectory[4]; // OBSOLETE - SWORD patchcount; - mappatch_t patches[1]; -}; - -#define MAPTEXF_WORLDPANNING 0x8000 - -// Strife uses versions of the above structures that remove all unused fields - -struct strifemappatch_t -{ - SWORD originx; - SWORD originy; - SWORD patch; -}; - -// -// A wall texture is a list of patches which are to be combined in a -// predefined order. -// -struct strifemaptexture_t -{ - BYTE name[8]; - WORD Flags; // [RH] Was unused - BYTE ScaleX; // [RH] Scaling (8 is normal) - BYTE ScaleY; // [RH] Same as above - SWORD width; - SWORD height; - SWORD patchcount; - strifemappatch_t patches[1]; -}; - - -//========================================================================== -// -// In-memory representation of a single PNAMES lump entry -// -//========================================================================== - -struct FPatchLookup -{ - char Name[9]; - FTexture *Texture; -}; - - -//========================================================================== -// -// A texture defined in a TEXTURE1 or TEXTURE2 lump -// -//========================================================================== - -class FMultiPatchTexture : public FTexture -{ -public: - FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflump); - FMultiPatchTexture (FScanner &sc, int usetype); - ~FMultiPatchTexture (); - - const BYTE *GetColumn (unsigned int column, const Span **spans_out); - const BYTE *GetPixels (); - FTextureFormat GetFormat(); - bool UseBasePalette() ; - void Unload (); - virtual void SetFrontSkyLayer (); - - int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf = NULL); - int GetSourceLump() { return DefinitionLump; } - FTexture *GetRedirect(bool wantwarped); - FTexture *GetRawTexture(); - -protected: - BYTE *Pixels; - Span **Spans; - int DefinitionLump; - - struct TexPart - { - SWORD OriginX, OriginY; - BYTE Rotate; - BYTE op; - FRemapTable *Translation; - PalEntry Blend; - FTexture *Texture; - fixed_t Alpha; - - TexPart(); - }; - - int NumParts; - TexPart *Parts; - bool bRedirect:1; - bool bTranslucentPatches:1; - - void MakeTexture (); - -private: - void CheckForHacks (); - void ParsePatch(FScanner &sc, TexPart & part, bool silent, int usetype); -}; - -//========================================================================== -// -// FMultiPatchTexture :: FMultiPatchTexture -// -//========================================================================== - -FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflumpnum) -: Pixels (0), Spans(0), Parts(0), bRedirect(false), bTranslucentPatches(false) -{ - union - { - const maptexture_t *d; - const strifemaptexture_t *s; - } - mtexture; - - union - { - const mappatch_t *d; - const strifemappatch_t *s; - } - mpatch; - - int i; - - mtexture.d = (const maptexture_t *)texdef; - bMultiPatch = true; - - if (strife) - { - NumParts = SAFESHORT(mtexture.s->patchcount); - } - else - { - NumParts = SAFESHORT(mtexture.d->patchcount); - } - - if (NumParts < 0) - { - I_FatalError ("Bad texture directory"); - } - - UseType = FTexture::TEX_Wall; - Parts = NumParts > 0 ? new TexPart[NumParts] : NULL; - Width = SAFESHORT(mtexture.d->width); - Height = SAFESHORT(mtexture.d->height); - strncpy (Name, (const char *)mtexture.d->name, 8); - Name[8] = 0; - CalcBitSize (); - - xScale = mtexture.d->ScaleX ? mtexture.d->ScaleX*(FRACUNIT/8) : FRACUNIT; - yScale = mtexture.d->ScaleY ? mtexture.d->ScaleY*(FRACUNIT/8) : FRACUNIT; - - if (mtexture.d->Flags & MAPTEXF_WORLDPANNING) - { - bWorldPanning = true; - } - - if (strife) - { - mpatch.s = &mtexture.s->patches[0]; - } - else - { - mpatch.d = &mtexture.d->patches[0]; - } - - for (i = 0; i < NumParts; ++i) - { - if (unsigned(LittleShort(mpatch.d->patch)) >= unsigned(maxpatchnum)) - { - I_FatalError ("Bad PNAMES and/or texture directory:\n\nPNAMES has %d entries, but\n%s wants to use entry %d.", - maxpatchnum, Name, LittleShort(mpatch.d->patch)+1); - } - Parts[i].OriginX = LittleShort(mpatch.d->originx); - Parts[i].OriginY = LittleShort(mpatch.d->originy); - Parts[i].Texture = patchlookup[LittleShort(mpatch.d->patch)].Texture; - if (Parts[i].Texture == NULL) - { - Printf (TEXTCOLOR_RED "Unknown patch %s in texture %s\n", patchlookup[LittleShort(mpatch.d->patch)].Name, Name); - NumParts--; - i--; - } - else - { - Parts[i].Texture->bKeepAround = true; - } - if (strife) - mpatch.s++; - else - mpatch.d++; - } - if (NumParts == 0) - { - Printf ("Texture %s is left without any patches\n", Name); - } - - CheckForHacks (); - - // If this texture is just a wrapper around a single patch, we can simply - // forward GetPixels() and GetColumn() calls to that patch. - if (NumParts == 1) - { - if (Parts->OriginX == 0 && Parts->OriginY == 0 && - Parts->Texture->GetWidth() == Width && - Parts->Texture->GetHeight() == Height) - { - bRedirect = true; - } - } - DefinitionLump = deflumpnum; -} - -//========================================================================== -// -// FMultiPatchTexture :: ~FMultiPatchTexture -// -//========================================================================== - -FMultiPatchTexture::~FMultiPatchTexture () -{ - Unload (); - if (Parts != NULL) - { - for(int i=0; iSetFrontSkyLayer (); - } - bNoRemap0 = true; -} - -//========================================================================== -// -// FMultiPatchTexture :: Unload -// -//========================================================================== - -void FMultiPatchTexture::Unload () -{ - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } -} - -//========================================================================== -// -// FMultiPatchTexture :: GetPixels -// -//========================================================================== - -const BYTE *FMultiPatchTexture::GetPixels () -{ - if (bRedirect) - { - return Parts->Texture->GetPixels (); - } - if (Pixels == NULL) - { - MakeTexture (); - } - return Pixels; -} - -//========================================================================== -// -// FMultiPatchTexture :: GetColumn -// -//========================================================================== - -const BYTE *FMultiPatchTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - if (bRedirect) - { - return Parts->Texture->GetColumn (column, spans_out); - } - if (Pixels == NULL) - { - MakeTexture (); - } - if ((unsigned)column >= (unsigned)Width) - { - if (WidthMask + 1 == Width) - { - column &= WidthMask; - } - else - { - column %= Width; - } - } - if (spans_out != NULL) - { - if (Spans == NULL) - { - Spans = CreateSpans (Pixels); - } - *spans_out = Spans[column]; - } - return Pixels + column*Height; -} - - -//========================================================================== -// -// GetBlendMap -// -//========================================================================== - -BYTE *GetBlendMap(PalEntry blend, BYTE *blendwork) -{ - - switch (blend.a==0 ? int(blend) : -1) - { - case BLEND_ICEMAP: - return TranslationToTable(TRANSLATION(TRANSLATION_Standard, 7))->Remap; - - default: - if (blend >= BLEND_SPECIALCOLORMAP1 && blend < BLEND_SPECIALCOLORMAP1 + SpecialColormaps.Size()) - { - return SpecialColormaps[blend - BLEND_SPECIALCOLORMAP1].Colormap; - } - else if (blend >= BLEND_DESATURATE1 && blend <= BLEND_DESATURATE31) - { - return DesaturateColormap[blend - BLEND_DESATURATE1]; - } - else - { - blendwork[0]=0; - if (blend.a == 255) - { - for(int i=1;i<256;i++) - { - int rr = (blend.r * GPalette.BaseColors[i].r) / 255; - int gg = (blend.g * GPalette.BaseColors[i].g) / 255; - int bb = (blend.b * GPalette.BaseColors[i].b) / 255; - - blendwork[i] = ColorMatcher.Pick(rr, gg, bb); - } - return blendwork; - } - else if (blend.a != 0) - { - for(int i=1;i<256;i++) - { - int rr = (blend.r * blend.a + GPalette.BaseColors[i].r * (255-blend.a)) / 255; - int gg = (blend.g * blend.a + GPalette.BaseColors[i].g * (255-blend.a)) / 255; - int bb = (blend.b * blend.a + GPalette.BaseColors[i].b * (255-blend.a)) / 255; - - blendwork[i] = ColorMatcher.Pick(rr, gg, bb); - } - return blendwork; - } - } - } - return NULL; -} - -//========================================================================== -// -// FMultiPatchTexture :: MakeTexture -// -//========================================================================== - -void FMultiPatchTexture::MakeTexture () -{ - // Add a little extra space at the end if the texture's height is not - // a power of 2, in case somebody accidentally makes it repeat vertically. - int numpix = Width * Height + (1 << HeightBits) - Height; - BYTE blendwork[256]; - bool hasTranslucent = false; - - Pixels = new BYTE[numpix]; - memset (Pixels, 0, numpix); - - for (int i = 0; i < NumParts; ++i) - { - if (Parts[i].op != OP_COPY) - { - hasTranslucent = true; - } - } - - if (!hasTranslucent) - { - for (int i = 0; i < NumParts; ++i) - { - if (Parts[i].Texture->bHasCanvas) continue; // cannot use camera textures as patch. - - BYTE *trans = Parts[i].Translation ? Parts[i].Translation->Remap : NULL; - { - if (Parts[i].Blend != 0) - { - trans = GetBlendMap(Parts[i].Blend, blendwork); - } - Parts[i].Texture->CopyToBlock (Pixels, Width, Height, - Parts[i].OriginX, Parts[i].OriginY, Parts[i].Rotate, trans); - } - } - } - else - { - // In case there are translucent patches let's do the composition in - // True color to keep as much precision as possible before downconverting to the palette. - BYTE *buffer = new BYTE[Width * Height * 4]; - memset(buffer, 0, Width * Height * 4); - FillBuffer(buffer, Width * 4, Height, TEX_RGB); - for(int y = 0; y < Height; y++) - { - BYTE *in = buffer + Width * y * 4; - BYTE *out = Pixels + y; - for (int x = 0; x < Width; x++) - { - if (*out == 0 && in[3] != 0) - { - *out = RGB32k[in[2]>>3][in[1]>>3][in[0]>>3]; - } - out += Height; - in += 4; - } - } - delete [] buffer; - } -} - -//=========================================================================== -// -// FMultipatchTexture::CopyTrueColorPixels -// -// Preserves the palettes of each individual patch -// -//=========================================================================== - -int FMultiPatchTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) -{ - int retv = -1; - - if (bRedirect) - { // Redirect straight to the real texture's routine. - return Parts[0].Texture->CopyTrueColorPixels(bmp, x, y, rotate, inf); - } - - if (rotate != 0 || (inf != NULL && ((inf->op != OP_OVERWRITE && inf->op != OP_COPY) || inf->blend != BLEND_NONE))) - { // We are doing some sort of fancy stuff to the destination bitmap, so composite to - // a temporary bitmap, and copy that. - FBitmap tbmp; - if (tbmp.Create(Width, Height)) - { - retv = MAX(retv, CopyTrueColorPixels(&tbmp, 0, 0, 0)); - bmp->CopyPixelDataRGB(x, y, tbmp.GetPixels(), Width, Height, - 4, tbmp.GetPitch(), rotate, CF_BGRA, inf); - } - return retv; - } - - // When compositing a multipatch texture with multipatch parts, - // drawing must be restricted to the actual area which is covered by this texture. - FClipRect saved_cr = bmp->GetClipRect(); - bmp->IntersectClipRect(x, y, Width, Height); - - if (inf != NULL && inf->op == OP_OVERWRITE) - { - bmp->Zero(); - } - - for(int i = 0; i < NumParts; i++) - { - int ret = -1; - FCopyInfo info; - - if (Parts[i].Texture->bHasCanvas) continue; // cannot use camera textures as patch. - - memset (&info, 0, sizeof(info)); - info.alpha = Parts[i].Alpha; - info.invalpha = FRACUNIT - info.alpha; - info.op = ECopyOp(Parts[i].op); - PalEntry b = Parts[i].Blend; - if (b.a == 0 && b != BLEND_NONE) - { - info.blend = EBlend(b.d); - } - else if (b.a != 0) - { - if (b.a == 255) - { - info.blendcolor[0] = b.r * FRACUNIT / 255; - info.blendcolor[1] = b.g * FRACUNIT / 255; - info.blendcolor[2] = b.b * FRACUNIT / 255; - info.blend = BLEND_MODULATE; - } - else - { - fixed_t blendalpha = b.a * FRACUNIT / 255; - info.blendcolor[0] = b.r * blendalpha; - info.blendcolor[1] = b.g * blendalpha; - info.blendcolor[2] = b.b * blendalpha; - info.blendcolor[3] = FRACUNIT - blendalpha; - info.blend = BLEND_OVERLAY; - } - } - - if (Parts[i].Translation != NULL) - { // Using a translation forces downconversion to the base palette - ret = Parts[i].Texture->CopyTrueColorTranslated(bmp, x+Parts[i].OriginX, y+Parts[i].OriginY, Parts[i].Rotate, Parts[i].Translation, &info); - } - else - { - ret = Parts[i].Texture->CopyTrueColorPixels(bmp, x+Parts[i].OriginX, y+Parts[i].OriginY, Parts[i].Rotate, &info); - } - - if (ret > retv) retv = ret; - } - // Restore previous clipping rectangle. - bmp->SetClipRect(saved_cr); - return retv; -} - -//========================================================================== -// -// FMultiPatchTexture :: GetFormat -// -// only returns 'paletted' if all patches use the base palette. -// -//========================================================================== - -FTextureFormat FMultiPatchTexture::GetFormat() -{ - if (bComplex) return TEX_RGB; - if (NumParts == 1) return Parts[0].Texture->GetFormat(); - return UseBasePalette() ? TEX_Pal : TEX_RGB; -} - - -//=========================================================================== -// -// FMultipatchTexture::UseBasePalette -// -// returns true if all patches in the texture use the unmodified base -// palette. -// -//=========================================================================== - -bool FMultiPatchTexture::UseBasePalette() -{ - if (bComplex) return false; - for(int i=0;iUseBasePalette()) return false; - } - return true; -} - -//========================================================================== -// -// FMultiPatchTexture :: CheckForHacks -// -//========================================================================== - -void FMultiPatchTexture::CheckForHacks () -{ - if (NumParts <= 0) - { - return; - } - - // Heretic sky textures are marked as only 128 pixels tall, - // even though they are really 200 pixels tall. - if (gameinfo.gametype == GAME_Heretic && - Name[0] == 'S' && - Name[1] == 'K' && - Name[2] == 'Y' && - Name[4] == 0 && - Name[3] >= '1' && - Name[3] <= '3' && - Height == 128) - { - Height = 200; - HeightBits = 8; - return; - } - - // The Doom E1 sky has its patch's y offset at -8 instead of 0. - if (gameinfo.gametype == GAME_Doom && - !(gameinfo.flags & GI_MAPxx) && - NumParts == 1 && - Height == 128 && - Parts->OriginY == -8 && - Name[0] == 'S' && - Name[1] == 'K' && - Name[2] == 'Y' && - Name[3] == '1' && - Name[4] == 0) - { - Parts->OriginY = 0; - return; - } - - // BIGDOOR7 in Doom also has patches at y offset -4 instead of 0. - if (gameinfo.gametype == GAME_Doom && - !(gameinfo.flags & GI_MAPxx) && - NumParts == 2 && - Height == 128 && - Parts[0].OriginY == -4 && - Parts[1].OriginY == -4 && - Name[0] == 'B' && - Name[1] == 'I' && - Name[2] == 'G' && - Name[3] == 'D' && - Name[4] == 'O' && - Name[5] == 'O' && - Name[6] == 'R' && - Name[7] == '7') - { - Parts[0].OriginY = 0; - Parts[1].OriginY = 0; - return; - } - - // [RH] Some wads (I forget which!) have single-patch textures 256 - // pixels tall that have patch lengths recorded as 0. I can't think of - // any good reason for them to do this, and since I didn't make note - // of which wad made me hack in support for them, the hack is gone - // because I've added support for DeePsea's true tall patches. - // - // Okay, I found a wad with crap patches: Pleiades.wad's sky patches almost - // fit this description and are a big mess, but they're not single patch! - if (Height == 256) - { - int i; - - // All patches must be at the top of the texture for this fix - for (i = 0; i < NumParts; ++i) - { - if (Parts[i].OriginY != 0) - { - break; - } - } - - if (i == NumParts) - { - for (i = 0; i < NumParts; ++i) - { - Parts[i].Texture->HackHack(256); - } - } - } -} - -//========================================================================== -// -// FMultiPatchTexture :: GetRedirect -// -//========================================================================== - -FTexture *FMultiPatchTexture::GetRedirect(bool wantwarped) -{ - return bRedirect ? Parts->Texture : this; -} - -//========================================================================== -// -// FMultiPatchTexture :: GetRawTexture -// -// Doom ignored all compositing of mid-sided textures on two-sided lines. -// Since these textures had to be single-patch in Doom, that essentially -// means it ignores their Y offsets. -// -// If this texture is composed of only one patch, return that patch. -// Otherwise, return this texture, since Doom wouldn't have been able to -// draw it anyway. -// -//========================================================================== - -FTexture *FMultiPatchTexture::GetRawTexture() -{ - return NumParts == 1 ? Parts->Texture : this; -} - -//========================================================================== -// -// FMultiPatchTexture :: TexPart :: TexPart -// -//========================================================================== - -FMultiPatchTexture::TexPart::TexPart() -{ - OriginX = OriginY = 0; - Rotate = 0; - Texture = NULL; - Translation = NULL; - Blend = 0; - Alpha = FRACUNIT; - op = OP_COPY; -} - -//========================================================================== -// -// FTextureManager :: AddTexturesLump -// -//========================================================================== - -void FTextureManager::AddTexturesLump (const void *lumpdata, int lumpsize, int deflumpnum, int patcheslump, int firstdup, bool texture1) -{ - FPatchLookup *patchlookup; - int i; - DWORD numpatches; - - if (firstdup == 0) - { - firstdup = (int)Textures.Size(); - } - - { - FWadLump pnames = Wads.OpenLumpNum (patcheslump); - - pnames >> numpatches; - - // Check whether the amount of names reported is correct. - if ((signed)numpatches < 0) - { - Printf("Corrupt PNAMES lump found (negative amount of entries reported)"); - return; - } - - // Check whether the amount of names reported is correct. - int lumplength = Wads.LumpLength(patcheslump); - if (numpatches > DWORD((lumplength-4)/8)) - { - Printf("PNAMES lump is shorter than required (%u entries reported but only %d bytes (%d entries) long\n", - numpatches, lumplength, (lumplength-4)/8); - // Truncate but continue reading. Who knows how many such lumps exist? - numpatches = (lumplength-4)/8; - } - - // Catalog the patches these textures use so we know which - // textures they represent. - patchlookup = (FPatchLookup *)alloca (numpatches * sizeof(*patchlookup)); - - for (DWORD i = 0; i < numpatches; ++i) - { - pnames.Read (patchlookup[i].Name, 8); - patchlookup[i].Name[8] = 0; - FTextureID j = CheckForTexture (patchlookup[i].Name, FTexture::TEX_WallPatch); - if (j.isValid()) - { - patchlookup[i].Texture = Textures[j.GetIndex()].Texture; - } - else - { - // Shareware Doom has the same PNAMES lump as the registered - // Doom, so printing warnings for patches that don't really - // exist isn't such a good idea. - //Printf ("Patch %s not found.\n", patchlookup[i].Name); - patchlookup[i].Texture = NULL; - } - } - } - - bool isStrife = false; - const DWORD *maptex, *directory; - DWORD maxoff; - int numtextures; - DWORD offset = 0; // Shut up, GCC! - - maptex = (const DWORD *)lumpdata; - numtextures = LittleLong(*maptex); - maxoff = lumpsize; - - if (maxoff < DWORD(numtextures+1)*4) - { - Printf ("Texture directory is too short"); - return; - } - - // Scan the texture lump to decide if it contains Doom or Strife textures - for (i = 0, directory = maptex+1; i < numtextures; ++i) - { - offset = LittleLong(directory[i]); - if (offset > maxoff) - { - Printf ("Bad texture directory"); - return; - } - - maptexture_t *tex = (maptexture_t *)((BYTE *)maptex + offset); - - // There is bizzarely a Doom editing tool that writes to the - // first two elements of columndirectory, so I can't check those. - if (SAFESHORT(tex->patchcount) < 0 || - tex->columndirectory[2] != 0 || - tex->columndirectory[3] != 0) - { - isStrife = true; - break; - } - } - - - // Textures defined earlier in the lump take precedence over those defined later, - // but later TEXTUREx lumps take precedence over earlier ones. - for (i = 1, directory = maptex; i <= numtextures; ++i) - { - if (i == 1 && texture1) - { - // The very first texture is just a dummy. Copy its dimensions to texture 0. - // It still needs to be created in case someone uses it by name. - offset = LittleLong(directory[1]); - const maptexture_t *tex = (const maptexture_t *)((const BYTE *)maptex + offset); - FDummyTexture *tex0 = static_cast(Textures[0].Texture); - tex0->SetSize (SAFESHORT(tex->width), SAFESHORT(tex->height)); - } - - offset = LittleLong(directory[i]); - if (offset > maxoff) - { - Printf ("Bad texture directory"); - return; - } - - // If this texture was defined already in this lump, skip it - // This could cause problems with animations that use the same name for intermediate - // textures. Should I be worried? - int j; - for (j = (int)Textures.Size() - 1; j >= firstdup; --j) - { - if (strnicmp (Textures[j].Texture->Name, (const char *)maptex + offset, 8) == 0) - break; - } - if (j + 1 == firstdup) - { - FMultiPatchTexture *tex = new FMultiPatchTexture ((const BYTE *)maptex + offset, patchlookup, numpatches, isStrife, deflumpnum); - if (i == 1 && texture1) - { - tex->UseType = FTexture::TEX_FirstDefined; - } - TexMan.AddTexture (tex); - StartScreen->Progress(); - } - } -} - - -//========================================================================== -// -// FTextureManager :: AddTexturesLumps -// -//========================================================================== - -void FTextureManager::AddTexturesLumps (int lump1, int lump2, int patcheslump) -{ - int firstdup = (int)Textures.Size(); - - if (lump1 >= 0) - { - FMemLump texdir = Wads.ReadLump (lump1); - AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump1), lump1, patcheslump, firstdup, true); - } - if (lump2 >= 0) - { - FMemLump texdir = Wads.ReadLump (lump2); - AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump2), lump2, patcheslump, firstdup, false); - } -} - - -//========================================================================== -// -// -// -//========================================================================== - -void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part, bool silent, int usetype) -{ - FString patchname; - sc.MustGetString(); - - FTextureID texno = TexMan.CheckForTexture(sc.String, usetype); - int Mirror = 0; - - if (!texno.isValid()) - { - if (strlen(sc.String) <= 8 && !strpbrk(sc.String, "./")) - { - int lumpnum = Wads.CheckNumForName(sc.String, usetype == TEX_MiscPatch? ns_graphics : ns_patches); - if (lumpnum >= 0) - { - part.Texture = FTexture::CreateTexture(lumpnum, usetype); - TexMan.AddTexture(part.Texture); - } - } - } - else - { - part.Texture = TexMan[texno]; - bComplex |= part.Texture->bComplex; - } - if (part.Texture == NULL) - { - if (!silent) Printf(TEXTCOLOR_RED "Unknown patch '%s' in texture '%s'\n", sc.String, Name); - } - sc.MustGetStringName(","); - sc.MustGetNumber(); - part.OriginX = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - part.OriginY = sc.Number; - - if (sc.CheckString("{")) - { - while (!sc.CheckString("}")) - { - sc.MustGetString(); - if (sc.Compare("flipx")) - { - Mirror |= 1; - } - else if (sc.Compare("flipy")) - { - Mirror |= 2; - } - else if (sc.Compare("rotate")) - { - sc.MustGetNumber(); - sc.Number = (((sc.Number + 90)%360)-90); - if (sc.Number != 0 && sc.Number !=90 && sc.Number != 180 && sc.Number != -90) - { - sc.ScriptError("Rotation must be a multiple of 90 degrees."); - } - part.Rotate = (sc.Number / 90) & 3; - } - else if (sc.Compare("Translation")) - { - int match; - - bComplex = true; - if (part.Translation != NULL) delete part.Translation; - part.Translation = NULL; - part.Blend = 0; - static const char *maps[] = { "inverse", "gold", "red", "green", "blue", NULL }; - sc.MustGetString(); - - match = sc.MatchString(maps); - if (match >= 0) - { - part.Blend = BLEND_SPECIALCOLORMAP1 + match; - } - else if (sc.Compare("ICE")) - { - part.Blend = BLEND_ICEMAP; - } - else if (sc.Compare("DESATURATE")) - { - sc.MustGetStringName(","); - sc.MustGetNumber(); - part.Blend = BLEND_DESATURATE1 + clamp(sc.Number-1, 0, 30); - } - else - { - sc.UnGet(); - part.Translation = new FRemapTable; - part.Translation->MakeIdentity(); - do - { - sc.MustGetString(); - part.Translation->AddToTranslation(sc.String); - } - while (sc.CheckString(",")); - } - - } - else if (sc.Compare("Colormap")) - { - float r1,g1,b1; - float r2,g2,b2; - - sc.MustGetFloat(); - r1 = (float)sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - g1 = (float)sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - b1 = (float)sc.Float; - if (!sc.CheckString(",")) - { - part.Blend = AddSpecialColormap(0,0,0, r1, g1, b1); - } - else - { - sc.MustGetFloat(); - r2 = (float)sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - g2 = (float)sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - b2 = (float)sc.Float; - part.Blend = AddSpecialColormap(r1, g1, b1, r2, g2, b2); - } - } - else if (sc.Compare("Blend")) - { - bComplex = true; - if (part.Translation != NULL) delete part.Translation; - part.Translation = NULL; - part.Blend = 0; - - if (!sc.CheckNumber()) - { - sc.MustGetString(); - part.Blend = V_GetColor(NULL, sc.String); - } - else - { - int r,g,b; - - r = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - g = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - b = sc.Number; - sc.MustGetStringName(","); - part.Blend = MAKERGB(r, g, b); - } - // Blend.a may never be 0 here. - if (sc.CheckString(",")) - { - sc.MustGetFloat(); - if (sc.Float > 0.f) - part.Blend.a = clamp(int(sc.Float*255), 1, 254); - else - part.Blend = 0; - } - else part.Blend.a = 255; - } - else if (sc.Compare("alpha")) - { - sc.MustGetFloat(); - part.Alpha = clamp(FLOAT2FIXED(sc.Float), 0, FRACUNIT); - // bComplex is not set because it is only needed when the style is not OP_COPY. - } - else if (sc.Compare("style")) - { - static const char *styles[] = {"copy", "translucent", "add", "subtract", "reversesubtract", "modulate", "copyalpha", "copynewalpha", "overlay", NULL }; - sc.MustGetString(); - part.op = sc.MustMatchString(styles); - bComplex |= (part.op != OP_COPY); - bTranslucentPatches = bComplex; - } - else if (sc.Compare("useoffsets")) - { - if (part.Texture != NULL) - { - part.OriginX -= part.Texture->LeftOffset; - part.OriginY -= part.Texture->TopOffset; - } - } - } - } - if (Mirror & 2) - { - part.Rotate = (part.Rotate + 2) & 3; - Mirror ^= 1; - } - if (Mirror & 1) - { - part.Rotate |= 4; - } -} - -//========================================================================== -// -// Constructor for text based multipatch definitions -// -//========================================================================== - -FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) -: Pixels (0), Spans(0), Parts(0), bRedirect(false), bTranslucentPatches(false) -{ - TArray parts; - bool bSilent = false; - - bMultiPatch = true; - sc.SetCMode(true); - sc.MustGetString(); - const char* textureName = NULL; - if (sc.Compare("optional")) - { - bSilent = true; - sc.MustGetString(); - if (sc.Compare(",")) - { - // this is not right. Apparently a texture named 'optional' is being defined right now... - sc.UnGet(); - textureName = "optional"; - bSilent = false; - } - } - uppercopy(Name, !textureName ? sc.String : textureName); - Name[8] = 0; - sc.MustGetStringName(","); - sc.MustGetNumber(); - Width = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - Height = sc.Number; - UseType = usetype; - - if (sc.CheckString("{")) - { - while (!sc.CheckString("}")) - { - sc.MustGetString(); - if (sc.Compare("XScale")) - { - sc.MustGetFloat(); - xScale = FLOAT2FIXED(sc.Float); - if (xScale == 0) sc.ScriptError("Texture %s is defined with null x-scale\n", Name); - } - else if (sc.Compare("YScale")) - { - sc.MustGetFloat(); - yScale = FLOAT2FIXED(sc.Float); - if (yScale == 0) sc.ScriptError("Texture %s is defined with null y-scale\n", Name); - } - else if (sc.Compare("WorldPanning")) - { - bWorldPanning = true; - } - else if (sc.Compare("NullTexture")) - { - UseType = FTexture::TEX_Null; - } - else if (sc.Compare("NoDecals")) - { - bNoDecals = true; - } - else if (sc.Compare("Patch")) - { - TexPart part; - ParsePatch(sc, part, bSilent, TEX_WallPatch); - if (part.Texture != NULL) parts.Push(part); - part.Texture = NULL; - part.Translation = NULL; - } - else if (sc.Compare("Graphic")) - { - TexPart part; - ParsePatch(sc, part, bSilent, TEX_MiscPatch); - if (part.Texture != NULL) parts.Push(part); - part.Texture = NULL; - part.Translation = NULL; - } - else if (sc.Compare("Offset")) - { - sc.MustGetNumber(); - LeftOffset = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - TopOffset = sc.Number; - } - else - { - sc.ScriptError("Unknown texture property '%s'", sc.String); - } - } - - NumParts = parts.Size(); - Parts = new TexPart[NumParts]; - memcpy(Parts, &parts[0], NumParts * sizeof(*Parts)); - - //CalcBitSize (); - - // If this texture is just a wrapper around a single patch, we can simply - // forward GetPixels() and GetColumn() calls to that patch. - if (NumParts == 1) - { - if (Parts->OriginX == 0 && Parts->OriginY == 0 && - Parts->Texture->GetWidth() == Width && - Parts->Texture->GetHeight() == Height && - Parts->Rotate == 0 && - !bComplex) - { - bRedirect = true; - } - } - } - - if (Width <= 0 || Height <= 0) - { - UseType = FTexture::TEX_Null; - Printf("Texture %s has invalid dimensions (%d, %d)\n", Name, Width, Height); - Width = Height = 1; - } - CalcBitSize (); - - - sc.SetCMode(false); -} - - - -void FTextureManager::ParseXTexture(FScanner &sc, int usetype) -{ - FTexture *tex = new FMultiPatchTexture(sc, usetype); - TexMan.AddTexture (tex); -} + WORD Flags; // [RH] Was unused + BYTE ScaleX; // [RH] Scaling (8 is normal) + BYTE ScaleY; // [RH] Same as above + SWORD width; + SWORD height; + BYTE columndirectory[4]; // OBSOLETE + SWORD patchcount; + mappatch_t patches[1]; +}; + +#define MAPTEXF_WORLDPANNING 0x8000 + +// Strife uses versions of the above structures that remove all unused fields + +struct strifemappatch_t +{ + SWORD originx; + SWORD originy; + SWORD patch; +}; + +// +// A wall texture is a list of patches which are to be combined in a +// predefined order. +// +struct strifemaptexture_t +{ + BYTE name[8]; + WORD Flags; // [RH] Was unused + BYTE ScaleX; // [RH] Scaling (8 is normal) + BYTE ScaleY; // [RH] Same as above + SWORD width; + SWORD height; + SWORD patchcount; + strifemappatch_t patches[1]; +}; + + +//========================================================================== +// +// In-memory representation of a single PNAMES lump entry +// +//========================================================================== + +struct FPatchLookup +{ + FString Name; + FTexture *Texture; +}; + + +//========================================================================== +// +// A texture defined in a TEXTURE1 or TEXTURE2 lump +// +//========================================================================== + +class FMultiPatchTexture : public FTexture +{ +public: + FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflump); + FMultiPatchTexture (FScanner &sc, int usetype); + ~FMultiPatchTexture (); + + const BYTE *GetColumn (unsigned int column, const Span **spans_out); + const BYTE *GetPixels (); + FTextureFormat GetFormat(); + bool UseBasePalette() ; + void Unload (); + virtual void SetFrontSkyLayer (); + + int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf = NULL); + int GetSourceLump() { return DefinitionLump; } + FTexture *GetRedirect(bool wantwarped); + FTexture *GetRawTexture(); + +protected: + BYTE *Pixels; + Span **Spans; + int DefinitionLump; + + struct TexPart + { + SWORD OriginX, OriginY; + BYTE Rotate; + BYTE op; + FRemapTable *Translation; + PalEntry Blend; + FTexture *Texture; + fixed_t Alpha; + + TexPart(); + }; + + int NumParts; + TexPart *Parts; + bool bRedirect:1; + bool bTranslucentPatches:1; + + void MakeTexture (); + +private: + void CheckForHacks (); + void ParsePatch(FScanner &sc, TexPart & part, bool silent, int usetype); +}; + +//========================================================================== +// +// FMultiPatchTexture :: FMultiPatchTexture +// +//========================================================================== + +FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflumpnum) +: Pixels (0), Spans(0), Parts(0), bRedirect(false), bTranslucentPatches(false) +{ + union + { + const maptexture_t *d; + const strifemaptexture_t *s; + } + mtexture; + + union + { + const mappatch_t *d; + const strifemappatch_t *s; + } + mpatch; + + int i; + + mtexture.d = (const maptexture_t *)texdef; + bMultiPatch = true; + + if (strife) + { + NumParts = SAFESHORT(mtexture.s->patchcount); + } + else + { + NumParts = SAFESHORT(mtexture.d->patchcount); + } + + if (NumParts < 0) + { + I_FatalError ("Bad texture directory"); + } + + UseType = FTexture::TEX_Wall; + Parts = NumParts > 0 ? new TexPart[NumParts] : NULL; + Width = SAFESHORT(mtexture.d->width); + Height = SAFESHORT(mtexture.d->height); + Name = (char *)mtexture.d->name; + CalcBitSize (); + + xScale = mtexture.d->ScaleX ? mtexture.d->ScaleX*(FRACUNIT/8) : FRACUNIT; + yScale = mtexture.d->ScaleY ? mtexture.d->ScaleY*(FRACUNIT/8) : FRACUNIT; + + if (mtexture.d->Flags & MAPTEXF_WORLDPANNING) + { + bWorldPanning = true; + } + + if (strife) + { + mpatch.s = &mtexture.s->patches[0]; + } + else + { + mpatch.d = &mtexture.d->patches[0]; + } + + for (i = 0; i < NumParts; ++i) + { + if (unsigned(LittleShort(mpatch.d->patch)) >= unsigned(maxpatchnum)) + { + I_FatalError ("Bad PNAMES and/or texture directory:\n\nPNAMES has %d entries, but\n%s wants to use entry %d.", + maxpatchnum, Name, LittleShort(mpatch.d->patch)+1); + } + Parts[i].OriginX = LittleShort(mpatch.d->originx); + Parts[i].OriginY = LittleShort(mpatch.d->originy); + Parts[i].Texture = patchlookup[LittleShort(mpatch.d->patch)].Texture; + if (Parts[i].Texture == NULL) + { + Printf (TEXTCOLOR_RED "Unknown patch %s in texture %s\n", patchlookup[LittleShort(mpatch.d->patch)].Name, Name); + NumParts--; + i--; + } + else + { + Parts[i].Texture->bKeepAround = true; + } + if (strife) + mpatch.s++; + else + mpatch.d++; + } + if (NumParts == 0) + { + Printf ("Texture %s is left without any patches\n", Name); + } + + CheckForHacks (); + + // If this texture is just a wrapper around a single patch, we can simply + // forward GetPixels() and GetColumn() calls to that patch. + if (NumParts == 1) + { + if (Parts->OriginX == 0 && Parts->OriginY == 0 && + Parts->Texture->GetWidth() == Width && + Parts->Texture->GetHeight() == Height) + { + bRedirect = true; + } + } + DefinitionLump = deflumpnum; +} + +//========================================================================== +// +// FMultiPatchTexture :: ~FMultiPatchTexture +// +//========================================================================== + +FMultiPatchTexture::~FMultiPatchTexture () +{ + Unload (); + if (Parts != NULL) + { + for(int i=0; iSetFrontSkyLayer (); + } + bNoRemap0 = true; +} + +//========================================================================== +// +// FMultiPatchTexture :: Unload +// +//========================================================================== + +void FMultiPatchTexture::Unload () +{ + if (Pixels != NULL) + { + delete[] Pixels; + Pixels = NULL; + } +} + +//========================================================================== +// +// FMultiPatchTexture :: GetPixels +// +//========================================================================== + +const BYTE *FMultiPatchTexture::GetPixels () +{ + if (bRedirect) + { + return Parts->Texture->GetPixels (); + } + if (Pixels == NULL) + { + MakeTexture (); + } + return Pixels; +} + +//========================================================================== +// +// FMultiPatchTexture :: GetColumn +// +//========================================================================== + +const BYTE *FMultiPatchTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + if (bRedirect) + { + return Parts->Texture->GetColumn (column, spans_out); + } + if (Pixels == NULL) + { + MakeTexture (); + } + if ((unsigned)column >= (unsigned)Width) + { + if (WidthMask + 1 == Width) + { + column &= WidthMask; + } + else + { + column %= Width; + } + } + if (spans_out != NULL) + { + if (Spans == NULL) + { + Spans = CreateSpans (Pixels); + } + *spans_out = Spans[column]; + } + return Pixels + column*Height; +} + + +//========================================================================== +// +// GetBlendMap +// +//========================================================================== + +BYTE *GetBlendMap(PalEntry blend, BYTE *blendwork) +{ + + switch (blend.a==0 ? int(blend) : -1) + { + case BLEND_ICEMAP: + return TranslationToTable(TRANSLATION(TRANSLATION_Standard, 7))->Remap; + + default: + if (blend >= BLEND_SPECIALCOLORMAP1 && blend < BLEND_SPECIALCOLORMAP1 + SpecialColormaps.Size()) + { + return SpecialColormaps[blend - BLEND_SPECIALCOLORMAP1].Colormap; + } + else if (blend >= BLEND_DESATURATE1 && blend <= BLEND_DESATURATE31) + { + return DesaturateColormap[blend - BLEND_DESATURATE1]; + } + else + { + blendwork[0]=0; + if (blend.a == 255) + { + for(int i=1;i<256;i++) + { + int rr = (blend.r * GPalette.BaseColors[i].r) / 255; + int gg = (blend.g * GPalette.BaseColors[i].g) / 255; + int bb = (blend.b * GPalette.BaseColors[i].b) / 255; + + blendwork[i] = ColorMatcher.Pick(rr, gg, bb); + } + return blendwork; + } + else if (blend.a != 0) + { + for(int i=1;i<256;i++) + { + int rr = (blend.r * blend.a + GPalette.BaseColors[i].r * (255-blend.a)) / 255; + int gg = (blend.g * blend.a + GPalette.BaseColors[i].g * (255-blend.a)) / 255; + int bb = (blend.b * blend.a + GPalette.BaseColors[i].b * (255-blend.a)) / 255; + + blendwork[i] = ColorMatcher.Pick(rr, gg, bb); + } + return blendwork; + } + } + } + return NULL; +} + +//========================================================================== +// +// FMultiPatchTexture :: MakeTexture +// +//========================================================================== + +void FMultiPatchTexture::MakeTexture () +{ + // Add a little extra space at the end if the texture's height is not + // a power of 2, in case somebody accidentally makes it repeat vertically. + int numpix = Width * Height + (1 << HeightBits) - Height; + BYTE blendwork[256]; + bool hasTranslucent = false; + + Pixels = new BYTE[numpix]; + memset (Pixels, 0, numpix); + + for (int i = 0; i < NumParts; ++i) + { + if (Parts[i].op != OP_COPY) + { + hasTranslucent = true; + } + } + + if (!hasTranslucent) + { + for (int i = 0; i < NumParts; ++i) + { + if (Parts[i].Texture->bHasCanvas) continue; // cannot use camera textures as patch. + + BYTE *trans = Parts[i].Translation ? Parts[i].Translation->Remap : NULL; + { + if (Parts[i].Blend != 0) + { + trans = GetBlendMap(Parts[i].Blend, blendwork); + } + Parts[i].Texture->CopyToBlock (Pixels, Width, Height, + Parts[i].OriginX, Parts[i].OriginY, Parts[i].Rotate, trans); + } + } + } + else + { + // In case there are translucent patches let's do the composition in + // True color to keep as much precision as possible before downconverting to the palette. + BYTE *buffer = new BYTE[Width * Height * 4]; + memset(buffer, 0, Width * Height * 4); + FillBuffer(buffer, Width * 4, Height, TEX_RGB); + for(int y = 0; y < Height; y++) + { + BYTE *in = buffer + Width * y * 4; + BYTE *out = Pixels + y; + for (int x = 0; x < Width; x++) + { + if (*out == 0 && in[3] != 0) + { + *out = RGB32k[in[2]>>3][in[1]>>3][in[0]>>3]; + } + out += Height; + in += 4; + } + } + delete [] buffer; + } +} + +//=========================================================================== +// +// FMultipatchTexture::CopyTrueColorPixels +// +// Preserves the palettes of each individual patch +// +//=========================================================================== + +int FMultiPatchTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) +{ + int retv = -1; + + if (bRedirect) + { // Redirect straight to the real texture's routine. + return Parts[0].Texture->CopyTrueColorPixels(bmp, x, y, rotate, inf); + } + + if (rotate != 0 || (inf != NULL && ((inf->op != OP_OVERWRITE && inf->op != OP_COPY) || inf->blend != BLEND_NONE))) + { // We are doing some sort of fancy stuff to the destination bitmap, so composite to + // a temporary bitmap, and copy that. + FBitmap tbmp; + if (tbmp.Create(Width, Height)) + { + retv = MAX(retv, CopyTrueColorPixels(&tbmp, 0, 0, 0)); + bmp->CopyPixelDataRGB(x, y, tbmp.GetPixels(), Width, Height, + 4, tbmp.GetPitch(), rotate, CF_BGRA, inf); + } + return retv; + } + + // When compositing a multipatch texture with multipatch parts, + // drawing must be restricted to the actual area which is covered by this texture. + FClipRect saved_cr = bmp->GetClipRect(); + bmp->IntersectClipRect(x, y, Width, Height); + + if (inf != NULL && inf->op == OP_OVERWRITE) + { + bmp->Zero(); + } + + for(int i = 0; i < NumParts; i++) + { + int ret = -1; + FCopyInfo info; + + if (Parts[i].Texture->bHasCanvas) continue; // cannot use camera textures as patch. + + memset (&info, 0, sizeof(info)); + info.alpha = Parts[i].Alpha; + info.invalpha = FRACUNIT - info.alpha; + info.op = ECopyOp(Parts[i].op); + PalEntry b = Parts[i].Blend; + if (b.a == 0 && b != BLEND_NONE) + { + info.blend = EBlend(b.d); + } + else if (b.a != 0) + { + if (b.a == 255) + { + info.blendcolor[0] = b.r * FRACUNIT / 255; + info.blendcolor[1] = b.g * FRACUNIT / 255; + info.blendcolor[2] = b.b * FRACUNIT / 255; + info.blend = BLEND_MODULATE; + } + else + { + fixed_t blendalpha = b.a * FRACUNIT / 255; + info.blendcolor[0] = b.r * blendalpha; + info.blendcolor[1] = b.g * blendalpha; + info.blendcolor[2] = b.b * blendalpha; + info.blendcolor[3] = FRACUNIT - blendalpha; + info.blend = BLEND_OVERLAY; + } + } + + if (Parts[i].Translation != NULL) + { // Using a translation forces downconversion to the base palette + ret = Parts[i].Texture->CopyTrueColorTranslated(bmp, x+Parts[i].OriginX, y+Parts[i].OriginY, Parts[i].Rotate, Parts[i].Translation, &info); + } + else + { + ret = Parts[i].Texture->CopyTrueColorPixels(bmp, x+Parts[i].OriginX, y+Parts[i].OriginY, Parts[i].Rotate, &info); + } + + if (ret > retv) retv = ret; + } + // Restore previous clipping rectangle. + bmp->SetClipRect(saved_cr); + return retv; +} + +//========================================================================== +// +// FMultiPatchTexture :: GetFormat +// +// only returns 'paletted' if all patches use the base palette. +// +//========================================================================== + +FTextureFormat FMultiPatchTexture::GetFormat() +{ + if (bComplex) return TEX_RGB; + if (NumParts == 1) return Parts[0].Texture->GetFormat(); + return UseBasePalette() ? TEX_Pal : TEX_RGB; +} + + +//=========================================================================== +// +// FMultipatchTexture::UseBasePalette +// +// returns true if all patches in the texture use the unmodified base +// palette. +// +//=========================================================================== + +bool FMultiPatchTexture::UseBasePalette() +{ + if (bComplex) return false; + for(int i=0;iUseBasePalette()) return false; + } + return true; +} + +//========================================================================== +// +// FMultiPatchTexture :: CheckForHacks +// +//========================================================================== + +void FMultiPatchTexture::CheckForHacks () +{ + if (NumParts <= 0) + { + return; + } + + // Heretic sky textures are marked as only 128 pixels tall, + // even though they are really 200 pixels tall. + if (gameinfo.gametype == GAME_Heretic && + Name[0] == 'S' && + Name[1] == 'K' && + Name[2] == 'Y' && + Name[4] == 0 && + Name[3] >= '1' && + Name[3] <= '3' && + Height == 128) + { + Height = 200; + HeightBits = 8; + return; + } + + // The Doom E1 sky has its patch's y offset at -8 instead of 0. + if (gameinfo.gametype == GAME_Doom && + !(gameinfo.flags & GI_MAPxx) && + NumParts == 1 && + Height == 128 && + Parts->OriginY == -8 && + Name[0] == 'S' && + Name[1] == 'K' && + Name[2] == 'Y' && + Name[3] == '1' && + Name[4] == 0) + { + Parts->OriginY = 0; + return; + } + + // BIGDOOR7 in Doom also has patches at y offset -4 instead of 0. + if (gameinfo.gametype == GAME_Doom && + !(gameinfo.flags & GI_MAPxx) && + NumParts == 2 && + Height == 128 && + Parts[0].OriginY == -4 && + Parts[1].OriginY == -4 && + Name[0] == 'B' && + Name[1] == 'I' && + Name[2] == 'G' && + Name[3] == 'D' && + Name[4] == 'O' && + Name[5] == 'O' && + Name[6] == 'R' && + Name[7] == '7') + { + Parts[0].OriginY = 0; + Parts[1].OriginY = 0; + return; + } + + // [RH] Some wads (I forget which!) have single-patch textures 256 + // pixels tall that have patch lengths recorded as 0. I can't think of + // any good reason for them to do this, and since I didn't make note + // of which wad made me hack in support for them, the hack is gone + // because I've added support for DeePsea's true tall patches. + // + // Okay, I found a wad with crap patches: Pleiades.wad's sky patches almost + // fit this description and are a big mess, but they're not single patch! + if (Height == 256) + { + int i; + + // All patches must be at the top of the texture for this fix + for (i = 0; i < NumParts; ++i) + { + if (Parts[i].OriginY != 0) + { + break; + } + } + + if (i == NumParts) + { + for (i = 0; i < NumParts; ++i) + { + Parts[i].Texture->HackHack(256); + } + } + } +} + +//========================================================================== +// +// FMultiPatchTexture :: GetRedirect +// +//========================================================================== + +FTexture *FMultiPatchTexture::GetRedirect(bool wantwarped) +{ + return bRedirect ? Parts->Texture : this; +} + +//========================================================================== +// +// FMultiPatchTexture :: GetRawTexture +// +// Doom ignored all compositing of mid-sided textures on two-sided lines. +// Since these textures had to be single-patch in Doom, that essentially +// means it ignores their Y offsets. +// +// If this texture is composed of only one patch, return that patch. +// Otherwise, return this texture, since Doom wouldn't have been able to +// draw it anyway. +// +//========================================================================== + +FTexture *FMultiPatchTexture::GetRawTexture() +{ + return NumParts == 1 ? Parts->Texture : this; +} + +//========================================================================== +// +// FMultiPatchTexture :: TexPart :: TexPart +// +//========================================================================== + +FMultiPatchTexture::TexPart::TexPart() +{ + OriginX = OriginY = 0; + Rotate = 0; + Texture = NULL; + Translation = NULL; + Blend = 0; + Alpha = FRACUNIT; + op = OP_COPY; +} + +//========================================================================== +// +// FTextureManager :: AddTexturesLump +// +//========================================================================== + +void FTextureManager::AddTexturesLump (const void *lumpdata, int lumpsize, int deflumpnum, int patcheslump, int firstdup, bool texture1) +{ + FPatchLookup *patchlookup = NULL; + int i; + DWORD numpatches; + + if (firstdup == 0) + { + firstdup = (int)Textures.Size(); + } + + { + FWadLump pnames = Wads.OpenLumpNum (patcheslump); + + pnames >> numpatches; + + // Check whether the amount of names reported is correct. + if ((signed)numpatches < 0) + { + Printf("Corrupt PNAMES lump found (negative amount of entries reported)"); + return; + } + + // Check whether the amount of names reported is correct. + int lumplength = Wads.LumpLength(patcheslump); + if (numpatches > DWORD((lumplength-4)/8)) + { + Printf("PNAMES lump is shorter than required (%u entries reported but only %d bytes (%d entries) long\n", + numpatches, lumplength, (lumplength-4)/8); + // Truncate but continue reading. Who knows how many such lumps exist? + numpatches = (lumplength-4)/8; + } + + // Catalog the patches these textures use so we know which + // textures they represent. + patchlookup = new FPatchLookup[numpatches]; + for (DWORD i = 0; i < numpatches; ++i) + { + char pname[9]; + pnames.Read (pname, 8); + pname[8] = '\0'; + patchlookup[i].Name = pname; + FTextureID j = CheckForTexture (patchlookup[i].Name, FTexture::TEX_WallPatch); + if (j.isValid()) + { + patchlookup[i].Texture = Textures[j.GetIndex()].Texture; + } + else + { + // Shareware Doom has the same PNAMES lump as the registered + // Doom, so printing warnings for patches that don't really + // exist isn't such a good idea. + //Printf ("Patch %s not found.\n", patchlookup[i].Name); + patchlookup[i].Texture = NULL; + } + } + } + + bool isStrife = false; + const DWORD *maptex, *directory; + DWORD maxoff; + int numtextures; + DWORD offset = 0; // Shut up, GCC! + + maptex = (const DWORD *)lumpdata; + numtextures = LittleLong(*maptex); + maxoff = lumpsize; + + if (maxoff < DWORD(numtextures+1)*4) + { + Printf ("Texture directory is too short"); + delete[] patchlookup; + return; + } + + // Scan the texture lump to decide if it contains Doom or Strife textures + for (i = 0, directory = maptex+1; i < numtextures; ++i) + { + offset = LittleLong(directory[i]); + if (offset > maxoff) + { + Printf ("Bad texture directory"); + delete[] patchlookup; + return; + } + + maptexture_t *tex = (maptexture_t *)((BYTE *)maptex + offset); + + // There is bizzarely a Doom editing tool that writes to the + // first two elements of columndirectory, so I can't check those. + if (SAFESHORT(tex->patchcount) < 0 || + tex->columndirectory[2] != 0 || + tex->columndirectory[3] != 0) + { + isStrife = true; + break; + } + } + + + // Textures defined earlier in the lump take precedence over those defined later, + // but later TEXTUREx lumps take precedence over earlier ones. + for (i = 1, directory = maptex; i <= numtextures; ++i) + { + if (i == 1 && texture1) + { + // The very first texture is just a dummy. Copy its dimensions to texture 0. + // It still needs to be created in case someone uses it by name. + offset = LittleLong(directory[1]); + const maptexture_t *tex = (const maptexture_t *)((const BYTE *)maptex + offset); + FDummyTexture *tex0 = static_cast(Textures[0].Texture); + tex0->SetSize (SAFESHORT(tex->width), SAFESHORT(tex->height)); + } + + offset = LittleLong(directory[i]); + if (offset > maxoff) + { + Printf ("Bad texture directory"); + delete[] patchlookup; + return; + } + + // If this texture was defined already in this lump, skip it + // This could cause problems with animations that use the same name for intermediate + // textures. Should I be worried? + int j; + for (j = (int)Textures.Size() - 1; j >= firstdup; --j) + { + if (strnicmp (Textures[j].Texture->Name, (const char *)maptex + offset, 8) == 0) + break; + } + if (j + 1 == firstdup) + { + FMultiPatchTexture *tex = new FMultiPatchTexture ((const BYTE *)maptex + offset, patchlookup, numpatches, isStrife, deflumpnum); + if (i == 1 && texture1) + { + tex->UseType = FTexture::TEX_FirstDefined; + } + TexMan.AddTexture (tex); + StartScreen->Progress(); + } + } + delete[] patchlookup; +} + + +//========================================================================== +// +// FTextureManager :: AddTexturesLumps +// +//========================================================================== + +void FTextureManager::AddTexturesLumps (int lump1, int lump2, int patcheslump) +{ + int firstdup = (int)Textures.Size(); + + if (lump1 >= 0) + { + FMemLump texdir = Wads.ReadLump (lump1); + AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump1), lump1, patcheslump, firstdup, true); + } + if (lump2 >= 0) + { + FMemLump texdir = Wads.ReadLump (lump2); + AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump2), lump2, patcheslump, firstdup, false); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part, bool silent, int usetype) +{ + FString patchname; + sc.MustGetString(); + + FTextureID texno = TexMan.CheckForTexture(sc.String, usetype); + int Mirror = 0; + + if (!texno.isValid()) + { + if (strlen(sc.String) <= 8 && !strpbrk(sc.String, "./")) + { + int lumpnum = Wads.CheckNumForName(sc.String, usetype == TEX_MiscPatch? ns_graphics : ns_patches); + if (lumpnum >= 0) + { + part.Texture = FTexture::CreateTexture(lumpnum, usetype); + TexMan.AddTexture(part.Texture); + } + } + } + else + { + part.Texture = TexMan[texno]; + bComplex |= part.Texture->bComplex; + } + if (part.Texture == NULL) + { + if (!silent) Printf(TEXTCOLOR_RED "Unknown patch '%s' in texture '%s'\n", sc.String, Name); + } + sc.MustGetStringName(","); + sc.MustGetNumber(); + part.OriginX = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + part.OriginY = sc.Number; + + if (sc.CheckString("{")) + { + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("flipx")) + { + Mirror |= 1; + } + else if (sc.Compare("flipy")) + { + Mirror |= 2; + } + else if (sc.Compare("rotate")) + { + sc.MustGetNumber(); + sc.Number = (((sc.Number + 90)%360)-90); + if (sc.Number != 0 && sc.Number !=90 && sc.Number != 180 && sc.Number != -90) + { + sc.ScriptError("Rotation must be a multiple of 90 degrees."); + } + part.Rotate = (sc.Number / 90) & 3; + } + else if (sc.Compare("Translation")) + { + int match; + + bComplex = true; + if (part.Translation != NULL) delete part.Translation; + part.Translation = NULL; + part.Blend = 0; + static const char *maps[] = { "inverse", "gold", "red", "green", "blue", NULL }; + sc.MustGetString(); + + match = sc.MatchString(maps); + if (match >= 0) + { + part.Blend = BLEND_SPECIALCOLORMAP1 + match; + } + else if (sc.Compare("ICE")) + { + part.Blend = BLEND_ICEMAP; + } + else if (sc.Compare("DESATURATE")) + { + sc.MustGetStringName(","); + sc.MustGetNumber(); + part.Blend = BLEND_DESATURATE1 + clamp(sc.Number-1, 0, 30); + } + else + { + sc.UnGet(); + part.Translation = new FRemapTable; + part.Translation->MakeIdentity(); + do + { + sc.MustGetString(); + part.Translation->AddToTranslation(sc.String); + } + while (sc.CheckString(",")); + } + + } + else if (sc.Compare("Colormap")) + { + float r1,g1,b1; + float r2,g2,b2; + + sc.MustGetFloat(); + r1 = (float)sc.Float; + sc.MustGetStringName(","); + sc.MustGetFloat(); + g1 = (float)sc.Float; + sc.MustGetStringName(","); + sc.MustGetFloat(); + b1 = (float)sc.Float; + if (!sc.CheckString(",")) + { + part.Blend = AddSpecialColormap(0,0,0, r1, g1, b1); + } + else + { + sc.MustGetFloat(); + r2 = (float)sc.Float; + sc.MustGetStringName(","); + sc.MustGetFloat(); + g2 = (float)sc.Float; + sc.MustGetStringName(","); + sc.MustGetFloat(); + b2 = (float)sc.Float; + part.Blend = AddSpecialColormap(r1, g1, b1, r2, g2, b2); + } + } + else if (sc.Compare("Blend")) + { + bComplex = true; + if (part.Translation != NULL) delete part.Translation; + part.Translation = NULL; + part.Blend = 0; + + if (!sc.CheckNumber()) + { + sc.MustGetString(); + part.Blend = V_GetColor(NULL, sc.String); + } + else + { + int r,g,b; + + r = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + g = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + b = sc.Number; + sc.MustGetStringName(","); + part.Blend = MAKERGB(r, g, b); + } + // Blend.a may never be 0 here. + if (sc.CheckString(",")) + { + sc.MustGetFloat(); + if (sc.Float > 0.f) + part.Blend.a = clamp(int(sc.Float*255), 1, 254); + else + part.Blend = 0; + } + else part.Blend.a = 255; + } + else if (sc.Compare("alpha")) + { + sc.MustGetFloat(); + part.Alpha = clamp(FLOAT2FIXED(sc.Float), 0, FRACUNIT); + // bComplex is not set because it is only needed when the style is not OP_COPY. + } + else if (sc.Compare("style")) + { + static const char *styles[] = {"copy", "translucent", "add", "subtract", "reversesubtract", "modulate", "copyalpha", "copynewalpha", "overlay", NULL }; + sc.MustGetString(); + part.op = sc.MustMatchString(styles); + bComplex |= (part.op != OP_COPY); + bTranslucentPatches = bComplex; + } + else if (sc.Compare("useoffsets")) + { + if (part.Texture != NULL) + { + part.OriginX -= part.Texture->LeftOffset; + part.OriginY -= part.Texture->TopOffset; + } + } + } + } + if (Mirror & 2) + { + part.Rotate = (part.Rotate + 2) & 3; + Mirror ^= 1; + } + if (Mirror & 1) + { + part.Rotate |= 4; + } +} + +//========================================================================== +// +// Constructor for text based multipatch definitions +// +//========================================================================== + +FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) +: Pixels (0), Spans(0), Parts(0), bRedirect(false), bTranslucentPatches(false) +{ + TArray parts; + bool bSilent = false; + + bMultiPatch = true; + sc.SetCMode(true); + sc.MustGetString(); + const char* textureName = NULL; + if (sc.Compare("optional")) + { + bSilent = true; + sc.MustGetString(); + if (sc.Compare(",")) + { + // this is not right. Apparently a texture named 'optional' is being defined right now... + sc.UnGet(); + textureName = "optional"; + bSilent = false; + } + } + Name = !textureName ? sc.String : textureName; + Name.ToUpper(); + sc.MustGetStringName(","); + sc.MustGetNumber(); + Width = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + Height = sc.Number; + UseType = usetype; + + if (sc.CheckString("{")) + { + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("XScale")) + { + sc.MustGetFloat(); + xScale = FLOAT2FIXED(sc.Float); + if (xScale == 0) sc.ScriptError("Texture %s is defined with null x-scale\n", Name); + } + else if (sc.Compare("YScale")) + { + sc.MustGetFloat(); + yScale = FLOAT2FIXED(sc.Float); + if (yScale == 0) sc.ScriptError("Texture %s is defined with null y-scale\n", Name); + } + else if (sc.Compare("WorldPanning")) + { + bWorldPanning = true; + } + else if (sc.Compare("NullTexture")) + { + UseType = FTexture::TEX_Null; + } + else if (sc.Compare("NoDecals")) + { + bNoDecals = true; + } + else if (sc.Compare("Patch")) + { + TexPart part; + ParsePatch(sc, part, bSilent, TEX_WallPatch); + if (part.Texture != NULL) parts.Push(part); + part.Texture = NULL; + part.Translation = NULL; + } + else if (sc.Compare("Graphic")) + { + TexPart part; + ParsePatch(sc, part, bSilent, TEX_MiscPatch); + if (part.Texture != NULL) parts.Push(part); + part.Texture = NULL; + part.Translation = NULL; + } + else if (sc.Compare("Offset")) + { + sc.MustGetNumber(); + LeftOffset = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + TopOffset = sc.Number; + } + else + { + sc.ScriptError("Unknown texture property '%s'", sc.String); + } + } + + NumParts = parts.Size(); + Parts = new TexPart[NumParts]; + memcpy(Parts, &parts[0], NumParts * sizeof(*Parts)); + + //CalcBitSize (); + + // If this texture is just a wrapper around a single patch, we can simply + // forward GetPixels() and GetColumn() calls to that patch. + if (NumParts == 1) + { + if (Parts->OriginX == 0 && Parts->OriginY == 0 && + Parts->Texture->GetWidth() == Width && + Parts->Texture->GetHeight() == Height && + Parts->Rotate == 0 && + !bComplex) + { + bRedirect = true; + } + } + } + + if (Width <= 0 || Height <= 0) + { + UseType = FTexture::TEX_Null; + Printf("Texture %s has invalid dimensions (%d, %d)\n", Name, Width, Height); + Width = Height = 1; + } + CalcBitSize (); + + + sc.SetCMode(false); +} + + + +void FTextureManager::ParseXTexture(FScanner &sc, int usetype) +{ + FTexture *tex = new FMultiPatchTexture(sc, usetype); + TexMan.AddTexture (tex); +} diff --git a/src/textures/texture.cpp b/src/textures/texture.cpp index f41f1902dc..6ee6ccb0ac 100644 --- a/src/textures/texture.cpp +++ b/src/textures/texture.cpp @@ -137,7 +137,10 @@ FTexture * FTexture::CreateTexture (int lumpnum, int usetype) FTexture * FTexture::CreateTexture (const char *name, int lumpnum, int usetype) { FTexture *tex = CreateTexture(lumpnum, usetype); - if (tex != NULL && name != NULL) uppercopy(tex->Name, name); + if (tex != NULL && name != NULL) { + tex->Name = name; + tex->Name.ToUpper(); + } return tex; } @@ -152,16 +155,16 @@ FTexture::FTexture (const char *name, int lumpnum) id.SetInvalid(); if (name != NULL) { - uppercopy(Name, name); + Name = name; + Name.ToUpper(); } else if (lumpnum < 0) { - *Name = 0; + Name = FString(); } else { Wads.GetLumpName (Name, lumpnum); - Name[8] = 0; } } @@ -574,7 +577,6 @@ FDummyTexture::FDummyTexture () HeightBits = 6; WidthBits = 6; WidthMask = 63; - Name[0] = 0; UseType = TEX_Null; } diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 6fb963e42b..2a10ce0df9 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -373,7 +373,7 @@ FTextureID FTextureManager::AddTexture (FTexture *texture) // Later textures take precedence over earlier ones // Textures without name can't be looked for - if (texture->Name[0] != 0) + if (texture->Name[0] != '\0') { bucket = int(MakeKey (texture->Name) % HASH_SIZE); hash = HashFirst[bucket]; @@ -429,7 +429,7 @@ void FTextureManager::ReplaceTexture (FTextureID picnum, FTexture *newtexture, b FTexture *oldtexture = Textures[index].Texture; - strcpy (newtexture->Name, oldtexture->Name); + newtexture->Name = oldtexture->Name; newtexture->UseType = oldtexture->UseType; Textures[index].Texture = newtexture; @@ -488,9 +488,7 @@ void FTextureManager::AddGroup(int wadnum, int ns, int usetype) { int firsttx = Wads.GetFirstLump(wadnum); int lasttx = Wads.GetLastLump(wadnum); - char name[9]; - - name[8] = 0; + FString Name; // Go from first to last so that ANIMDEFS work as expected. However, // to avoid duplicates (and to keep earlier entries from overriding @@ -501,9 +499,9 @@ void FTextureManager::AddGroup(int wadnum, int ns, int usetype) { if (Wads.GetLumpNamespace(firsttx) == ns) { - Wads.GetLumpName (name, firsttx); + Wads.GetLumpName (Name, firsttx); - if (Wads.CheckNumForName (name, ns) == firsttx) + if (Wads.CheckNumForName (Name, ns) == firsttx) { CreateTexture (firsttx, usetype); } @@ -511,7 +509,7 @@ void FTextureManager::AddGroup(int wadnum, int ns, int usetype) } else if (ns == ns_flats && Wads.GetLumpFlags(firsttx) & LUMPF_MAYBEFLAT) { - if (Wads.CheckNumForName (name, ns) < firsttx) + if (Wads.CheckNumForName (Name, ns) < firsttx) { CreateTexture (firsttx, usetype); } @@ -531,7 +529,7 @@ void FTextureManager::AddHiresTextures (int wadnum) int firsttx = Wads.GetFirstLump(wadnum); int lasttx = Wads.GetLastLump(wadnum); - char name[9]; + FString Name; TArray tlist; if (firsttx == -1 || lasttx == -1) @@ -539,18 +537,16 @@ void FTextureManager::AddHiresTextures (int wadnum) return; } - name[8] = 0; - for (;firsttx <= lasttx; ++firsttx) { if (Wads.GetLumpNamespace(firsttx) == ns_hires) { - Wads.GetLumpName (name, firsttx); + Wads.GetLumpName (Name, firsttx); - if (Wads.CheckNumForName (name, ns_hires) == firsttx) + if (Wads.CheckNumForName (Name, ns_hires) == firsttx) { tlist.Clear(); - int amount = ListTextures(name, tlist); + int amount = ListTextures(Name, tlist); if (amount == 0) { // A texture with this name does not yet exist @@ -594,14 +590,13 @@ void FTextureManager::AddHiresTextures (int wadnum) void FTextureManager::LoadTextureDefs(int wadnum, const char *lumpname) { int remapLump, lastLump; - char src[9]; + FString src; bool is32bit; int width, height; int type, mode; TArray tlist; lastLump = 0; - src[8] = '\0'; while ((remapLump = Wads.FindLump(lumpname, &lastLump)) != -1) { @@ -678,7 +673,7 @@ void FTextureManager::LoadTextureDefs(int wadnum, const char *lumpname) FString base = ExtractFileBase(sc.String, false); if (!base.IsEmpty()) { - strncpy(src, base, 8); + src = base.Left(8); int lumpnum = Wads.CheckNumForFullName(sc.String, true, ns_patches); if (lumpnum == -1) lumpnum = Wads.CheckNumForFullName(sc.String, true, ns_graphics); @@ -701,7 +696,7 @@ void FTextureManager::LoadTextureDefs(int wadnum, const char *lumpname) // Replace the entire texture and adjust the scaling and offset factors. newtex->bWorldPanning = true; newtex->SetScaledSize(width, height); - memcpy(newtex->Name, src, sizeof(newtex->Name)); + newtex->Name = src; FTextureID oldtex = TexMan.CheckForTexture(src, FTexture::TEX_MiscPatch); if (oldtex.isValid()) @@ -757,7 +752,7 @@ void FTextureManager::AddPatches (int lumpnum) char name[9]; *file >> numpatches; - name[8] = 0; + name[8] = '\0'; for (i = 0; i < numpatches; ++i) { @@ -839,9 +834,8 @@ void FTextureManager::AddTexturesForWad(int wadnum) for (int i= firsttx; i <= lasttx; i++) { bool skin = false; - char name[9]; - Wads.GetLumpName(name, i); - name[8]=0; + FString Name; + Wads.GetLumpName(Name, i); // Ignore anything not in the global namespace int ns = Wads.GetLumpNamespace(i); @@ -867,20 +861,20 @@ void FTextureManager::AddTexturesForWad(int wadnum) if (Wads.CheckLumpName(i, "BEHAVIOR")) continue; // Don't bother looking at this lump if something later overrides it. - if (Wads.CheckNumForName(name, ns_graphics) != i) continue; + if (Wads.CheckNumForName(Name, ns_graphics) != i) continue; // skip this if it has already been added as a wall patch. - if (CheckForTexture(name, FTexture::TEX_WallPatch, 0).Exists()) continue; + if (CheckForTexture(Name, FTexture::TEX_WallPatch, 0).Exists()) continue; } else if (ns == ns_graphics) { // Don't bother looking this lump if something later overrides it. - if (Wads.CheckNumForName(name, ns_graphics) != i) continue; + if (Wads.CheckNumForName(Name, ns_graphics) != i) continue; } else if (ns >= ns_firstskin) { // Don't bother looking this lump if something later overrides it. - if (Wads.CheckNumForName(name, ns) != i) continue; + if (Wads.CheckNumForName(Name, ns) != i) continue; skin = true; } else continue; diff --git a/src/textures/textures.h b/src/textures/textures.h index 15c9bce5c7..9dbd85a550 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -157,11 +157,7 @@ public: int SourceLump; FTextureID id; - union - { - char Name[9]; - DWORD dwName; // Used with sprites - }; + FString Name; BYTE UseType; // This texture's primary purpose BYTE bNoDecals:1; // Decals should not stick to texture diff --git a/src/textures/tgatexture.cpp b/src/textures/tgatexture.cpp index 374861699e..cff89603c0 100644 --- a/src/textures/tgatexture.cpp +++ b/src/textures/tgatexture.cpp @@ -145,7 +145,6 @@ FTGATexture::FTGATexture (int lumpnum, TGAHeader * hdr) : FTexture(NULL, lumpnum), Pixels(0), Spans(0) { Wads.GetLumpName (Name, lumpnum); - Name[8] = 0; Width = hdr->width; Height = hdr->height; // Alpha channel is used only for 32 bit RGBA and paletted images with RGBA palettes. diff --git a/src/w_wad.cpp b/src/w_wad.cpp index c351de6f8f..9455817db4 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -492,7 +492,7 @@ int FWadCollection::CheckNumForFullName (const char *name, bool trynormal, int n return -1; } - i = FirstLumpIndex_FullName[MakeKey (name) % NumLumps]; + i = FirstLumpIndex_FullName[MakeKey(name) % NumLumps]; while (i != NULL_INDEX && stricmp(name, LumpInfo[i].lump->FullName)) { @@ -1015,6 +1015,16 @@ void FWadCollection::GetLumpName (char *to, int lump) const uppercopy (to, LumpInfo[lump].lump->Name); } +void FWadCollection::GetLumpName(FString &to, int lump) const +{ + if ((size_t)lump >= NumLumps) + to = FString(); + else { + to = LumpInfo[lump].lump->Name; + to.ToUpper(); + } +} + //========================================================================== // // FWadCollection :: GetLumpFullName diff --git a/src/w_wad.h b/src/w_wad.h index 530182147e..0a95699f62 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -196,7 +196,8 @@ public: int GetLumpOffset (int lump); // [RH] Returns offset of lump in the wadfile int GetLumpFlags (int lump); // Return the flags for this lump void GetLumpName (char *to, int lump) const; // [RH] Copies the lump name to to using uppercopy - const char *GetLumpFullName (int lump) const; // [RH] Returns the lump's full name + void FWadCollection::GetLumpName(FString &to, int lump) const; + const char *GetLumpFullName(int lump) const; // [RH] Returns the lump's full name FString GetLumpFullPath (int lump) const; // [RH] Returns wad's name + lump's full name int GetLumpFile (int lump) const; // [RH] Returns wadnum for a specified lump int GetLumpNamespace (int lump) const; // [RH] Returns the namespace a lump belongs to From 582b1990b7f342601a1976c85d0ce2b25343458e Mon Sep 17 00:00:00 2001 From: Shawn Walker Date: Sun, 18 May 2014 16:00:31 -0700 Subject: [PATCH 3/7] - restore original line endings --- src/textures/multipatchtexture.cpp | 2670 ++++++++++++++-------------- 1 file changed, 1335 insertions(+), 1335 deletions(-) diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp index c51f301687..84e7385dbb 100644 --- a/src/textures/multipatchtexture.cpp +++ b/src/textures/multipatchtexture.cpp @@ -1,1336 +1,1336 @@ -/* -** multipatchtexture.cpp -** Texture class for standard Doom multipatch textures -** -**--------------------------------------------------------------------------- -** Copyright 2004-2006 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -** -*/ - -#include -#include "doomtype.h" -#include "files.h" -#include "w_wad.h" -#include "i_system.h" -#include "gi.h" -#include "st_start.h" -#include "sc_man.h" -#include "templates.h" -#include "r_data/r_translate.h" -#include "bitmap.h" -#include "colormatcher.h" -#include "v_palette.h" -#include "v_video.h" -#include "v_text.h" -#include "m_fixed.h" -#include "textures/textures.h" -#include "r_data/colormaps.h" - -// On the Alpha, accessing the shorts directly if they aren't aligned on a -// 4-byte boundary causes unaligned access warnings. Why it does this at -// all and only while initing the textures is beyond me. - -#ifdef ALPHA -#define SAFESHORT(s) ((short)(((BYTE *)&(s))[0] + ((BYTE *)&(s))[1] * 256)) -#else -#define SAFESHORT(s) LittleShort(s) -#endif - - - -//-------------------------------------------------------------------------- -// -// Data structures for the TEXTUREx lumps -// -//-------------------------------------------------------------------------- - -// -// Each texture is composed of one or more patches, with patches being lumps -// stored in the WAD. The lumps are referenced by number, and patched into -// the rectangular texture space using origin and possibly other attributes. -// -struct mappatch_t -{ - SWORD originx; - SWORD originy; - SWORD patch; - SWORD stepdir; - SWORD colormap; -}; - -// -// A wall texture is a list of patches which are to be combined in a -// predefined order. -// -struct maptexture_t -{ +/* +** multipatchtexture.cpp +** Texture class for standard Doom multipatch textures +** +**--------------------------------------------------------------------------- +** Copyright 2004-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** +*/ + +#include +#include "doomtype.h" +#include "files.h" +#include "w_wad.h" +#include "i_system.h" +#include "gi.h" +#include "st_start.h" +#include "sc_man.h" +#include "templates.h" +#include "r_data/r_translate.h" +#include "bitmap.h" +#include "colormatcher.h" +#include "v_palette.h" +#include "v_video.h" +#include "v_text.h" +#include "m_fixed.h" +#include "textures/textures.h" +#include "r_data/colormaps.h" + +// On the Alpha, accessing the shorts directly if they aren't aligned on a +// 4-byte boundary causes unaligned access warnings. Why it does this at +// all and only while initing the textures is beyond me. + +#ifdef ALPHA +#define SAFESHORT(s) ((short)(((BYTE *)&(s))[0] + ((BYTE *)&(s))[1] * 256)) +#else +#define SAFESHORT(s) LittleShort(s) +#endif + + + +//-------------------------------------------------------------------------- +// +// Data structures for the TEXTUREx lumps +// +//-------------------------------------------------------------------------- + +// +// Each texture is composed of one or more patches, with patches being lumps +// stored in the WAD. The lumps are referenced by number, and patched into +// the rectangular texture space using origin and possibly other attributes. +// +struct mappatch_t +{ + SWORD originx; + SWORD originy; + SWORD patch; + SWORD stepdir; + SWORD colormap; +}; + +// +// A wall texture is a list of patches which are to be combined in a +// predefined order. +// +struct maptexture_t +{ BYTE name[8]; - WORD Flags; // [RH] Was unused - BYTE ScaleX; // [RH] Scaling (8 is normal) - BYTE ScaleY; // [RH] Same as above - SWORD width; - SWORD height; - BYTE columndirectory[4]; // OBSOLETE - SWORD patchcount; - mappatch_t patches[1]; -}; - -#define MAPTEXF_WORLDPANNING 0x8000 - -// Strife uses versions of the above structures that remove all unused fields - -struct strifemappatch_t -{ - SWORD originx; - SWORD originy; - SWORD patch; -}; - -// -// A wall texture is a list of patches which are to be combined in a -// predefined order. -// -struct strifemaptexture_t -{ - BYTE name[8]; - WORD Flags; // [RH] Was unused - BYTE ScaleX; // [RH] Scaling (8 is normal) - BYTE ScaleY; // [RH] Same as above - SWORD width; - SWORD height; - SWORD patchcount; - strifemappatch_t patches[1]; -}; - - -//========================================================================== -// -// In-memory representation of a single PNAMES lump entry -// -//========================================================================== - -struct FPatchLookup -{ - FString Name; - FTexture *Texture; -}; - - -//========================================================================== -// -// A texture defined in a TEXTURE1 or TEXTURE2 lump -// -//========================================================================== - -class FMultiPatchTexture : public FTexture -{ -public: - FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflump); - FMultiPatchTexture (FScanner &sc, int usetype); - ~FMultiPatchTexture (); - - const BYTE *GetColumn (unsigned int column, const Span **spans_out); - const BYTE *GetPixels (); - FTextureFormat GetFormat(); - bool UseBasePalette() ; - void Unload (); - virtual void SetFrontSkyLayer (); - - int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf = NULL); - int GetSourceLump() { return DefinitionLump; } - FTexture *GetRedirect(bool wantwarped); - FTexture *GetRawTexture(); - -protected: - BYTE *Pixels; - Span **Spans; - int DefinitionLump; - - struct TexPart - { - SWORD OriginX, OriginY; - BYTE Rotate; - BYTE op; - FRemapTable *Translation; - PalEntry Blend; - FTexture *Texture; - fixed_t Alpha; - - TexPart(); - }; - - int NumParts; - TexPart *Parts; - bool bRedirect:1; - bool bTranslucentPatches:1; - - void MakeTexture (); - -private: - void CheckForHacks (); - void ParsePatch(FScanner &sc, TexPart & part, bool silent, int usetype); -}; - -//========================================================================== -// -// FMultiPatchTexture :: FMultiPatchTexture -// -//========================================================================== - -FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflumpnum) -: Pixels (0), Spans(0), Parts(0), bRedirect(false), bTranslucentPatches(false) -{ - union - { - const maptexture_t *d; - const strifemaptexture_t *s; - } - mtexture; - - union - { - const mappatch_t *d; - const strifemappatch_t *s; - } - mpatch; - - int i; - - mtexture.d = (const maptexture_t *)texdef; - bMultiPatch = true; - - if (strife) - { - NumParts = SAFESHORT(mtexture.s->patchcount); - } - else - { - NumParts = SAFESHORT(mtexture.d->patchcount); - } - - if (NumParts < 0) - { - I_FatalError ("Bad texture directory"); - } - - UseType = FTexture::TEX_Wall; - Parts = NumParts > 0 ? new TexPart[NumParts] : NULL; - Width = SAFESHORT(mtexture.d->width); - Height = SAFESHORT(mtexture.d->height); - Name = (char *)mtexture.d->name; - CalcBitSize (); - - xScale = mtexture.d->ScaleX ? mtexture.d->ScaleX*(FRACUNIT/8) : FRACUNIT; - yScale = mtexture.d->ScaleY ? mtexture.d->ScaleY*(FRACUNIT/8) : FRACUNIT; - - if (mtexture.d->Flags & MAPTEXF_WORLDPANNING) - { - bWorldPanning = true; - } - - if (strife) - { - mpatch.s = &mtexture.s->patches[0]; - } - else - { - mpatch.d = &mtexture.d->patches[0]; - } - - for (i = 0; i < NumParts; ++i) - { - if (unsigned(LittleShort(mpatch.d->patch)) >= unsigned(maxpatchnum)) - { - I_FatalError ("Bad PNAMES and/or texture directory:\n\nPNAMES has %d entries, but\n%s wants to use entry %d.", - maxpatchnum, Name, LittleShort(mpatch.d->patch)+1); - } - Parts[i].OriginX = LittleShort(mpatch.d->originx); - Parts[i].OriginY = LittleShort(mpatch.d->originy); - Parts[i].Texture = patchlookup[LittleShort(mpatch.d->patch)].Texture; - if (Parts[i].Texture == NULL) - { - Printf (TEXTCOLOR_RED "Unknown patch %s in texture %s\n", patchlookup[LittleShort(mpatch.d->patch)].Name, Name); - NumParts--; - i--; - } - else - { - Parts[i].Texture->bKeepAround = true; - } - if (strife) - mpatch.s++; - else - mpatch.d++; - } - if (NumParts == 0) - { - Printf ("Texture %s is left without any patches\n", Name); - } - - CheckForHacks (); - - // If this texture is just a wrapper around a single patch, we can simply - // forward GetPixels() and GetColumn() calls to that patch. - if (NumParts == 1) - { - if (Parts->OriginX == 0 && Parts->OriginY == 0 && - Parts->Texture->GetWidth() == Width && - Parts->Texture->GetHeight() == Height) - { - bRedirect = true; - } - } - DefinitionLump = deflumpnum; -} - -//========================================================================== -// -// FMultiPatchTexture :: ~FMultiPatchTexture -// -//========================================================================== - -FMultiPatchTexture::~FMultiPatchTexture () -{ - Unload (); - if (Parts != NULL) - { - for(int i=0; iSetFrontSkyLayer (); - } - bNoRemap0 = true; -} - -//========================================================================== -// -// FMultiPatchTexture :: Unload -// -//========================================================================== - -void FMultiPatchTexture::Unload () -{ - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } -} - -//========================================================================== -// -// FMultiPatchTexture :: GetPixels -// -//========================================================================== - -const BYTE *FMultiPatchTexture::GetPixels () -{ - if (bRedirect) - { - return Parts->Texture->GetPixels (); - } - if (Pixels == NULL) - { - MakeTexture (); - } - return Pixels; -} - -//========================================================================== -// -// FMultiPatchTexture :: GetColumn -// -//========================================================================== - -const BYTE *FMultiPatchTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - if (bRedirect) - { - return Parts->Texture->GetColumn (column, spans_out); - } - if (Pixels == NULL) - { - MakeTexture (); - } - if ((unsigned)column >= (unsigned)Width) - { - if (WidthMask + 1 == Width) - { - column &= WidthMask; - } - else - { - column %= Width; - } - } - if (spans_out != NULL) - { - if (Spans == NULL) - { - Spans = CreateSpans (Pixels); - } - *spans_out = Spans[column]; - } - return Pixels + column*Height; -} - - -//========================================================================== -// -// GetBlendMap -// -//========================================================================== - -BYTE *GetBlendMap(PalEntry blend, BYTE *blendwork) -{ - - switch (blend.a==0 ? int(blend) : -1) - { - case BLEND_ICEMAP: - return TranslationToTable(TRANSLATION(TRANSLATION_Standard, 7))->Remap; - - default: - if (blend >= BLEND_SPECIALCOLORMAP1 && blend < BLEND_SPECIALCOLORMAP1 + SpecialColormaps.Size()) - { - return SpecialColormaps[blend - BLEND_SPECIALCOLORMAP1].Colormap; - } - else if (blend >= BLEND_DESATURATE1 && blend <= BLEND_DESATURATE31) - { - return DesaturateColormap[blend - BLEND_DESATURATE1]; - } - else - { - blendwork[0]=0; - if (blend.a == 255) - { - for(int i=1;i<256;i++) - { - int rr = (blend.r * GPalette.BaseColors[i].r) / 255; - int gg = (blend.g * GPalette.BaseColors[i].g) / 255; - int bb = (blend.b * GPalette.BaseColors[i].b) / 255; - - blendwork[i] = ColorMatcher.Pick(rr, gg, bb); - } - return blendwork; - } - else if (blend.a != 0) - { - for(int i=1;i<256;i++) - { - int rr = (blend.r * blend.a + GPalette.BaseColors[i].r * (255-blend.a)) / 255; - int gg = (blend.g * blend.a + GPalette.BaseColors[i].g * (255-blend.a)) / 255; - int bb = (blend.b * blend.a + GPalette.BaseColors[i].b * (255-blend.a)) / 255; - - blendwork[i] = ColorMatcher.Pick(rr, gg, bb); - } - return blendwork; - } - } - } - return NULL; -} - -//========================================================================== -// -// FMultiPatchTexture :: MakeTexture -// -//========================================================================== - -void FMultiPatchTexture::MakeTexture () -{ - // Add a little extra space at the end if the texture's height is not - // a power of 2, in case somebody accidentally makes it repeat vertically. - int numpix = Width * Height + (1 << HeightBits) - Height; - BYTE blendwork[256]; - bool hasTranslucent = false; - - Pixels = new BYTE[numpix]; - memset (Pixels, 0, numpix); - - for (int i = 0; i < NumParts; ++i) - { - if (Parts[i].op != OP_COPY) - { - hasTranslucent = true; - } - } - - if (!hasTranslucent) - { - for (int i = 0; i < NumParts; ++i) - { - if (Parts[i].Texture->bHasCanvas) continue; // cannot use camera textures as patch. - - BYTE *trans = Parts[i].Translation ? Parts[i].Translation->Remap : NULL; - { - if (Parts[i].Blend != 0) - { - trans = GetBlendMap(Parts[i].Blend, blendwork); - } - Parts[i].Texture->CopyToBlock (Pixels, Width, Height, - Parts[i].OriginX, Parts[i].OriginY, Parts[i].Rotate, trans); - } - } - } - else - { - // In case there are translucent patches let's do the composition in - // True color to keep as much precision as possible before downconverting to the palette. - BYTE *buffer = new BYTE[Width * Height * 4]; - memset(buffer, 0, Width * Height * 4); - FillBuffer(buffer, Width * 4, Height, TEX_RGB); - for(int y = 0; y < Height; y++) - { - BYTE *in = buffer + Width * y * 4; - BYTE *out = Pixels + y; - for (int x = 0; x < Width; x++) - { - if (*out == 0 && in[3] != 0) - { - *out = RGB32k[in[2]>>3][in[1]>>3][in[0]>>3]; - } - out += Height; - in += 4; - } - } - delete [] buffer; - } -} - -//=========================================================================== -// -// FMultipatchTexture::CopyTrueColorPixels -// -// Preserves the palettes of each individual patch -// -//=========================================================================== - -int FMultiPatchTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) -{ - int retv = -1; - - if (bRedirect) - { // Redirect straight to the real texture's routine. - return Parts[0].Texture->CopyTrueColorPixels(bmp, x, y, rotate, inf); - } - - if (rotate != 0 || (inf != NULL && ((inf->op != OP_OVERWRITE && inf->op != OP_COPY) || inf->blend != BLEND_NONE))) - { // We are doing some sort of fancy stuff to the destination bitmap, so composite to - // a temporary bitmap, and copy that. - FBitmap tbmp; - if (tbmp.Create(Width, Height)) - { - retv = MAX(retv, CopyTrueColorPixels(&tbmp, 0, 0, 0)); - bmp->CopyPixelDataRGB(x, y, tbmp.GetPixels(), Width, Height, - 4, tbmp.GetPitch(), rotate, CF_BGRA, inf); - } - return retv; - } - - // When compositing a multipatch texture with multipatch parts, - // drawing must be restricted to the actual area which is covered by this texture. - FClipRect saved_cr = bmp->GetClipRect(); - bmp->IntersectClipRect(x, y, Width, Height); - - if (inf != NULL && inf->op == OP_OVERWRITE) - { - bmp->Zero(); - } - - for(int i = 0; i < NumParts; i++) - { - int ret = -1; - FCopyInfo info; - - if (Parts[i].Texture->bHasCanvas) continue; // cannot use camera textures as patch. - - memset (&info, 0, sizeof(info)); - info.alpha = Parts[i].Alpha; - info.invalpha = FRACUNIT - info.alpha; - info.op = ECopyOp(Parts[i].op); - PalEntry b = Parts[i].Blend; - if (b.a == 0 && b != BLEND_NONE) - { - info.blend = EBlend(b.d); - } - else if (b.a != 0) - { - if (b.a == 255) - { - info.blendcolor[0] = b.r * FRACUNIT / 255; - info.blendcolor[1] = b.g * FRACUNIT / 255; - info.blendcolor[2] = b.b * FRACUNIT / 255; - info.blend = BLEND_MODULATE; - } - else - { - fixed_t blendalpha = b.a * FRACUNIT / 255; - info.blendcolor[0] = b.r * blendalpha; - info.blendcolor[1] = b.g * blendalpha; - info.blendcolor[2] = b.b * blendalpha; - info.blendcolor[3] = FRACUNIT - blendalpha; - info.blend = BLEND_OVERLAY; - } - } - - if (Parts[i].Translation != NULL) - { // Using a translation forces downconversion to the base palette - ret = Parts[i].Texture->CopyTrueColorTranslated(bmp, x+Parts[i].OriginX, y+Parts[i].OriginY, Parts[i].Rotate, Parts[i].Translation, &info); - } - else - { - ret = Parts[i].Texture->CopyTrueColorPixels(bmp, x+Parts[i].OriginX, y+Parts[i].OriginY, Parts[i].Rotate, &info); - } - - if (ret > retv) retv = ret; - } - // Restore previous clipping rectangle. - bmp->SetClipRect(saved_cr); - return retv; -} - -//========================================================================== -// -// FMultiPatchTexture :: GetFormat -// -// only returns 'paletted' if all patches use the base palette. -// -//========================================================================== - -FTextureFormat FMultiPatchTexture::GetFormat() -{ - if (bComplex) return TEX_RGB; - if (NumParts == 1) return Parts[0].Texture->GetFormat(); - return UseBasePalette() ? TEX_Pal : TEX_RGB; -} - - -//=========================================================================== -// -// FMultipatchTexture::UseBasePalette -// -// returns true if all patches in the texture use the unmodified base -// palette. -// -//=========================================================================== - -bool FMultiPatchTexture::UseBasePalette() -{ - if (bComplex) return false; - for(int i=0;iUseBasePalette()) return false; - } - return true; -} - -//========================================================================== -// -// FMultiPatchTexture :: CheckForHacks -// -//========================================================================== - -void FMultiPatchTexture::CheckForHacks () -{ - if (NumParts <= 0) - { - return; - } - - // Heretic sky textures are marked as only 128 pixels tall, - // even though they are really 200 pixels tall. - if (gameinfo.gametype == GAME_Heretic && - Name[0] == 'S' && - Name[1] == 'K' && - Name[2] == 'Y' && - Name[4] == 0 && - Name[3] >= '1' && - Name[3] <= '3' && - Height == 128) - { - Height = 200; - HeightBits = 8; - return; - } - - // The Doom E1 sky has its patch's y offset at -8 instead of 0. - if (gameinfo.gametype == GAME_Doom && - !(gameinfo.flags & GI_MAPxx) && - NumParts == 1 && - Height == 128 && - Parts->OriginY == -8 && - Name[0] == 'S' && - Name[1] == 'K' && - Name[2] == 'Y' && - Name[3] == '1' && - Name[4] == 0) - { - Parts->OriginY = 0; - return; - } - - // BIGDOOR7 in Doom also has patches at y offset -4 instead of 0. - if (gameinfo.gametype == GAME_Doom && - !(gameinfo.flags & GI_MAPxx) && - NumParts == 2 && - Height == 128 && - Parts[0].OriginY == -4 && - Parts[1].OriginY == -4 && - Name[0] == 'B' && - Name[1] == 'I' && - Name[2] == 'G' && - Name[3] == 'D' && - Name[4] == 'O' && - Name[5] == 'O' && - Name[6] == 'R' && - Name[7] == '7') - { - Parts[0].OriginY = 0; - Parts[1].OriginY = 0; - return; - } - - // [RH] Some wads (I forget which!) have single-patch textures 256 - // pixels tall that have patch lengths recorded as 0. I can't think of - // any good reason for them to do this, and since I didn't make note - // of which wad made me hack in support for them, the hack is gone - // because I've added support for DeePsea's true tall patches. - // - // Okay, I found a wad with crap patches: Pleiades.wad's sky patches almost - // fit this description and are a big mess, but they're not single patch! - if (Height == 256) - { - int i; - - // All patches must be at the top of the texture for this fix - for (i = 0; i < NumParts; ++i) - { - if (Parts[i].OriginY != 0) - { - break; - } - } - - if (i == NumParts) - { - for (i = 0; i < NumParts; ++i) - { - Parts[i].Texture->HackHack(256); - } - } - } -} - -//========================================================================== -// -// FMultiPatchTexture :: GetRedirect -// -//========================================================================== - -FTexture *FMultiPatchTexture::GetRedirect(bool wantwarped) -{ - return bRedirect ? Parts->Texture : this; -} - -//========================================================================== -// -// FMultiPatchTexture :: GetRawTexture -// -// Doom ignored all compositing of mid-sided textures on two-sided lines. -// Since these textures had to be single-patch in Doom, that essentially -// means it ignores their Y offsets. -// -// If this texture is composed of only one patch, return that patch. -// Otherwise, return this texture, since Doom wouldn't have been able to -// draw it anyway. -// -//========================================================================== - -FTexture *FMultiPatchTexture::GetRawTexture() -{ - return NumParts == 1 ? Parts->Texture : this; -} - -//========================================================================== -// -// FMultiPatchTexture :: TexPart :: TexPart -// -//========================================================================== - -FMultiPatchTexture::TexPart::TexPart() -{ - OriginX = OriginY = 0; - Rotate = 0; - Texture = NULL; - Translation = NULL; - Blend = 0; - Alpha = FRACUNIT; - op = OP_COPY; -} - -//========================================================================== -// -// FTextureManager :: AddTexturesLump -// -//========================================================================== - -void FTextureManager::AddTexturesLump (const void *lumpdata, int lumpsize, int deflumpnum, int patcheslump, int firstdup, bool texture1) -{ - FPatchLookup *patchlookup = NULL; - int i; - DWORD numpatches; - - if (firstdup == 0) - { - firstdup = (int)Textures.Size(); - } - - { - FWadLump pnames = Wads.OpenLumpNum (patcheslump); - - pnames >> numpatches; - - // Check whether the amount of names reported is correct. - if ((signed)numpatches < 0) - { - Printf("Corrupt PNAMES lump found (negative amount of entries reported)"); - return; - } - - // Check whether the amount of names reported is correct. - int lumplength = Wads.LumpLength(patcheslump); - if (numpatches > DWORD((lumplength-4)/8)) - { - Printf("PNAMES lump is shorter than required (%u entries reported but only %d bytes (%d entries) long\n", - numpatches, lumplength, (lumplength-4)/8); - // Truncate but continue reading. Who knows how many such lumps exist? - numpatches = (lumplength-4)/8; - } - - // Catalog the patches these textures use so we know which - // textures they represent. - patchlookup = new FPatchLookup[numpatches]; - for (DWORD i = 0; i < numpatches; ++i) - { - char pname[9]; - pnames.Read (pname, 8); - pname[8] = '\0'; - patchlookup[i].Name = pname; - FTextureID j = CheckForTexture (patchlookup[i].Name, FTexture::TEX_WallPatch); - if (j.isValid()) - { - patchlookup[i].Texture = Textures[j.GetIndex()].Texture; - } - else - { - // Shareware Doom has the same PNAMES lump as the registered - // Doom, so printing warnings for patches that don't really - // exist isn't such a good idea. - //Printf ("Patch %s not found.\n", patchlookup[i].Name); - patchlookup[i].Texture = NULL; - } - } - } - - bool isStrife = false; - const DWORD *maptex, *directory; - DWORD maxoff; - int numtextures; - DWORD offset = 0; // Shut up, GCC! - - maptex = (const DWORD *)lumpdata; - numtextures = LittleLong(*maptex); - maxoff = lumpsize; - - if (maxoff < DWORD(numtextures+1)*4) - { - Printf ("Texture directory is too short"); - delete[] patchlookup; - return; - } - - // Scan the texture lump to decide if it contains Doom or Strife textures - for (i = 0, directory = maptex+1; i < numtextures; ++i) - { - offset = LittleLong(directory[i]); - if (offset > maxoff) - { - Printf ("Bad texture directory"); - delete[] patchlookup; - return; - } - - maptexture_t *tex = (maptexture_t *)((BYTE *)maptex + offset); - - // There is bizzarely a Doom editing tool that writes to the - // first two elements of columndirectory, so I can't check those. - if (SAFESHORT(tex->patchcount) < 0 || - tex->columndirectory[2] != 0 || - tex->columndirectory[3] != 0) - { - isStrife = true; - break; - } - } - - - // Textures defined earlier in the lump take precedence over those defined later, - // but later TEXTUREx lumps take precedence over earlier ones. - for (i = 1, directory = maptex; i <= numtextures; ++i) - { - if (i == 1 && texture1) - { - // The very first texture is just a dummy. Copy its dimensions to texture 0. - // It still needs to be created in case someone uses it by name. - offset = LittleLong(directory[1]); - const maptexture_t *tex = (const maptexture_t *)((const BYTE *)maptex + offset); - FDummyTexture *tex0 = static_cast(Textures[0].Texture); - tex0->SetSize (SAFESHORT(tex->width), SAFESHORT(tex->height)); - } - - offset = LittleLong(directory[i]); - if (offset > maxoff) - { - Printf ("Bad texture directory"); - delete[] patchlookup; - return; - } - - // If this texture was defined already in this lump, skip it - // This could cause problems with animations that use the same name for intermediate - // textures. Should I be worried? - int j; - for (j = (int)Textures.Size() - 1; j >= firstdup; --j) - { - if (strnicmp (Textures[j].Texture->Name, (const char *)maptex + offset, 8) == 0) - break; - } - if (j + 1 == firstdup) - { - FMultiPatchTexture *tex = new FMultiPatchTexture ((const BYTE *)maptex + offset, patchlookup, numpatches, isStrife, deflumpnum); - if (i == 1 && texture1) - { - tex->UseType = FTexture::TEX_FirstDefined; - } - TexMan.AddTexture (tex); - StartScreen->Progress(); - } - } - delete[] patchlookup; -} - - -//========================================================================== -// -// FTextureManager :: AddTexturesLumps -// -//========================================================================== - -void FTextureManager::AddTexturesLumps (int lump1, int lump2, int patcheslump) -{ - int firstdup = (int)Textures.Size(); - - if (lump1 >= 0) - { - FMemLump texdir = Wads.ReadLump (lump1); - AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump1), lump1, patcheslump, firstdup, true); - } - if (lump2 >= 0) - { - FMemLump texdir = Wads.ReadLump (lump2); - AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump2), lump2, patcheslump, firstdup, false); - } -} - - -//========================================================================== -// -// -// -//========================================================================== - -void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part, bool silent, int usetype) -{ - FString patchname; - sc.MustGetString(); - - FTextureID texno = TexMan.CheckForTexture(sc.String, usetype); - int Mirror = 0; - - if (!texno.isValid()) - { - if (strlen(sc.String) <= 8 && !strpbrk(sc.String, "./")) - { - int lumpnum = Wads.CheckNumForName(sc.String, usetype == TEX_MiscPatch? ns_graphics : ns_patches); - if (lumpnum >= 0) - { - part.Texture = FTexture::CreateTexture(lumpnum, usetype); - TexMan.AddTexture(part.Texture); - } - } - } - else - { - part.Texture = TexMan[texno]; - bComplex |= part.Texture->bComplex; - } - if (part.Texture == NULL) - { - if (!silent) Printf(TEXTCOLOR_RED "Unknown patch '%s' in texture '%s'\n", sc.String, Name); - } - sc.MustGetStringName(","); - sc.MustGetNumber(); - part.OriginX = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - part.OriginY = sc.Number; - - if (sc.CheckString("{")) - { - while (!sc.CheckString("}")) - { - sc.MustGetString(); - if (sc.Compare("flipx")) - { - Mirror |= 1; - } - else if (sc.Compare("flipy")) - { - Mirror |= 2; - } - else if (sc.Compare("rotate")) - { - sc.MustGetNumber(); - sc.Number = (((sc.Number + 90)%360)-90); - if (sc.Number != 0 && sc.Number !=90 && sc.Number != 180 && sc.Number != -90) - { - sc.ScriptError("Rotation must be a multiple of 90 degrees."); - } - part.Rotate = (sc.Number / 90) & 3; - } - else if (sc.Compare("Translation")) - { - int match; - - bComplex = true; - if (part.Translation != NULL) delete part.Translation; - part.Translation = NULL; - part.Blend = 0; - static const char *maps[] = { "inverse", "gold", "red", "green", "blue", NULL }; - sc.MustGetString(); - - match = sc.MatchString(maps); - if (match >= 0) - { - part.Blend = BLEND_SPECIALCOLORMAP1 + match; - } - else if (sc.Compare("ICE")) - { - part.Blend = BLEND_ICEMAP; - } - else if (sc.Compare("DESATURATE")) - { - sc.MustGetStringName(","); - sc.MustGetNumber(); - part.Blend = BLEND_DESATURATE1 + clamp(sc.Number-1, 0, 30); - } - else - { - sc.UnGet(); - part.Translation = new FRemapTable; - part.Translation->MakeIdentity(); - do - { - sc.MustGetString(); - part.Translation->AddToTranslation(sc.String); - } - while (sc.CheckString(",")); - } - - } - else if (sc.Compare("Colormap")) - { - float r1,g1,b1; - float r2,g2,b2; - - sc.MustGetFloat(); - r1 = (float)sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - g1 = (float)sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - b1 = (float)sc.Float; - if (!sc.CheckString(",")) - { - part.Blend = AddSpecialColormap(0,0,0, r1, g1, b1); - } - else - { - sc.MustGetFloat(); - r2 = (float)sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - g2 = (float)sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - b2 = (float)sc.Float; - part.Blend = AddSpecialColormap(r1, g1, b1, r2, g2, b2); - } - } - else if (sc.Compare("Blend")) - { - bComplex = true; - if (part.Translation != NULL) delete part.Translation; - part.Translation = NULL; - part.Blend = 0; - - if (!sc.CheckNumber()) - { - sc.MustGetString(); - part.Blend = V_GetColor(NULL, sc.String); - } - else - { - int r,g,b; - - r = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - g = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - b = sc.Number; - sc.MustGetStringName(","); - part.Blend = MAKERGB(r, g, b); - } - // Blend.a may never be 0 here. - if (sc.CheckString(",")) - { - sc.MustGetFloat(); - if (sc.Float > 0.f) - part.Blend.a = clamp(int(sc.Float*255), 1, 254); - else - part.Blend = 0; - } - else part.Blend.a = 255; - } - else if (sc.Compare("alpha")) - { - sc.MustGetFloat(); - part.Alpha = clamp(FLOAT2FIXED(sc.Float), 0, FRACUNIT); - // bComplex is not set because it is only needed when the style is not OP_COPY. - } - else if (sc.Compare("style")) - { - static const char *styles[] = {"copy", "translucent", "add", "subtract", "reversesubtract", "modulate", "copyalpha", "copynewalpha", "overlay", NULL }; - sc.MustGetString(); - part.op = sc.MustMatchString(styles); - bComplex |= (part.op != OP_COPY); - bTranslucentPatches = bComplex; - } - else if (sc.Compare("useoffsets")) - { - if (part.Texture != NULL) - { - part.OriginX -= part.Texture->LeftOffset; - part.OriginY -= part.Texture->TopOffset; - } - } - } - } - if (Mirror & 2) - { - part.Rotate = (part.Rotate + 2) & 3; - Mirror ^= 1; - } - if (Mirror & 1) - { - part.Rotate |= 4; - } -} - -//========================================================================== -// -// Constructor for text based multipatch definitions -// -//========================================================================== - -FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) -: Pixels (0), Spans(0), Parts(0), bRedirect(false), bTranslucentPatches(false) -{ - TArray parts; - bool bSilent = false; - - bMultiPatch = true; - sc.SetCMode(true); - sc.MustGetString(); - const char* textureName = NULL; - if (sc.Compare("optional")) - { - bSilent = true; - sc.MustGetString(); - if (sc.Compare(",")) - { - // this is not right. Apparently a texture named 'optional' is being defined right now... - sc.UnGet(); - textureName = "optional"; - bSilent = false; - } - } - Name = !textureName ? sc.String : textureName; - Name.ToUpper(); - sc.MustGetStringName(","); - sc.MustGetNumber(); - Width = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - Height = sc.Number; - UseType = usetype; - - if (sc.CheckString("{")) - { - while (!sc.CheckString("}")) - { - sc.MustGetString(); - if (sc.Compare("XScale")) - { - sc.MustGetFloat(); - xScale = FLOAT2FIXED(sc.Float); - if (xScale == 0) sc.ScriptError("Texture %s is defined with null x-scale\n", Name); - } - else if (sc.Compare("YScale")) - { - sc.MustGetFloat(); - yScale = FLOAT2FIXED(sc.Float); - if (yScale == 0) sc.ScriptError("Texture %s is defined with null y-scale\n", Name); - } - else if (sc.Compare("WorldPanning")) - { - bWorldPanning = true; - } - else if (sc.Compare("NullTexture")) - { - UseType = FTexture::TEX_Null; - } - else if (sc.Compare("NoDecals")) - { - bNoDecals = true; - } - else if (sc.Compare("Patch")) - { - TexPart part; - ParsePatch(sc, part, bSilent, TEX_WallPatch); - if (part.Texture != NULL) parts.Push(part); - part.Texture = NULL; - part.Translation = NULL; - } - else if (sc.Compare("Graphic")) - { - TexPart part; - ParsePatch(sc, part, bSilent, TEX_MiscPatch); - if (part.Texture != NULL) parts.Push(part); - part.Texture = NULL; - part.Translation = NULL; - } - else if (sc.Compare("Offset")) - { - sc.MustGetNumber(); - LeftOffset = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - TopOffset = sc.Number; - } - else - { - sc.ScriptError("Unknown texture property '%s'", sc.String); - } - } - - NumParts = parts.Size(); - Parts = new TexPart[NumParts]; - memcpy(Parts, &parts[0], NumParts * sizeof(*Parts)); - - //CalcBitSize (); - - // If this texture is just a wrapper around a single patch, we can simply - // forward GetPixels() and GetColumn() calls to that patch. - if (NumParts == 1) - { - if (Parts->OriginX == 0 && Parts->OriginY == 0 && - Parts->Texture->GetWidth() == Width && - Parts->Texture->GetHeight() == Height && - Parts->Rotate == 0 && - !bComplex) - { - bRedirect = true; - } - } - } - - if (Width <= 0 || Height <= 0) - { - UseType = FTexture::TEX_Null; - Printf("Texture %s has invalid dimensions (%d, %d)\n", Name, Width, Height); - Width = Height = 1; - } - CalcBitSize (); - - - sc.SetCMode(false); -} - - - -void FTextureManager::ParseXTexture(FScanner &sc, int usetype) -{ - FTexture *tex = new FMultiPatchTexture(sc, usetype); - TexMan.AddTexture (tex); -} + WORD Flags; // [RH] Was unused + BYTE ScaleX; // [RH] Scaling (8 is normal) + BYTE ScaleY; // [RH] Same as above + SWORD width; + SWORD height; + BYTE columndirectory[4]; // OBSOLETE + SWORD patchcount; + mappatch_t patches[1]; +}; + +#define MAPTEXF_WORLDPANNING 0x8000 + +// Strife uses versions of the above structures that remove all unused fields + +struct strifemappatch_t +{ + SWORD originx; + SWORD originy; + SWORD patch; +}; + +// +// A wall texture is a list of patches which are to be combined in a +// predefined order. +// +struct strifemaptexture_t +{ + BYTE name[8]; + WORD Flags; // [RH] Was unused + BYTE ScaleX; // [RH] Scaling (8 is normal) + BYTE ScaleY; // [RH] Same as above + SWORD width; + SWORD height; + SWORD patchcount; + strifemappatch_t patches[1]; +}; + + +//========================================================================== +// +// In-memory representation of a single PNAMES lump entry +// +//========================================================================== + +struct FPatchLookup +{ + FString Name; + FTexture *Texture; +}; + + +//========================================================================== +// +// A texture defined in a TEXTURE1 or TEXTURE2 lump +// +//========================================================================== + +class FMultiPatchTexture : public FTexture +{ +public: + FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflump); + FMultiPatchTexture (FScanner &sc, int usetype); + ~FMultiPatchTexture (); + + const BYTE *GetColumn (unsigned int column, const Span **spans_out); + const BYTE *GetPixels (); + FTextureFormat GetFormat(); + bool UseBasePalette() ; + void Unload (); + virtual void SetFrontSkyLayer (); + + int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf = NULL); + int GetSourceLump() { return DefinitionLump; } + FTexture *GetRedirect(bool wantwarped); + FTexture *GetRawTexture(); + +protected: + BYTE *Pixels; + Span **Spans; + int DefinitionLump; + + struct TexPart + { + SWORD OriginX, OriginY; + BYTE Rotate; + BYTE op; + FRemapTable *Translation; + PalEntry Blend; + FTexture *Texture; + fixed_t Alpha; + + TexPart(); + }; + + int NumParts; + TexPart *Parts; + bool bRedirect:1; + bool bTranslucentPatches:1; + + void MakeTexture (); + +private: + void CheckForHacks (); + void ParsePatch(FScanner &sc, TexPart & part, bool silent, int usetype); +}; + +//========================================================================== +// +// FMultiPatchTexture :: FMultiPatchTexture +// +//========================================================================== + +FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflumpnum) +: Pixels (0), Spans(0), Parts(0), bRedirect(false), bTranslucentPatches(false) +{ + union + { + const maptexture_t *d; + const strifemaptexture_t *s; + } + mtexture; + + union + { + const mappatch_t *d; + const strifemappatch_t *s; + } + mpatch; + + int i; + + mtexture.d = (const maptexture_t *)texdef; + bMultiPatch = true; + + if (strife) + { + NumParts = SAFESHORT(mtexture.s->patchcount); + } + else + { + NumParts = SAFESHORT(mtexture.d->patchcount); + } + + if (NumParts < 0) + { + I_FatalError ("Bad texture directory"); + } + + UseType = FTexture::TEX_Wall; + Parts = NumParts > 0 ? new TexPart[NumParts] : NULL; + Width = SAFESHORT(mtexture.d->width); + Height = SAFESHORT(mtexture.d->height); + Name = (char *)mtexture.d->name; + CalcBitSize (); + + xScale = mtexture.d->ScaleX ? mtexture.d->ScaleX*(FRACUNIT/8) : FRACUNIT; + yScale = mtexture.d->ScaleY ? mtexture.d->ScaleY*(FRACUNIT/8) : FRACUNIT; + + if (mtexture.d->Flags & MAPTEXF_WORLDPANNING) + { + bWorldPanning = true; + } + + if (strife) + { + mpatch.s = &mtexture.s->patches[0]; + } + else + { + mpatch.d = &mtexture.d->patches[0]; + } + + for (i = 0; i < NumParts; ++i) + { + if (unsigned(LittleShort(mpatch.d->patch)) >= unsigned(maxpatchnum)) + { + I_FatalError ("Bad PNAMES and/or texture directory:\n\nPNAMES has %d entries, but\n%s wants to use entry %d.", + maxpatchnum, Name, LittleShort(mpatch.d->patch)+1); + } + Parts[i].OriginX = LittleShort(mpatch.d->originx); + Parts[i].OriginY = LittleShort(mpatch.d->originy); + Parts[i].Texture = patchlookup[LittleShort(mpatch.d->patch)].Texture; + if (Parts[i].Texture == NULL) + { + Printf(TEXTCOLOR_RED "Unknown patch %s in texture %s\n", patchlookup[LittleShort(mpatch.d->patch)].Name, Name); + NumParts--; + i--; + } + else + { + Parts[i].Texture->bKeepAround = true; + } + if (strife) + mpatch.s++; + else + mpatch.d++; + } + if (NumParts == 0) + { + Printf ("Texture %s is left without any patches\n", Name); + } + + CheckForHacks (); + + // If this texture is just a wrapper around a single patch, we can simply + // forward GetPixels() and GetColumn() calls to that patch. + if (NumParts == 1) + { + if (Parts->OriginX == 0 && Parts->OriginY == 0 && + Parts->Texture->GetWidth() == Width && + Parts->Texture->GetHeight() == Height) + { + bRedirect = true; + } + } + DefinitionLump = deflumpnum; +} + +//========================================================================== +// +// FMultiPatchTexture :: ~FMultiPatchTexture +// +//========================================================================== + +FMultiPatchTexture::~FMultiPatchTexture () +{ + Unload (); + if (Parts != NULL) + { + for(int i=0; iSetFrontSkyLayer (); + } + bNoRemap0 = true; +} + +//========================================================================== +// +// FMultiPatchTexture :: Unload +// +//========================================================================== + +void FMultiPatchTexture::Unload () +{ + if (Pixels != NULL) + { + delete[] Pixels; + Pixels = NULL; + } +} + +//========================================================================== +// +// FMultiPatchTexture :: GetPixels +// +//========================================================================== + +const BYTE *FMultiPatchTexture::GetPixels () +{ + if (bRedirect) + { + return Parts->Texture->GetPixels (); + } + if (Pixels == NULL) + { + MakeTexture (); + } + return Pixels; +} + +//========================================================================== +// +// FMultiPatchTexture :: GetColumn +// +//========================================================================== + +const BYTE *FMultiPatchTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + if (bRedirect) + { + return Parts->Texture->GetColumn (column, spans_out); + } + if (Pixels == NULL) + { + MakeTexture (); + } + if ((unsigned)column >= (unsigned)Width) + { + if (WidthMask + 1 == Width) + { + column &= WidthMask; + } + else + { + column %= Width; + } + } + if (spans_out != NULL) + { + if (Spans == NULL) + { + Spans = CreateSpans (Pixels); + } + *spans_out = Spans[column]; + } + return Pixels + column*Height; +} + + +//========================================================================== +// +// GetBlendMap +// +//========================================================================== + +BYTE *GetBlendMap(PalEntry blend, BYTE *blendwork) +{ + + switch (blend.a==0 ? int(blend) : -1) + { + case BLEND_ICEMAP: + return TranslationToTable(TRANSLATION(TRANSLATION_Standard, 7))->Remap; + + default: + if (blend >= BLEND_SPECIALCOLORMAP1 && blend < BLEND_SPECIALCOLORMAP1 + SpecialColormaps.Size()) + { + return SpecialColormaps[blend - BLEND_SPECIALCOLORMAP1].Colormap; + } + else if (blend >= BLEND_DESATURATE1 && blend <= BLEND_DESATURATE31) + { + return DesaturateColormap[blend - BLEND_DESATURATE1]; + } + else + { + blendwork[0]=0; + if (blend.a == 255) + { + for(int i=1;i<256;i++) + { + int rr = (blend.r * GPalette.BaseColors[i].r) / 255; + int gg = (blend.g * GPalette.BaseColors[i].g) / 255; + int bb = (blend.b * GPalette.BaseColors[i].b) / 255; + + blendwork[i] = ColorMatcher.Pick(rr, gg, bb); + } + return blendwork; + } + else if (blend.a != 0) + { + for(int i=1;i<256;i++) + { + int rr = (blend.r * blend.a + GPalette.BaseColors[i].r * (255-blend.a)) / 255; + int gg = (blend.g * blend.a + GPalette.BaseColors[i].g * (255-blend.a)) / 255; + int bb = (blend.b * blend.a + GPalette.BaseColors[i].b * (255-blend.a)) / 255; + + blendwork[i] = ColorMatcher.Pick(rr, gg, bb); + } + return blendwork; + } + } + } + return NULL; +} + +//========================================================================== +// +// FMultiPatchTexture :: MakeTexture +// +//========================================================================== + +void FMultiPatchTexture::MakeTexture () +{ + // Add a little extra space at the end if the texture's height is not + // a power of 2, in case somebody accidentally makes it repeat vertically. + int numpix = Width * Height + (1 << HeightBits) - Height; + BYTE blendwork[256]; + bool hasTranslucent = false; + + Pixels = new BYTE[numpix]; + memset (Pixels, 0, numpix); + + for (int i = 0; i < NumParts; ++i) + { + if (Parts[i].op != OP_COPY) + { + hasTranslucent = true; + } + } + + if (!hasTranslucent) + { + for (int i = 0; i < NumParts; ++i) + { + if (Parts[i].Texture->bHasCanvas) continue; // cannot use camera textures as patch. + + BYTE *trans = Parts[i].Translation ? Parts[i].Translation->Remap : NULL; + { + if (Parts[i].Blend != 0) + { + trans = GetBlendMap(Parts[i].Blend, blendwork); + } + Parts[i].Texture->CopyToBlock (Pixels, Width, Height, + Parts[i].OriginX, Parts[i].OriginY, Parts[i].Rotate, trans); + } + } + } + else + { + // In case there are translucent patches let's do the composition in + // True color to keep as much precision as possible before downconverting to the palette. + BYTE *buffer = new BYTE[Width * Height * 4]; + memset(buffer, 0, Width * Height * 4); + FillBuffer(buffer, Width * 4, Height, TEX_RGB); + for(int y = 0; y < Height; y++) + { + BYTE *in = buffer + Width * y * 4; + BYTE *out = Pixels + y; + for (int x = 0; x < Width; x++) + { + if (*out == 0 && in[3] != 0) + { + *out = RGB32k[in[2]>>3][in[1]>>3][in[0]>>3]; + } + out += Height; + in += 4; + } + } + delete [] buffer; + } +} + +//=========================================================================== +// +// FMultipatchTexture::CopyTrueColorPixels +// +// Preserves the palettes of each individual patch +// +//=========================================================================== + +int FMultiPatchTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) +{ + int retv = -1; + + if (bRedirect) + { // Redirect straight to the real texture's routine. + return Parts[0].Texture->CopyTrueColorPixels(bmp, x, y, rotate, inf); + } + + if (rotate != 0 || (inf != NULL && ((inf->op != OP_OVERWRITE && inf->op != OP_COPY) || inf->blend != BLEND_NONE))) + { // We are doing some sort of fancy stuff to the destination bitmap, so composite to + // a temporary bitmap, and copy that. + FBitmap tbmp; + if (tbmp.Create(Width, Height)) + { + retv = MAX(retv, CopyTrueColorPixels(&tbmp, 0, 0, 0)); + bmp->CopyPixelDataRGB(x, y, tbmp.GetPixels(), Width, Height, + 4, tbmp.GetPitch(), rotate, CF_BGRA, inf); + } + return retv; + } + + // When compositing a multipatch texture with multipatch parts, + // drawing must be restricted to the actual area which is covered by this texture. + FClipRect saved_cr = bmp->GetClipRect(); + bmp->IntersectClipRect(x, y, Width, Height); + + if (inf != NULL && inf->op == OP_OVERWRITE) + { + bmp->Zero(); + } + + for(int i = 0; i < NumParts; i++) + { + int ret = -1; + FCopyInfo info; + + if (Parts[i].Texture->bHasCanvas) continue; // cannot use camera textures as patch. + + memset (&info, 0, sizeof(info)); + info.alpha = Parts[i].Alpha; + info.invalpha = FRACUNIT - info.alpha; + info.op = ECopyOp(Parts[i].op); + PalEntry b = Parts[i].Blend; + if (b.a == 0 && b != BLEND_NONE) + { + info.blend = EBlend(b.d); + } + else if (b.a != 0) + { + if (b.a == 255) + { + info.blendcolor[0] = b.r * FRACUNIT / 255; + info.blendcolor[1] = b.g * FRACUNIT / 255; + info.blendcolor[2] = b.b * FRACUNIT / 255; + info.blend = BLEND_MODULATE; + } + else + { + fixed_t blendalpha = b.a * FRACUNIT / 255; + info.blendcolor[0] = b.r * blendalpha; + info.blendcolor[1] = b.g * blendalpha; + info.blendcolor[2] = b.b * blendalpha; + info.blendcolor[3] = FRACUNIT - blendalpha; + info.blend = BLEND_OVERLAY; + } + } + + if (Parts[i].Translation != NULL) + { // Using a translation forces downconversion to the base palette + ret = Parts[i].Texture->CopyTrueColorTranslated(bmp, x+Parts[i].OriginX, y+Parts[i].OriginY, Parts[i].Rotate, Parts[i].Translation, &info); + } + else + { + ret = Parts[i].Texture->CopyTrueColorPixels(bmp, x+Parts[i].OriginX, y+Parts[i].OriginY, Parts[i].Rotate, &info); + } + + if (ret > retv) retv = ret; + } + // Restore previous clipping rectangle. + bmp->SetClipRect(saved_cr); + return retv; +} + +//========================================================================== +// +// FMultiPatchTexture :: GetFormat +// +// only returns 'paletted' if all patches use the base palette. +// +//========================================================================== + +FTextureFormat FMultiPatchTexture::GetFormat() +{ + if (bComplex) return TEX_RGB; + if (NumParts == 1) return Parts[0].Texture->GetFormat(); + return UseBasePalette() ? TEX_Pal : TEX_RGB; +} + + +//=========================================================================== +// +// FMultipatchTexture::UseBasePalette +// +// returns true if all patches in the texture use the unmodified base +// palette. +// +//=========================================================================== + +bool FMultiPatchTexture::UseBasePalette() +{ + if (bComplex) return false; + for(int i=0;iUseBasePalette()) return false; + } + return true; +} + +//========================================================================== +// +// FMultiPatchTexture :: CheckForHacks +// +//========================================================================== + +void FMultiPatchTexture::CheckForHacks () +{ + if (NumParts <= 0) + { + return; + } + + // Heretic sky textures are marked as only 128 pixels tall, + // even though they are really 200 pixels tall. + if (gameinfo.gametype == GAME_Heretic && + Name[0] == 'S' && + Name[1] == 'K' && + Name[2] == 'Y' && + Name[4] == 0 && + Name[3] >= '1' && + Name[3] <= '3' && + Height == 128) + { + Height = 200; + HeightBits = 8; + return; + } + + // The Doom E1 sky has its patch's y offset at -8 instead of 0. + if (gameinfo.gametype == GAME_Doom && + !(gameinfo.flags & GI_MAPxx) && + NumParts == 1 && + Height == 128 && + Parts->OriginY == -8 && + Name[0] == 'S' && + Name[1] == 'K' && + Name[2] == 'Y' && + Name[3] == '1' && + Name[4] == 0) + { + Parts->OriginY = 0; + return; + } + + // BIGDOOR7 in Doom also has patches at y offset -4 instead of 0. + if (gameinfo.gametype == GAME_Doom && + !(gameinfo.flags & GI_MAPxx) && + NumParts == 2 && + Height == 128 && + Parts[0].OriginY == -4 && + Parts[1].OriginY == -4 && + Name[0] == 'B' && + Name[1] == 'I' && + Name[2] == 'G' && + Name[3] == 'D' && + Name[4] == 'O' && + Name[5] == 'O' && + Name[6] == 'R' && + Name[7] == '7') + { + Parts[0].OriginY = 0; + Parts[1].OriginY = 0; + return; + } + + // [RH] Some wads (I forget which!) have single-patch textures 256 + // pixels tall that have patch lengths recorded as 0. I can't think of + // any good reason for them to do this, and since I didn't make note + // of which wad made me hack in support for them, the hack is gone + // because I've added support for DeePsea's true tall patches. + // + // Okay, I found a wad with crap patches: Pleiades.wad's sky patches almost + // fit this description and are a big mess, but they're not single patch! + if (Height == 256) + { + int i; + + // All patches must be at the top of the texture for this fix + for (i = 0; i < NumParts; ++i) + { + if (Parts[i].OriginY != 0) + { + break; + } + } + + if (i == NumParts) + { + for (i = 0; i < NumParts; ++i) + { + Parts[i].Texture->HackHack(256); + } + } + } +} + +//========================================================================== +// +// FMultiPatchTexture :: GetRedirect +// +//========================================================================== + +FTexture *FMultiPatchTexture::GetRedirect(bool wantwarped) +{ + return bRedirect ? Parts->Texture : this; +} + +//========================================================================== +// +// FMultiPatchTexture :: GetRawTexture +// +// Doom ignored all compositing of mid-sided textures on two-sided lines. +// Since these textures had to be single-patch in Doom, that essentially +// means it ignores their Y offsets. +// +// If this texture is composed of only one patch, return that patch. +// Otherwise, return this texture, since Doom wouldn't have been able to +// draw it anyway. +// +//========================================================================== + +FTexture *FMultiPatchTexture::GetRawTexture() +{ + return NumParts == 1 ? Parts->Texture : this; +} + +//========================================================================== +// +// FMultiPatchTexture :: TexPart :: TexPart +// +//========================================================================== + +FMultiPatchTexture::TexPart::TexPart() +{ + OriginX = OriginY = 0; + Rotate = 0; + Texture = NULL; + Translation = NULL; + Blend = 0; + Alpha = FRACUNIT; + op = OP_COPY; +} + +//========================================================================== +// +// FTextureManager :: AddTexturesLump +// +//========================================================================== + +void FTextureManager::AddTexturesLump (const void *lumpdata, int lumpsize, int deflumpnum, int patcheslump, int firstdup, bool texture1) +{ + FPatchLookup *patchlookup = NULL; + int i; + DWORD numpatches; + + if (firstdup == 0) + { + firstdup = (int)Textures.Size(); + } + + { + FWadLump pnames = Wads.OpenLumpNum (patcheslump); + + pnames >> numpatches; + + // Check whether the amount of names reported is correct. + if ((signed)numpatches < 0) + { + Printf("Corrupt PNAMES lump found (negative amount of entries reported)"); + return; + } + + // Check whether the amount of names reported is correct. + int lumplength = Wads.LumpLength(patcheslump); + if (numpatches > DWORD((lumplength-4)/8)) + { + Printf("PNAMES lump is shorter than required (%u entries reported but only %d bytes (%d entries) long\n", + numpatches, lumplength, (lumplength-4)/8); + // Truncate but continue reading. Who knows how many such lumps exist? + numpatches = (lumplength-4)/8; + } + + // Catalog the patches these textures use so we know which + // textures they represent. + patchlookup = new FPatchLookup[numpatches]; + for (DWORD i = 0; i < numpatches; ++i) + { + char pname[9]; + pnames.Read(pname, 8); + pname[8] = '\0'; + patchlookup[i].Name = pname; + FTextureID j = CheckForTexture (patchlookup[i].Name, FTexture::TEX_WallPatch); + if (j.isValid()) + { + patchlookup[i].Texture = Textures[j.GetIndex()].Texture; + } + else + { + // Shareware Doom has the same PNAMES lump as the registered + // Doom, so printing warnings for patches that don't really + // exist isn't such a good idea. + //Printf ("Patch %s not found.\n", patchlookup[i].Name); + patchlookup[i].Texture = NULL; + } + } + } + + bool isStrife = false; + const DWORD *maptex, *directory; + DWORD maxoff; + int numtextures; + DWORD offset = 0; // Shut up, GCC! + + maptex = (const DWORD *)lumpdata; + numtextures = LittleLong(*maptex); + maxoff = lumpsize; + + if (maxoff < DWORD(numtextures+1)*4) + { + Printf ("Texture directory is too short"); + delete[] patchlookup; + return; + } + + // Scan the texture lump to decide if it contains Doom or Strife textures + for (i = 0, directory = maptex+1; i < numtextures; ++i) + { + offset = LittleLong(directory[i]); + if (offset > maxoff) + { + Printf ("Bad texture directory"); + delete[] patchlookup; + return; + } + + maptexture_t *tex = (maptexture_t *)((BYTE *)maptex + offset); + + // There is bizzarely a Doom editing tool that writes to the + // first two elements of columndirectory, so I can't check those. + if (SAFESHORT(tex->patchcount) < 0 || + tex->columndirectory[2] != 0 || + tex->columndirectory[3] != 0) + { + isStrife = true; + break; + } + } + + + // Textures defined earlier in the lump take precedence over those defined later, + // but later TEXTUREx lumps take precedence over earlier ones. + for (i = 1, directory = maptex; i <= numtextures; ++i) + { + if (i == 1 && texture1) + { + // The very first texture is just a dummy. Copy its dimensions to texture 0. + // It still needs to be created in case someone uses it by name. + offset = LittleLong(directory[1]); + const maptexture_t *tex = (const maptexture_t *)((const BYTE *)maptex + offset); + FDummyTexture *tex0 = static_cast(Textures[0].Texture); + tex0->SetSize (SAFESHORT(tex->width), SAFESHORT(tex->height)); + } + + offset = LittleLong(directory[i]); + if (offset > maxoff) + { + Printf ("Bad texture directory"); + delete[] patchlookup; + return; + } + + // If this texture was defined already in this lump, skip it + // This could cause problems with animations that use the same name for intermediate + // textures. Should I be worried? + int j; + for (j = (int)Textures.Size() - 1; j >= firstdup; --j) + { + if (strnicmp (Textures[j].Texture->Name, (const char *)maptex + offset, 8) == 0) + break; + } + if (j + 1 == firstdup) + { + FMultiPatchTexture *tex = new FMultiPatchTexture ((const BYTE *)maptex + offset, patchlookup, numpatches, isStrife, deflumpnum); + if (i == 1 && texture1) + { + tex->UseType = FTexture::TEX_FirstDefined; + } + TexMan.AddTexture (tex); + StartScreen->Progress(); + } + } + delete[] patchlookup; +} + + +//========================================================================== +// +// FTextureManager :: AddTexturesLumps +// +//========================================================================== + +void FTextureManager::AddTexturesLumps (int lump1, int lump2, int patcheslump) +{ + int firstdup = (int)Textures.Size(); + + if (lump1 >= 0) + { + FMemLump texdir = Wads.ReadLump (lump1); + AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump1), lump1, patcheslump, firstdup, true); + } + if (lump2 >= 0) + { + FMemLump texdir = Wads.ReadLump (lump2); + AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump2), lump2, patcheslump, firstdup, false); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part, bool silent, int usetype) +{ + FString patchname; + sc.MustGetString(); + + FTextureID texno = TexMan.CheckForTexture(sc.String, usetype); + int Mirror = 0; + + if (!texno.isValid()) + { + if (strlen(sc.String) <= 8 && !strpbrk(sc.String, "./")) + { + int lumpnum = Wads.CheckNumForName(sc.String, usetype == TEX_MiscPatch? ns_graphics : ns_patches); + if (lumpnum >= 0) + { + part.Texture = FTexture::CreateTexture(lumpnum, usetype); + TexMan.AddTexture(part.Texture); + } + } + } + else + { + part.Texture = TexMan[texno]; + bComplex |= part.Texture->bComplex; + } + if (part.Texture == NULL) + { + if (!silent) Printf(TEXTCOLOR_RED "Unknown patch '%s' in texture '%s'\n", sc.String, Name); + } + sc.MustGetStringName(","); + sc.MustGetNumber(); + part.OriginX = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + part.OriginY = sc.Number; + + if (sc.CheckString("{")) + { + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("flipx")) + { + Mirror |= 1; + } + else if (sc.Compare("flipy")) + { + Mirror |= 2; + } + else if (sc.Compare("rotate")) + { + sc.MustGetNumber(); + sc.Number = (((sc.Number + 90)%360)-90); + if (sc.Number != 0 && sc.Number !=90 && sc.Number != 180 && sc.Number != -90) + { + sc.ScriptError("Rotation must be a multiple of 90 degrees."); + } + part.Rotate = (sc.Number / 90) & 3; + } + else if (sc.Compare("Translation")) + { + int match; + + bComplex = true; + if (part.Translation != NULL) delete part.Translation; + part.Translation = NULL; + part.Blend = 0; + static const char *maps[] = { "inverse", "gold", "red", "green", "blue", NULL }; + sc.MustGetString(); + + match = sc.MatchString(maps); + if (match >= 0) + { + part.Blend = BLEND_SPECIALCOLORMAP1 + match; + } + else if (sc.Compare("ICE")) + { + part.Blend = BLEND_ICEMAP; + } + else if (sc.Compare("DESATURATE")) + { + sc.MustGetStringName(","); + sc.MustGetNumber(); + part.Blend = BLEND_DESATURATE1 + clamp(sc.Number-1, 0, 30); + } + else + { + sc.UnGet(); + part.Translation = new FRemapTable; + part.Translation->MakeIdentity(); + do + { + sc.MustGetString(); + part.Translation->AddToTranslation(sc.String); + } + while (sc.CheckString(",")); + } + + } + else if (sc.Compare("Colormap")) + { + float r1,g1,b1; + float r2,g2,b2; + + sc.MustGetFloat(); + r1 = (float)sc.Float; + sc.MustGetStringName(","); + sc.MustGetFloat(); + g1 = (float)sc.Float; + sc.MustGetStringName(","); + sc.MustGetFloat(); + b1 = (float)sc.Float; + if (!sc.CheckString(",")) + { + part.Blend = AddSpecialColormap(0,0,0, r1, g1, b1); + } + else + { + sc.MustGetFloat(); + r2 = (float)sc.Float; + sc.MustGetStringName(","); + sc.MustGetFloat(); + g2 = (float)sc.Float; + sc.MustGetStringName(","); + sc.MustGetFloat(); + b2 = (float)sc.Float; + part.Blend = AddSpecialColormap(r1, g1, b1, r2, g2, b2); + } + } + else if (sc.Compare("Blend")) + { + bComplex = true; + if (part.Translation != NULL) delete part.Translation; + part.Translation = NULL; + part.Blend = 0; + + if (!sc.CheckNumber()) + { + sc.MustGetString(); + part.Blend = V_GetColor(NULL, sc.String); + } + else + { + int r,g,b; + + r = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + g = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + b = sc.Number; + sc.MustGetStringName(","); + part.Blend = MAKERGB(r, g, b); + } + // Blend.a may never be 0 here. + if (sc.CheckString(",")) + { + sc.MustGetFloat(); + if (sc.Float > 0.f) + part.Blend.a = clamp(int(sc.Float*255), 1, 254); + else + part.Blend = 0; + } + else part.Blend.a = 255; + } + else if (sc.Compare("alpha")) + { + sc.MustGetFloat(); + part.Alpha = clamp(FLOAT2FIXED(sc.Float), 0, FRACUNIT); + // bComplex is not set because it is only needed when the style is not OP_COPY. + } + else if (sc.Compare("style")) + { + static const char *styles[] = {"copy", "translucent", "add", "subtract", "reversesubtract", "modulate", "copyalpha", "copynewalpha", "overlay", NULL }; + sc.MustGetString(); + part.op = sc.MustMatchString(styles); + bComplex |= (part.op != OP_COPY); + bTranslucentPatches = bComplex; + } + else if (sc.Compare("useoffsets")) + { + if (part.Texture != NULL) + { + part.OriginX -= part.Texture->LeftOffset; + part.OriginY -= part.Texture->TopOffset; + } + } + } + } + if (Mirror & 2) + { + part.Rotate = (part.Rotate + 2) & 3; + Mirror ^= 1; + } + if (Mirror & 1) + { + part.Rotate |= 4; + } +} + +//========================================================================== +// +// Constructor for text based multipatch definitions +// +//========================================================================== + +FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) +: Pixels (0), Spans(0), Parts(0), bRedirect(false), bTranslucentPatches(false) +{ + TArray parts; + bool bSilent = false; + + bMultiPatch = true; + sc.SetCMode(true); + sc.MustGetString(); + const char* textureName = NULL; + if (sc.Compare("optional")) + { + bSilent = true; + sc.MustGetString(); + if (sc.Compare(",")) + { + // this is not right. Apparently a texture named 'optional' is being defined right now... + sc.UnGet(); + textureName = "optional"; + bSilent = false; + } + } + Name = !textureName ? sc.String : textureName; + Name.ToUpper(); + sc.MustGetStringName(","); + sc.MustGetNumber(); + Width = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + Height = sc.Number; + UseType = usetype; + + if (sc.CheckString("{")) + { + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("XScale")) + { + sc.MustGetFloat(); + xScale = FLOAT2FIXED(sc.Float); + if (xScale == 0) sc.ScriptError("Texture %s is defined with null x-scale\n", Name); + } + else if (sc.Compare("YScale")) + { + sc.MustGetFloat(); + yScale = FLOAT2FIXED(sc.Float); + if (yScale == 0) sc.ScriptError("Texture %s is defined with null y-scale\n", Name); + } + else if (sc.Compare("WorldPanning")) + { + bWorldPanning = true; + } + else if (sc.Compare("NullTexture")) + { + UseType = FTexture::TEX_Null; + } + else if (sc.Compare("NoDecals")) + { + bNoDecals = true; + } + else if (sc.Compare("Patch")) + { + TexPart part; + ParsePatch(sc, part, bSilent, TEX_WallPatch); + if (part.Texture != NULL) parts.Push(part); + part.Texture = NULL; + part.Translation = NULL; + } + else if (sc.Compare("Graphic")) + { + TexPart part; + ParsePatch(sc, part, bSilent, TEX_MiscPatch); + if (part.Texture != NULL) parts.Push(part); + part.Texture = NULL; + part.Translation = NULL; + } + else if (sc.Compare("Offset")) + { + sc.MustGetNumber(); + LeftOffset = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + TopOffset = sc.Number; + } + else + { + sc.ScriptError("Unknown texture property '%s'", sc.String); + } + } + + NumParts = parts.Size(); + Parts = new TexPart[NumParts]; + memcpy(Parts, &parts[0], NumParts * sizeof(*Parts)); + + //CalcBitSize (); + + // If this texture is just a wrapper around a single patch, we can simply + // forward GetPixels() and GetColumn() calls to that patch. + if (NumParts == 1) + { + if (Parts->OriginX == 0 && Parts->OriginY == 0 && + Parts->Texture->GetWidth() == Width && + Parts->Texture->GetHeight() == Height && + Parts->Rotate == 0 && + !bComplex) + { + bRedirect = true; + } + } + } + + if (Width <= 0 || Height <= 0) + { + UseType = FTexture::TEX_Null; + Printf("Texture %s has invalid dimensions (%d, %d)\n", Name, Width, Height); + Width = Height = 1; + } + CalcBitSize (); + + + sc.SetCMode(false); +} + + + +void FTextureManager::ParseXTexture(FScanner &sc, int usetype) +{ + FTexture *tex = new FMultiPatchTexture(sc, usetype); + TexMan.AddTexture (tex); +} From 9d846395bc0e78a3881ea8530502125191a5a4c9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 May 2014 21:05:00 +0200 Subject: [PATCH 4/7] - replaced console buffer with a significantly more efficient new version that also can hold a lot more data. --- src/CMakeLists.txt | 1 + src/c_console.cpp | 402 +++++----------------------------------- src/c_console.h | 3 + src/c_consolebuffer.cpp | 317 +++++++++++++++++++++++++++++++ src/c_consolebuffer.h | 85 +++++++++ src/nodebuild.cpp | 6 - src/v_text.cpp | 4 +- src/v_text.h | 6 +- 8 files changed, 458 insertions(+), 366 deletions(-) create mode 100644 src/c_consolebuffer.cpp create mode 100644 src/c_consolebuffer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5203b294cf..517b6bdbe8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -789,6 +789,7 @@ add_executable( zdoom WIN32 c_bind.cpp c_cmds.cpp c_console.cpp + c_consolebuffer.cpp c_cvars.cpp c_dispatch.cpp c_expr.cpp diff --git a/src/c_console.cpp b/src/c_console.cpp index 396b2dfcec..4dabdb9e11 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -65,22 +65,29 @@ #include "g_level.h" #include "d_event.h" #include "d_player.h" +#include "c_consolebuffer.h" #include "gi.h" -#define CONSOLESIZE 16384 // Number of characters to store in console -#define CONSOLELINES 256 // Max number of lines of console text -#define LINEMASK (CONSOLELINES-1) - #define LEFTMARGIN 8 #define RIGHTMARGIN 8 #define BOTTOMARGIN 12 + +CUSTOM_CVAR(Int, con_buffersize, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + // ensure a minimum size + if (self >= 0 && self < 128) self = 128; +} + +FConsoleBuffer *conbuffer; + static void C_TabComplete (bool goForward); static bool C_TabCompleteList (); static bool TabbedLast; // True if last key pressed was tab static bool TabbedList; // True if tab list was shown -CVAR (Bool, con_notablist, false, CVAR_ARCHIVE) +CVAR(Bool, con_notablist, false, CVAR_ARCHIVE) + static FTextureID conback; static DWORD conshade; @@ -94,18 +101,15 @@ extern FBaseCVar *CVars; extern FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE]; int ConCols, PhysRows; +int ConWidth; bool vidactive = false; bool cursoron = false; int ConBottom, ConScroll, RowAdjust; int CursorTicker; constate_e ConsoleState = c_up; -static char ConsoleBuffer[CONSOLESIZE]; -static char *Lines[CONSOLELINES]; -static bool LineJoins[CONSOLELINES]; static int TopLine, InsertLine; -static char *BufferRover = ConsoleBuffer; static void ClearConsole (); static void C_PasteText(FString clip, BYTE *buffer, int len); @@ -182,7 +186,6 @@ static struct NotifyText static int NotifyTop, NotifyTopGoal; -#define PRINTLEVELS 5 int PrintColors[PRINTLEVELS+2] = { CR_RED, CR_GOLD, CR_GRAY, CR_GREEN, CR_GREEN, CR_GOLD }; static void setmsgcolor (int index, int color); @@ -328,78 +331,11 @@ void C_InitConsole (int width, int height, bool ingame) { cwidth = cheight = 8; } - ConCols = (width - LEFTMARGIN - RIGHTMARGIN) / cwidth; + ConWidth = (width - LEFTMARGIN - RIGHTMARGIN); + ConCols = ConWidth / cwidth; PhysRows = height / cheight; - // If there is some text in the console buffer, reformat it - // for the new resolution. - if (TopLine != InsertLine) - { - // Note: Don't use new here, because we attach a handler to new in - // i_main.cpp that calls I_FatalError if the allocation fails, - // but we can gracefully handle such a condition here by just - // clearing the console buffer. (OTOH, what are the chances that - // any other memory allocations would succeed if we can't get - // these mallocs here?) - - char *fmtBuff = (char *)malloc (CONSOLESIZE); - char **fmtLines = (char **)malloc (CONSOLELINES*sizeof(char*)*4); - int out = 0; - - if (fmtBuff && fmtLines) - { - int in; - char *fmtpos; - bool newline = true; - - fmtpos = fmtBuff; - memset (fmtBuff, 0, CONSOLESIZE); - - for (in = TopLine; in != InsertLine; in = (in + 1) & LINEMASK) - { - size_t len = strlen (Lines[in]); - - if (fmtpos + len + 2 - fmtBuff > CONSOLESIZE) - { - break; - } - if (newline) - { - newline = false; - fmtLines[out++] = fmtpos; - } - strcpy (fmtpos, Lines[in]); - fmtpos += len; - if (!LineJoins[in]) - { - *fmtpos++ = '\n'; - fmtpos++; - if (out == CONSOLELINES*4) - { - break; - } - newline = true; - } - } - } - - ClearConsole (); - - if (fmtBuff && fmtLines) - { - int i; - - for (i = 0; i < out; i++) - { - AddToConsole (-1, fmtLines[i]); - } - } - - if (fmtBuff) - free (fmtBuff); - if (fmtLines) - free (fmtLines); - } + if (conbuffer == NULL) conbuffer = new FConsoleBuffer; } //========================================================================== @@ -507,16 +443,21 @@ void C_DeinitConsole () work = NULL; worklen = 0; } + + if (conbuffer != NULL) + { + delete conbuffer; + conbuffer = NULL; + } } static void ClearConsole () { - RowAdjust = 0; + if (conbuffer != NULL) + { + conbuffer->Clear(); + } TopLine = InsertLine = 0; - BufferRover = ConsoleBuffer; - memset (ConsoleBuffer, 0, CONSOLESIZE); - memset (Lines, 0, sizeof(Lines)); - memset (LineJoins, 0, sizeof(LineJoins)); } static void setmsgcolor (int index, int color) @@ -596,223 +537,9 @@ void C_AddNotifyString (int printlevel, const char *source) NotifyTopGoal = 0; } -static int FlushLines (const char *start, const char *stop) -{ - int i; - - for (i = TopLine; i != InsertLine; i = (i + 1) & LINEMASK) - { - if (Lines[i] < stop && Lines[i] + strlen (Lines[i]) > start) - { - Lines[i] = NULL; - } - else - { - break; - } - } - return i; -} - -static void AddLine (const char *text, bool more, size_t len) -{ - if (BufferRover + len + 1 - ConsoleBuffer > CONSOLESIZE) - { - TopLine = FlushLines (BufferRover, ConsoleBuffer + CONSOLESIZE); - BufferRover = ConsoleBuffer; - } - if (len >= CONSOLESIZE - 1) - { - text = text + len - CONSOLESIZE + 1; - len = CONSOLESIZE - 1; - } - TopLine = FlushLines (BufferRover, BufferRover + len + 1); - memcpy (BufferRover, text, len); - BufferRover[len] = 0; - Lines[InsertLine] = BufferRover; - BufferRover += len + 1; - LineJoins[InsertLine] = more; - InsertLine = (InsertLine + 1) & LINEMASK; - if (InsertLine == TopLine) - { - TopLine = (TopLine + 1) & LINEMASK; - } -} - void AddToConsole (int printlevel, const char *text) { - static enum - { - NEWLINE, - APPENDLINE, - REPLACELINE - } addtype = NEWLINE; - - char *work_p; - char *linestart; - FString cc('A' + char(CR_TAN)); - int size, len; - int x; - int maxwidth; - - if (ConsoleDrawing) - { - EnqueueConsoleText (false, printlevel, text); - return; - } - - len = (int)strlen (text); - size = len + 20; - - if (addtype != NEWLINE) - { - InsertLine = (InsertLine - 1) & LINEMASK; - if (Lines[InsertLine] == NULL) - { - InsertLine = (InsertLine + 1) & LINEMASK; - addtype = NEWLINE; - } - else - { - BufferRover = Lines[InsertLine]; - } - } - if (addtype == APPENDLINE) - { - size += (int)strlen (Lines[InsertLine]); - } - if (size > worklen) - { - work = (char *)M_Realloc (work, size); - worklen = size; - } - if (work == NULL) - { - static char oom[] = TEXTCOLOR_RED "*** OUT OF MEMORY ***"; - work = oom; - worklen = 0; - } - else - { - if (addtype == APPENDLINE) - { - strcpy (work, Lines[InsertLine]); - strcat (work, text); - } - else if (printlevel >= 0) - { - work[0] = TEXTCOLOR_ESCAPE; - work[1] = 'A' + (printlevel == PRINT_HIGH ? CR_TAN : - printlevel == 200 ? CR_GREEN : - printlevel < PRINTLEVELS ? PrintColors[printlevel] : - CR_TAN); - cc = work[1]; - strcpy (work + 2, text); - } - else - { - strcpy (work, text); - } - } - - work_p = linestart = work; - - if (ConFont != NULL && screen != NULL) - { - x = 0; - maxwidth = screen->GetWidth() - LEFTMARGIN - RIGHTMARGIN; - - while (*work_p) - { - if (*work_p == TEXTCOLOR_ESCAPE) - { - work_p++; - if (*work_p == '[') - { - char *start = work_p; - while (*work_p != ']' && *work_p != '\0') - { - work_p++; - } - if (*work_p != '\0') - { - work_p++; - } - cc = FString(start, work_p - start); - } - else if (*work_p != '\0') - { - cc = *work_p++; - } - continue; - } - int w = ConFont->GetCharWidth (*work_p); - if (*work_p == '\n' || x + w > maxwidth) - { - AddLine (linestart, *work_p != '\n', work_p - linestart); - if (*work_p == '\n') - { - x = 0; - work_p++; - } - else - { - x = w; - } - if (*work_p) - { - linestart = work_p - 1 - cc.Len(); - if (linestart < work) - { - // The line start is outside the buffer. - // Make space for the newly inserted stuff. - size_t movesize = work-linestart; - memmove(work + movesize, work, strlen(work)+1); - work_p += movesize; - linestart = work; - } - linestart[0] = TEXTCOLOR_ESCAPE; - strncpy (linestart + 1, cc, cc.Len()); - } - else - { - linestart = work_p; - } - } - else - { - x += w; - work_p++; - } - } - - if (*linestart) - { - AddLine (linestart, true, work_p - linestart); - } - } - else - { - while (*work_p) - { - if (*work_p++ == '\n') - { - AddLine (linestart, false, work_p - linestart - 1); - linestart = work_p; - } - } - if (*linestart) - { - AddLine (linestart, true, work_p - linestart); - } - } - - switch (text[len-1]) - { - case '\r': addtype = REPLACELINE; break; - case '\n': addtype = NEWLINE; break; - default: addtype = APPENDLINE; break; - } + conbuffer->AddText(printlevel, text, Logfile); } /* Adds a string to the console and also to the notify buffer */ @@ -823,34 +550,6 @@ int PrintString (int printlevel, const char *outline) return 0; } - if (Logfile) - { - // Strip out any color escape sequences before writing to the log file - char * copy = new char[strlen(outline)+1]; - const char * srcp = outline; - char * dstp = copy; - - while (*srcp != 0) - { - if (*srcp!=0x1c) - { - *dstp++=*srcp++; - } - else - { - if (srcp[1]!=0) srcp+=2; - else break; - } - } - *dstp=0; - - fputs (copy, Logfile); - delete [] copy; -//#ifdef _DEBUG - fflush (Logfile); -//#endif - } - if (printlevel != PRINT_LOG) { I_PrintStr (outline); @@ -949,6 +648,11 @@ void C_Ticker () if (lasttic == 0) lasttic = gametic - 1; + if (con_buffersize > 0) + { + conbuffer->ResizeBuffer(con_buffersize); + } + if (ConsoleState != c_up) { if (ConsoleState == c_falling) @@ -1236,38 +940,22 @@ void C_DrawConsole (bool hw2d) if (lines > 0) { + // No more enqueuing because adding new text to the console won't touch the actual print data. + conbuffer->FormatText(ConFont, ConWidth); + unsigned int consolelines = conbuffer->GetFormattedLineCount(); + FBrokenLines **blines = conbuffer->GetLines(); + FBrokenLines **printline = blines + consolelines - 1 - RowAdjust; + int bottomline = ConBottom - ConFont->GetHeight()*2 - 4; - int pos = (InsertLine - 1) & LINEMASK; - int i; ConsoleDrawing = true; - for (i = RowAdjust; i; i--) + for(FBrokenLines **p = printline; p >= blines && lines > 0; p--, lines--) { - if (pos == TopLine) - { - RowAdjust = RowAdjust - i; - break; - } - else - { - pos = (pos - 1) & LINEMASK; - } + screen->DrawText(ConFont, CR_TAN, LEFTMARGIN, offset + lines * ConFont->GetHeight(), (*p)->Text, TAG_DONE); } - pos++; - do - { - pos = (pos - 1) & LINEMASK; - if (Lines[pos] != NULL) - { - screen->DrawText (ConFont, CR_TAN, LEFTMARGIN, offset + lines * ConFont->GetHeight(), - Lines[pos], TAG_DONE); - } - lines--; - } while (pos != TopLine && lines > 0); ConsoleDrawing = false; - DequeueConsoleText (); if (ConBottom >= 20) { @@ -1293,7 +981,7 @@ void C_DrawConsole (bool hw2d) { // Indicate that the view has been scrolled up (10) // and if we can scroll no further (12) - screen->DrawChar (ConFont, CR_GREEN, 0, bottomline, pos == TopLine ? 12 : 10, TAG_DONE); + screen->DrawChar (ConFont, CR_GREEN, 0, bottomline, RowAdjust == conbuffer->GetFormattedLineCount() ? 12 : 10, TAG_DONE); } } } @@ -1445,7 +1133,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) RowAdjust += (SCREENHEIGHT-4) / ((gamestate == GS_FULLCONSOLE || gamestate == GS_STARTUP) ? ConFont->GetHeight() : ConFont->GetHeight()*2) - 3; } - else if (RowAdjust < CONSOLELINES) + else if (RowAdjust < conbuffer->GetFormattedLineCount()) { // Scroll console buffer up if (ev->subtype == EV_GUI_WheelUp) { @@ -1455,6 +1143,10 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) { RowAdjust++; } + if (RowAdjust > conbuffer->GetFormattedLineCount()) + { + RowAdjust = conbuffer->GetFormattedLineCount(); + } } break; @@ -1488,7 +1180,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) case GK_HOME: if (ev->data3 & GKM_CTRL) { // Move to top of console buffer - RowAdjust = CONSOLELINES; + RowAdjust = conbuffer->GetFormattedLineCount(); } else { // Move cursor to start of line diff --git a/src/c_console.h b/src/c_console.h index 8560d6779e..2c2e553f96 100644 --- a/src/c_console.h +++ b/src/c_console.h @@ -47,6 +47,9 @@ typedef enum cstate_t } constate_e; +#define PRINTLEVELS 5 +extern int PrintColors[PRINTLEVELS + 2]; + extern constate_e ConsoleState; extern int ConBottom; diff --git a/src/c_consolebuffer.cpp b/src/c_consolebuffer.cpp new file mode 100644 index 0000000000..3f64f34e4b --- /dev/null +++ b/src/c_consolebuffer.cpp @@ -0,0 +1,317 @@ +/* +** consolebuffer.cpp +** +** manages the text for the console +** +**--------------------------------------------------------------------------- +** Copyright 2014 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "c_console.h" +#include "c_consolebuffer.h" + + +//========================================================================== +// +// +// +//========================================================================== + +FConsoleBuffer::FConsoleBuffer() +{ + mLogFile = NULL; + mAddType = NEWLINE; + mLastFont = NULL; + mLastDisplayWidth = -1; + mLastLineNeedsUpdate = false; + mTextLines = 0; + mBufferWasCleared = true; + mBrokenStart.Push(0); +} + +//========================================================================== +// +// +// +//========================================================================== + +FConsoleBuffer::~FConsoleBuffer() +{ + FreeBrokenText(); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FConsoleBuffer::FreeBrokenText(unsigned start, unsigned end) +{ + if (end > mBrokenConsoleText.Size()) end = mBrokenConsoleText.Size(); + for (unsigned i = start; i < end; i++) + { + if (mBrokenConsoleText[i] != NULL) V_FreeBrokenLines(mBrokenConsoleText[i]); + mBrokenConsoleText[i] = NULL; + } +} + +//========================================================================== +// +// Adds a new line of text to the console +// This is kept as simple as possible. This function does not: +// - remove old text if the buffer gets larger than the specified size +// - format the text for the current screen layout +// +// These tasks will only be be performed once per frame because they are +// relatively expensive. The old console did them each time text was added +// resulting in extremely bad performance with a high output rate. +// +//========================================================================== + +void FConsoleBuffer::AddText(int printlevel, const char *text, FILE *logfile) +{ + FString build = TEXTCOLOR_TAN; + + if (mAddType == REPLACELINE) + { + // Just wondering: Do we actually need this case? If so, it may need some work. + mConsoleText.Pop(); // remove the line to be replaced + mLastLineNeedsUpdate = true; + } + else if (mAddType == APPENDLINE) + { + mConsoleText.Pop(build); + printlevel = -1; + mLastLineNeedsUpdate = true; + } + + if (printlevel >= 0 && printlevel != PRINT_HIGH) + { + if (printlevel == 200) build = TEXTCOLOR_GREEN; + else if (printlevel < PRINTLEVELS) build.Format("%c%c", TEXTCOLOR_ESCAPE, PrintColors[printlevel]); + } + + size_t textsize = strlen(text); + + if (text[textsize-1] == '\r') + { + textsize--; + mAddType = REPLACELINE; + } + else if (text[textsize-1] == '\n') + { + textsize--; + mAddType = NEWLINE; + } + else + { + mAddType = APPENDLINE; + } + + // don't bother about linefeeds etc. inside the text, we'll let the formatter sort this out later. + build.AppendCStrPart(text, textsize); + mConsoleText.Push(build); + if (logfile != NULL) WriteLineToLog(logfile, text); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FConsoleBuffer::WriteLineToLog(FILE *LogFile, const char *outline) +{ + // Strip out any color escape sequences before writing to the log file + char * copy = new char[strlen(outline)+1]; + const char * srcp = outline; + char * dstp = copy; + + while (*srcp != 0) + { + + if (*srcp != TEXTCOLOR_ESCAPE) + { + switch (*srcp) + { + case '\35': + *dstp++ = '<'; + break; + + case '\36': + *dstp++ = '-'; + break; + + case '\37': + *dstp++ = '>'; + break; + + default: + *dstp++=*srcp; + break; + } + srcp++; + } + else if (srcp[1] == '[') + { + srcp+=2; + while (*srcp != ']' && *srcp != 0) srcp++; + if (*srcp == ']') srcp++; + } + else + { + if (srcp[1]!=0) srcp+=2; + else break; + } + } + *dstp=0; + + fputs (copy, LogFile); + delete [] copy; + fflush (LogFile); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FConsoleBuffer::WriteContentToLog(FILE *LogFile) +{ + if (LogFile != NULL) + { + for (unsigned i = 0; i < mConsoleText.Size(); i++) + { + WriteLineToLog(LogFile, mConsoleText[i]); + } + } +} + +//========================================================================== +// +// ensures that the following text is not appended to the current line. +// +//========================================================================== + +void FConsoleBuffer::Linefeed(FILE *Logfile) +{ + if (mAddType != NEWLINE && Logfile != NULL) fputc('\n', Logfile); + mAddType = NEWLINE; +} + +//========================================================================== +// +// +// +//========================================================================== + +static const char bar1[] = TEXTCOLOR_RED "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36" + "\36\36\36\36\36\36\36\36\36\36\36\36\37" TEXTCOLOR_TAN "\n"; +static const char bar2[] = TEXTCOLOR_RED "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36" + "\36\36\36\36\36\36\36\36\36\36\36\36\37" TEXTCOLOR_GREEN "\n"; +static const char bar3[] = TEXTCOLOR_RED "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36" + "\36\36\36\36\36\36\36\36\36\36\36\36\37" TEXTCOLOR_NORMAL "\n"; +static const char logbar[] = "\n<------------------------------->\n"; + +void FConsoleBuffer::AddMidText(const char *string, bool bold, FILE *Logfile) +{ + Linefeed(Logfile); + AddText (-1, bold? bar2 : bar1, Logfile); + AddText (-1, string, Logfile); + Linefeed(Logfile); + AddText(-1, bar3, Logfile); +} + +//========================================================================== +// +// Format the text for output +// +//========================================================================== + +void FConsoleBuffer::FormatText(FFont *formatfont, int displaywidth) +{ + if (formatfont != mLastFont || displaywidth != mLastDisplayWidth || mBufferWasCleared) + { + FreeBrokenText(); + mBrokenConsoleText.Clear(); + mBrokenStart.Clear(); + mBrokenStart.Push(0); + mBrokenLines.Clear(); + mLastFont = formatfont; + mLastDisplayWidth = displaywidth; + mBufferWasCleared = false; + } + unsigned brokensize = mBrokenConsoleText.Size(); + if (brokensize == mConsoleText.Size()) + { + // The last line got text appended. We have to wait until here to format it because + // it is possible that during display new text will be added from the NetUpdate calls in the software version of DrawTextureV. + if (mLastLineNeedsUpdate) + { + brokensize--; + V_FreeBrokenLines(mBrokenConsoleText[brokensize]); + mBrokenConsoleText.Resize(brokensize); + } + } + mBrokenLines.Resize(mBrokenStart[brokensize]); + mBrokenStart.Resize(brokensize); + for (unsigned i = brokensize; i < mConsoleText.Size(); i++) + { + FBrokenLines *bl = V_BreakLines(formatfont, displaywidth, mConsoleText[i], true); + mBrokenConsoleText.Push(bl); + mBrokenStart.Push(mBrokenLines.Size()); + while (bl->Width != -1) + { + mBrokenLines.Push(bl); + bl++; + } + } + mTextLines = mBrokenLines.Size(); + mBrokenStart.Push(mTextLines); + mLastLineNeedsUpdate = false; +} + +//========================================================================== +// +// Delete old content if number of lines gets too large +// +//========================================================================== + +void FConsoleBuffer::ResizeBuffer(unsigned newsize) +{ + if (mConsoleText.Size() > newsize) + { + unsigned todelete = mConsoleText.Size() - newsize; + mConsoleText.Delete(0, todelete); + mBufferWasCleared = true; + } +} + diff --git a/src/c_consolebuffer.h b/src/c_consolebuffer.h new file mode 100644 index 0000000000..710423012b --- /dev/null +++ b/src/c_consolebuffer.h @@ -0,0 +1,85 @@ +/* +** consolebuffer.h +** +** manages the text for the console +** +**--------------------------------------------------------------------------- +** Copyright 2014 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include +#include "zstring.h" +#include "tarray.h" +#include "v_text.h" + +enum EAddType +{ + NEWLINE, + APPENDLINE, + REPLACELINE +}; + +class FConsoleBuffer +{ + TArray mConsoleText; + TArray mBrokenConsoleText; // This holds the structures returned by V_BreakLines and is used for memory management. + TArray mBrokenStart; + TArray mBrokenLines; // This holds the single lines, indexed by mBrokenStart and is used for printing. + FILE * mLogFile; + EAddType mAddType; + int mTextLines; + bool mBufferWasCleared; + + FFont *mLastFont; + int mLastDisplayWidth; + bool mLastLineNeedsUpdate; + + void WriteLineToLog(FILE *LogFile, const char *outline); + void FreeBrokenText(unsigned int start = 0, unsigned int end = INT_MAX); + + void Linefeed(FILE *Logfile); + +public: + FConsoleBuffer(); + ~FConsoleBuffer(); + void AddText(int printlevel, const char *string, FILE *logfile = NULL); + void AddMidText(const char *string, bool bold, FILE *logfile); + void FormatText(FFont *formatfont, int displaywidth); + void ResizeBuffer(unsigned newsize); + void WriteContentToLog(FILE *logfile); + void Clear() + { + mBufferWasCleared = true; + mConsoleText.Clear(); + } + int GetFormattedLineCount() { return mTextLines; } + FBrokenLines **GetLines() { return &mBrokenLines[0]; } +}; + diff --git a/src/nodebuild.cpp b/src/nodebuild.cpp index f3ceec17d9..210f0f3dea 100644 --- a/src/nodebuild.cpp +++ b/src/nodebuild.cpp @@ -125,12 +125,10 @@ void FNodeBuilder::BuildTree () { fixed_t bbox[4]; - C_InitTicker ("Building BSP", FRACUNIT); HackSeg = DWORD_MAX; HackMate = DWORD_MAX; CreateNode (0, Segs.Size(), bbox); CreateSubsectorsForReal (); - C_InitTicker (NULL, 0); } int FNodeBuilder::CreateNode (DWORD set, unsigned int count, fixed_t bbox[4]) @@ -199,10 +197,6 @@ int FNodeBuilder::CreateSubsector (DWORD set, fixed_t bbox[4]) } SegsStuffed += count; - if ((SegsStuffed & ~127) != ((SegsStuffed - count) & ~127)) - { - C_SetTicker (MulScale16 (SegsStuffed, (SDWORD)Segs.Size())); - } D(Printf (PRINT_LOG, "bbox (%d,%d)-(%d,%d)\n", bbox[BOXLEFT]>>16, bbox[BOXBOTTOM]>>16, bbox[BOXRIGHT]>>16, bbox[BOXTOP]>>16)); diff --git a/src/v_text.cpp b/src/v_text.cpp index c5208be36b..64b95c041f 100644 --- a/src/v_text.cpp +++ b/src/v_text.cpp @@ -336,7 +336,7 @@ static void breakit (FBrokenLines *line, FFont *font, const BYTE *start, const B line->Width = font->StringWidth (line->Text); } -FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *string) +FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *string, bool preservecolor) { FBrokenLines lines[128]; // Support up to 128 lines (should be plenty) @@ -397,7 +397,7 @@ FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *string) space = string - 1; breakit (&lines[i], font, start, space, linecolor); - if (c == '\n') + if (c == '\n' && !preservecolor) { lastcolor = ""; // Why, oh why, did I do it like this? } diff --git a/src/v_text.h b/src/v_text.h index 29dfa627b5..f1426b30ad 100644 --- a/src/v_text.h +++ b/src/v_text.h @@ -75,9 +75,9 @@ struct FBrokenLines #define TEXTCOLOR_CHAT "\034*" #define TEXTCOLOR_TEAMCHAT "\034!" -FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *str); +FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *str, bool preservecolor = false); void V_FreeBrokenLines (FBrokenLines *lines); -inline FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const char *str) - { return V_BreakLines (font, maxwidth, (const BYTE *)str); } +inline FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const char *str, bool preservecolor = false) + { return V_BreakLines (font, maxwidth, (const BYTE *)str, preservecolor); } #endif //__V_TEXT_H__ From b285cbebe4fb7f3a84240eabeb4276761565567b Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Sun, 25 May 2014 01:12:16 +0200 Subject: [PATCH 5/7] - Fixed compiler errors in latest TEXTURES code. --- src/r_data/sprites.cpp | 4 ++-- src/r_utility.cpp | 2 +- src/textures/animations.cpp | 4 ++-- src/textures/jpegtexture.cpp | 4 ++-- src/textures/multipatchtexture.cpp | 14 +++++++------- src/textures/texturemanager.cpp | 2 +- src/w_wad.h | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/r_data/sprites.cpp b/src/r_data/sprites.cpp index 6046194035..c1e59c415f 100644 --- a/src/r_data/sprites.cpp +++ b/src/r_data/sprites.cpp @@ -63,7 +63,7 @@ static bool R_InstallSpriteLump (FTextureID lump, unsigned frame, char rot, bool if (frame >= MAX_SPRITE_FRAMES || rotation > 16) { - Printf (TEXTCOLOR_RED"R_InstallSpriteLump: Bad frame characters in lump %s\n", TexMan[lump]->Name); + Printf (TEXTCOLOR_RED"R_InstallSpriteLump: Bad frame characters in lump %s\n", TexMan[lump]->Name.GetChars()); return false; } @@ -998,4 +998,4 @@ void R_DeinitSpriteData() delete[] skins; skins = NULL; } -} \ No newline at end of file +} diff --git a/src/r_utility.cpp b/src/r_utility.cpp index e11631700e..5bf38ad26e 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -972,7 +972,7 @@ void FCanvasTextureInfo::Add (AActor *viewpoint, FTextureID picnum, int fov) texture = static_cast(TexMan[picnum]); if (!texture->bHasCanvas) { - Printf ("%s is not a valid target for a camera\n", texture->Name); + Printf ("%s is not a valid target for a camera\n", texture->Name.GetChars()); return; } diff --git a/src/textures/animations.cpp b/src/textures/animations.cpp index 3601646a05..58ef7a5ca7 100644 --- a/src/textures/animations.cpp +++ b/src/textures/animations.cpp @@ -231,8 +231,8 @@ void FTextureManager::InitAnimated (void) if (debuganimated) { Printf("Defining animation '%s' (texture %d, lump %d, file %d) to '%s' (texture %d, lump %d, file %d)\n", - tex1->Name, pic1.GetIndex(), tex1->GetSourceLump(), Wads.GetLumpFile(tex1->GetSourceLump()), - tex2->Name, pic2.GetIndex(), tex2->GetSourceLump(), Wads.GetLumpFile(tex2->GetSourceLump())); + tex1->Name.GetChars(), pic1.GetIndex(), tex1->GetSourceLump(), Wads.GetLumpFile(tex1->GetSourceLump()), + tex2->Name.GetChars(), pic2.GetIndex(), tex2->GetSourceLump(), Wads.GetLumpFile(tex2->GetSourceLump())); } if (pic1 == pic2) diff --git a/src/textures/jpegtexture.cpp b/src/textures/jpegtexture.cpp index 3a87f70af8..849cec6458 100644 --- a/src/textures/jpegtexture.cpp +++ b/src/textures/jpegtexture.cpp @@ -448,7 +448,7 @@ void FJPEGTexture::MakeTexture () } catch (int) { - Printf (TEXTCOLOR_ORANGE " in texture %s\n", Name); + Printf (TEXTCOLOR_ORANGE " in texture %s\n", Name.GetChars()); jpeg_destroy_decompress(&cinfo); } if (buff != NULL) @@ -532,7 +532,7 @@ int FJPEGTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FC } catch(int) { - Printf (TEXTCOLOR_ORANGE " in JPEG texture %s\n", Name); + Printf (TEXTCOLOR_ORANGE " in JPEG texture %s\n", Name.GetChars()); } jpeg_destroy_decompress(&cinfo); if (buff != NULL) delete [] buff; diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp index 84e7385dbb..348ad3a989 100644 --- a/src/textures/multipatchtexture.cpp +++ b/src/textures/multipatchtexture.cpp @@ -268,14 +268,14 @@ FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchl if (unsigned(LittleShort(mpatch.d->patch)) >= unsigned(maxpatchnum)) { I_FatalError ("Bad PNAMES and/or texture directory:\n\nPNAMES has %d entries, but\n%s wants to use entry %d.", - maxpatchnum, Name, LittleShort(mpatch.d->patch)+1); + maxpatchnum, Name.GetChars(), LittleShort(mpatch.d->patch)+1); } Parts[i].OriginX = LittleShort(mpatch.d->originx); Parts[i].OriginY = LittleShort(mpatch.d->originy); Parts[i].Texture = patchlookup[LittleShort(mpatch.d->patch)].Texture; if (Parts[i].Texture == NULL) { - Printf(TEXTCOLOR_RED "Unknown patch %s in texture %s\n", patchlookup[LittleShort(mpatch.d->patch)].Name, Name); + Printf(TEXTCOLOR_RED "Unknown patch %s in texture %s\n", patchlookup[LittleShort(mpatch.d->patch)].Name.GetChars(), Name.GetChars()); NumParts--; i--; } @@ -290,7 +290,7 @@ FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchl } if (NumParts == 0) { - Printf ("Texture %s is left without any patches\n", Name); + Printf ("Texture %s is left without any patches\n", Name.GetChars()); } CheckForHacks (); @@ -1023,7 +1023,7 @@ void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part, bool silent, i } if (part.Texture == NULL) { - if (!silent) Printf(TEXTCOLOR_RED "Unknown patch '%s' in texture '%s'\n", sc.String, Name); + if (!silent) Printf(TEXTCOLOR_RED "Unknown patch '%s' in texture '%s'\n", sc.String, Name.GetChars()); } sc.MustGetStringName(","); sc.MustGetNumber(); @@ -1244,13 +1244,13 @@ FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) { sc.MustGetFloat(); xScale = FLOAT2FIXED(sc.Float); - if (xScale == 0) sc.ScriptError("Texture %s is defined with null x-scale\n", Name); + if (xScale == 0) sc.ScriptError("Texture %s is defined with null x-scale\n", Name.GetChars()); } else if (sc.Compare("YScale")) { sc.MustGetFloat(); yScale = FLOAT2FIXED(sc.Float); - if (yScale == 0) sc.ScriptError("Texture %s is defined with null y-scale\n", Name); + if (yScale == 0) sc.ScriptError("Texture %s is defined with null y-scale\n", Name.GetChars()); } else if (sc.Compare("WorldPanning")) { @@ -1318,7 +1318,7 @@ FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) if (Width <= 0 || Height <= 0) { UseType = FTexture::TEX_Null; - Printf("Texture %s has invalid dimensions (%d, %d)\n", Name, Width, Height); + Printf("Texture %s has invalid dimensions (%d, %d)\n", Name.GetChars(), Width, Height); Width = Height = 1; } CalcBitSize (); diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 2a10ce0df9..a79769c3d9 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -949,7 +949,7 @@ void FTextureManager::SortTexturesByType(int start, int end) { if (newtextures[j] != NULL) { - Printf("Texture %s has unknown type!\n", newtextures[j]->Name); + Printf("Texture %s has unknown type!\n", newtextures[j]->Name.GetChars()); AddTexture(newtextures[j]); } } diff --git a/src/w_wad.h b/src/w_wad.h index 0a95699f62..4dfe3434d1 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -196,8 +196,8 @@ public: int GetLumpOffset (int lump); // [RH] Returns offset of lump in the wadfile int GetLumpFlags (int lump); // Return the flags for this lump void GetLumpName (char *to, int lump) const; // [RH] Copies the lump name to to using uppercopy - void FWadCollection::GetLumpName(FString &to, int lump) const; - const char *GetLumpFullName(int lump) const; // [RH] Returns the lump's full name + void GetLumpName (FString &to, int lump) const; + const char *GetLumpFullName (int lump) const; // [RH] Returns the lump's full name FString GetLumpFullPath (int lump) const; // [RH] Returns wad's name + lump's full name int GetLumpFile (int lump) const; // [RH] Returns wadnum for a specified lump int GetLumpNamespace (int lump) const; // [RH] Returns the namespace a lump belongs to From 75cde0b2219fdd798eaa30e1d41ce33dd696b9f6 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 29 May 2014 17:30:01 +0200 Subject: [PATCH 6/7] - allow locks to check for a key's species so that newly defined keys can open previously defined locks without the need to redefine them. --- src/g_shared/a_keys.cpp | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/g_shared/a_keys.cpp b/src/g_shared/a_keys.cpp index e7f1b286b0..67d0ffb101 100644 --- a/src/g_shared/a_keys.cpp +++ b/src/g_shared/a_keys.cpp @@ -19,12 +19,29 @@ struct OneKey bool check(AActor * owner) { - // P_GetMapColorForKey() checks the key directly - if (owner->IsKindOf (RUNTIME_CLASS(AKey))) + if (owner->IsKindOf(RUNTIME_CLASS(AKey))) + { + // P_GetMapColorForKey() checks the key directly return owner->IsA(key); - // Other calls check an actor that may have a key in its inventory. - else - return !!owner->FindInventory(key); + } + else + { + // Other calls check an actor that may have a key in its inventory. + AInventory *item; + + for (item = owner->Inventory; item != NULL; item = item->Inventory) + { + if (item->IsA(key)) + { + return true; + } + else if (item->GetSpecies() == key->TypeName) + { + return true; + } + } + return false; + } } }; From 8f5683e23d61825e77b2c7b68312a01cf4354d61 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 29 May 2014 17:50:14 +0200 Subject: [PATCH 7/7] - moved secret found message to string table and removed the CVAR crutch that dates from a time when modifying string table content wasn't as easy as it is now. - added 'showsecretsector' CVAR to show the sector number with the secret found message. --- src/g_shared/a_pickups.cpp | 2 +- src/g_shared/a_secrettrigger.cpp | 4 +--- src/p_spec.cpp | 21 +++++++++++++++------ src/p_spec.h | 2 +- wadsrc/static/language.enu | 2 ++ 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index cc559ea94b..f14e7f4c70 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -1018,7 +1018,7 @@ void AInventory::Touch (AActor *toucher) if (flags5 & MF5_COUNTSECRET) { - P_GiveSecret(toucher, true, true); + P_GiveSecret(toucher, true, true, -1); } //Added by MC: Check if item taken was the roam destination of any bot diff --git a/src/g_shared/a_secrettrigger.cpp b/src/g_shared/a_secrettrigger.cpp index 4484340db6..24c6f0db4d 100644 --- a/src/g_shared/a_secrettrigger.cpp +++ b/src/g_shared/a_secrettrigger.cpp @@ -42,8 +42,6 @@ #include "v_font.h" #include "p_spec.h" -EXTERN_CVAR(String, secretmessage) - class ASecretTrigger : public AActor { DECLARE_CLASS (ASecretTrigger, AActor) @@ -62,7 +60,7 @@ void ASecretTrigger::PostBeginPlay () void ASecretTrigger::Activate (AActor *activator) { - P_GiveSecret(activator, args[0] <= 1, (args[0] == 0 || args[0] == 2)); + P_GiveSecret(activator, args[0] <= 1, (args[0] == 0 || args[0] == 2), -1); Destroy (); } diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 63f7c1c89e..e66dc2ab46 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -61,6 +61,7 @@ #include "a_sharedglobal.h" #include "farchive.h" #include "a_keys.h" +#include "c_dispatch.h" // State. #include "r_state.h" @@ -73,9 +74,6 @@ static FRandom pr_playerinspecialsector ("PlayerInSpecialSector"); void P_SetupPortals(); -// [GrafZahl] Make this message changable by the user! ;) -CVAR(String, secretmessage, "A Secret is revealed!", CVAR_ARCHIVE) - IMPLEMENT_POINTY_CLASS (DScroller) DECLARE_POINTER (m_Interpolations[0]) DECLARE_POINTER (m_Interpolations[1]) @@ -581,7 +579,7 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector) if (sector->special & SECRET_MASK) { sector->special &= ~SECRET_MASK; - P_GiveSecret(player->mo, true, true); + P_GiveSecret(player->mo, true, true, int(sector - sectors)); } } @@ -672,7 +670,9 @@ void P_SectorDamage(int tag, int amount, FName type, const PClass *protectClass, // //============================================================================ -void P_GiveSecret(AActor *actor, bool printmessage, bool playsound) +CVAR(Bool, showsecretsector, false, 0) + +void P_GiveSecret(AActor *actor, bool printmessage, bool playsound, int sectornum) { if (actor != NULL) { @@ -682,7 +682,16 @@ void P_GiveSecret(AActor *actor, bool printmessage, bool playsound) } if (actor->CheckLocalView (consoleplayer)) { - if (printmessage) C_MidPrint (SmallFont, secretmessage); + if (printmessage) + { + if (!showsecretsector || sectornum < 0) C_MidPrint(SmallFont, GStrings["SECRETMESSAGE"]); + else + { + FString s = GStrings["SECRETMESSAGE"]; + s.AppendFormat(" (Sector %d)", sectornum); + C_MidPrint(SmallFont, s); + } + } if (playsound) S_Sound (CHAN_AUTO | CHAN_UI, "misc/secret", 1, ATTN_NORM); } } diff --git a/src/p_spec.h b/src/p_spec.h index f8dad701b1..245c699d7b 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -172,7 +172,7 @@ void P_PlayerOnSpecialFlat (player_t *player, int floorType); void P_SectorDamage(int tag, int amount, FName type, const PClass *protectClass, int flags); void P_SetSectorFriction (int tag, int amount, bool alterFlag); -void P_GiveSecret(AActor *actor, bool printmessage, bool playsound); +void P_GiveSecret(AActor *actor, bool printmessage, bool playsound, int sectornum); // // getSide() diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 5695ad502c..76ba7c0d71 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2,6 +2,8 @@ [enu default] +SECRETMESSAGE = "A secret is revealed!"; + D_DEVSTR = "Useless mode ON.\n"; D_CDROM = "CD-ROM Version: zdoom.ini from c:\\zdoomdat\n"; PRESSKEY = "press a key.";