From 4e0810e8a33d06efbc73def0ed8a602e3e57d958 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 6 Jan 2020 18:22:23 -0300 Subject: [PATCH 001/136] Picture formats test --- src/CMakeLists.txt | 4 +- src/Makefile | 2 +- src/dehacked.c | 2 +- src/hardware/hw_cache.c | 2 +- src/hardware/hw_main.c | 2 +- src/lua_infolib.c | 2 +- src/p_setup.c | 12 +- src/r_data.c | 2 +- src/{r_patch.c => r_picformats.c} | 940 ++++++++++++++++--------- src/{r_patch.h => r_picformats.h} | 59 +- src/r_things.c | 2 +- src/r_things.h | 2 +- src/sdl/Srb2SDL-vc10.vcxproj | 4 +- src/sdl/Srb2SDL-vc10.vcxproj.filters | 4 +- src/win32/Srb2win-vc10.vcxproj | 4 +- src/win32/Srb2win-vc10.vcxproj.filters | 4 +- src/z_zone.c | 2 +- 17 files changed, 703 insertions(+), 346 deletions(-) rename src/{r_patch.c => r_picformats.c} (67%) rename src/{r_patch.h => r_picformats.h} (62%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e8c9c3182..7a6e7871a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -123,7 +123,7 @@ set(SRB2_CORE_RENDER_SOURCES r_sky.c r_splats.c r_things.c - r_patch.c + r_picformats.c r_portal.c r_bsp.h @@ -138,7 +138,7 @@ set(SRB2_CORE_RENDER_SOURCES r_splats.h r_state.h r_things.h - r_patch.h + r_picformats.h r_portal.h ) diff --git a/src/Makefile b/src/Makefile index c30c236de..282ff8f4f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -471,7 +471,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/r_sky.o \ $(OBJDIR)/r_splats.o \ $(OBJDIR)/r_things.o \ - $(OBJDIR)/r_patch.o \ + $(OBJDIR)/r_picformats.o \ $(OBJDIR)/r_portal.o \ $(OBJDIR)/screen.o \ $(OBJDIR)/v_video.o \ diff --git a/src/dehacked.c b/src/dehacked.c index 78be8b0b3..81690cde9 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -29,7 +29,7 @@ #include "p_setup.h" #include "r_data.h" #include "r_draw.h" -#include "r_patch.h" +#include "r_picformats.h" #include "r_sky.h" #include "fastcmp.h" #include "lua_script.h" diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index c47833187..90310a92f 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -30,7 +30,7 @@ #include "../z_zone.h" #include "../v_video.h" #include "../r_draw.h" -#include "../r_patch.h" +#include "../r_picformats.h" #include "../p_setup.h" // Values set after a call to HWR_ResizeBlock() diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index e8d16c092..23e52d19b 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -30,7 +30,7 @@ #include "../p_local.h" #include "../p_setup.h" #include "../r_local.h" -#include "../r_patch.h" +#include "../r_picformats.h" #include "../r_bsp.h" #include "../d_clisrv.h" #include "../w_wad.h" diff --git a/src/lua_infolib.c b/src/lua_infolib.c index b617a7a14..39c385102 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -18,7 +18,7 @@ #include "p_mobj.h" #include "p_local.h" #include "z_zone.h" -#include "r_patch.h" +#include "r_picformats.h" #include "r_things.h" #include "doomstat.h" // luabanks[] diff --git a/src/p_setup.c b/src/p_setup.c index 42a6438a0..eca5413b4 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -28,7 +28,7 @@ #include "r_data.h" #include "r_things.h" -#include "r_patch.h" +#include "r_picformats.h" #include "r_sky.h" #include "r_draw.h" @@ -574,6 +574,8 @@ Ploadflat (levelflat_t *levelflat, const char *flatname) lumpnum_t flatnum; int texturenum; + patch_t *flatpatch; + size_t lumplength; size_t i; @@ -635,7 +637,9 @@ texturefound: { flatfound: /* This could be a flat, patch, or PNG. */ - if (R_CheckIfPatch(flatnum)) + flatpatch = W_CacheLumpNum(flatnum, PU_STATIC); + lumplength = W_LumpLength(flatnum); + if (R_CheckIfPatch(flatpatch, lumplength)) levelflat->type = LEVELFLAT_PATCH; else { @@ -644,8 +648,10 @@ flatfound: Only need eight bytes for PNG headers. FIXME: Put this elsewhere. */ + if (flatpatch) + Z_Free(flatpatch); W_ReadLumpHeader(flatnum, buffer, 8, 0); - if (R_IsLumpPNG(buffer, W_LumpLength(flatnum))) + if (R_IsLumpPNG(buffer, lumplength)) levelflat->type = LEVELFLAT_PNG; else #endif/*NO_PNG_LUMPS*/ diff --git a/src/r_data.c b/src/r_data.c index 986b65dea..14c04f15e 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -19,7 +19,7 @@ #include "p_local.h" #include "m_misc.h" #include "r_data.h" -#include "r_patch.h" +#include "r_picformats.h" #include "w_wad.h" #include "z_zone.h" #include "p_setup.h" // levelflats diff --git a/src/r_patch.c b/src/r_picformats.c similarity index 67% rename from src/r_patch.c rename to src/r_picformats.c index 18dfe523a..8e229c4a6 100644 --- a/src/r_patch.c +++ b/src/r_picformats.c @@ -2,23 +2,24 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 2005-2009 by Andrey "entryway" Budko. -// Copyright (C) 2018-2019 by Jaime "Lactozilla" Passos. -// Copyright (C) 2019 by Sonic Team Junior. +// Copyright (C) 2018-2020 by Jaime "Lactozilla" Passos. +// Copyright (C) 2019-2020 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file r_patch.c -/// \brief Patch generation. +/// \file r_picformats.c +/// \brief Picture generation. #include "byteptr.h" #include "dehacked.h" #include "i_video.h" #include "r_data.h" #include "r_draw.h" -#include "r_patch.h" +#include "r_picformats.h" #include "r_things.h" +#include "v_video.h" #include "z_zone.h" #include "w_wad.h" @@ -52,29 +53,549 @@ static unsigned char imgbuf[1<<26]; fixed_t cosang2rad[ROTANGLES]; fixed_t sinang2rad[ROTANGLES]; -// -// R_CheckIfPatch -// -// Returns true if the lump is a valid patch. -// -boolean R_CheckIfPatch(lumpnum_t lump) +/** Converts a picture between two formats. + * + * \param informat Input picture format. + * \param picture Input picture data. + * \param outformat Output picture format. + * \param insize Input picture size. + * \param outsize Output picture size. + * \param inwidth Input picture width. + * \param inheight Input picture height. + * \param inleftoffset Input picture left offset, for patches. + * \param intopoffset Input picture top offset, for patches. + * \param flags Input picture flags. + * \return A pointer to the converted picture. + */ +void *Picture_Convert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT32 inwidth, INT32 inheight, INT32 inleftoffset, INT32 intopoffset, + pictureflags_t flags) { - size_t size; - INT16 width, height; - patch_t *patch; - boolean result; + if (informat == outformat) // wut? + I_Error("Picture_Convert: input and output formats are the same!"); - size = W_LumpLength(lump); + if (Picture_IsPatchFormat(outformat)) + return Picture_PatchConvert(informat, picture, outformat, insize, outsize, inwidth, inheight, inleftoffset, intopoffset, flags); + else if (Picture_IsFlatFormat(outformat)) + return Picture_FlatConvert(informat, picture, outformat, insize, outsize, inwidth, inheight, inleftoffset, intopoffset, flags); + else + I_Error("Picture_Convert: unsupported input format!"); + + return NULL; +} + +/** Converts a picture to a patch. + * + * \param informat Input picture format. + * \param picture Input picture data. + * \param outformat Output picture format. + * \param insize Input picture size. + * \param outsize Output picture size. + * \param inwidth Input picture width. + * \param inheight Input picture height. + * \param inleftoffset Input picture left offset, for patches. + * \param intopoffset Input picture top offset, for patches. + * \param flags Input picture flags. + * \return A pointer to the converted picture. + */ +void *Picture_PatchConvert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset, + pictureflags_t flags) +{ + UINT32 x, y; + UINT8 *img; + UINT8 *imgptr = imgbuf; + UINT8 *colpointers, *startofspan; + size_t size = 0; + patch_t *inpatch = NULL; + INT32 inbpp = Picture_FormatBPP(informat); + + if (informat == outformat) + I_Error("Picture_PatchConvert: input and output formats are the same!"); + + if (!inbpp) + I_Error("Picture_PatchConvert: unknown input bits per pixel?!"); + + (void)insize; // ignore + + // If it's a patch, you can just figure out + // the dimensions from the header. + if (Picture_IsPatchFormat(informat)) + { + inpatch = (patch_t *)picture; + inwidth = SHORT(inpatch->width); + inheight = SHORT(inpatch->height); + inleftoffset = SHORT(inpatch->leftoffset); + intopoffset = SHORT(inpatch->topoffset); + } + + // Write image size and offset + WRITEINT16(imgptr, inwidth); + WRITEINT16(imgptr, inheight); + WRITEINT16(imgptr, inleftoffset); + WRITEINT16(imgptr, intopoffset); + + // Leave placeholder to column pointers + colpointers = imgptr; + imgptr += inwidth*4; + + // Write columns + for (x = 0; x < inwidth; x++) + { + int lastStartY = 0; + int spanSize = 0; + startofspan = NULL; + + // Write column pointer + WRITEINT32(colpointers, imgptr - imgbuf); + + // Write pixels + for (y = 0; y < inheight; y++) + { + void *input = NULL; + boolean opaque = false; + + // Read pixel + if (Picture_IsPatchFormat(informat)) + input = Picture_GetPatchPixel(inpatch, informat, x, y, flags); + else if (Picture_IsFlatFormat(informat)) + { + size_t offs = ((y * inwidth) + x); + switch (informat) + { + case PICFMT_FLAT32: + input = picture + (offs * 4); + break; + case PICFMT_FLAT16: + input = picture + (offs * 2); + break; + case PICFMT_FLAT: + input = picture + offs; + break; + default: + I_Error("Picture_PatchConvert: unsupported flat input format!"); + break; + } + } + else + I_Error("Picture_PatchConvert: unsupported input format!"); + + // Determine opacity + if (input != NULL) + { + UINT8 alpha = 0xFF; + if (inbpp == 32) + { + RGBA_t px = *(RGBA_t *)input; + alpha = px.s.alpha; + } + else if (inbpp == 16) + { + UINT16 px = *(UINT16 *)input; + alpha = (px & 0xFF00) >> 8; + } + else if (inbpp == 8) + { + UINT8 px = *(UINT8 *)input; + if (px == TRANSPARENTPIXEL) + alpha = 0; + } + opaque = (alpha > 1); + } + + // End span if we have a transparent pixel + if (!opaque) + { + if (startofspan) + WRITEUINT8(imgptr, 0); + startofspan = NULL; + continue; + } + + // Start new column if we need to + if (!startofspan || spanSize == 255) + { + int writeY = y; + + // If we reached the span size limit, finish the previous span + if (startofspan) + WRITEUINT8(imgptr, 0); + + if (y > 254) + { + // Make sure we're aligned to 254 + if (lastStartY < 254) + { + WRITEUINT8(imgptr, 254); + WRITEUINT8(imgptr, 0); + imgptr += 2; + lastStartY = 254; + } + + // Write stopgap empty spans if needed + writeY = y - lastStartY; + + while (writeY > 254) + { + WRITEUINT8(imgptr, 254); + WRITEUINT8(imgptr, 0); + imgptr += 2; + writeY -= 254; + } + } + + startofspan = imgptr; + WRITEUINT8(imgptr, writeY); + imgptr += 2; + spanSize = 0; + + lastStartY = y; + } + + // Write the pixel + switch (outformat) + { + case PICFMT_PATCH32: + { + if (inbpp == 32) + { + RGBA_t out = *(RGBA_t *)input; + WRITEUINT32(imgptr, out.rgba); + } + else if (inbpp == 16) + { + RGBA_t out = pMasterPalette[*((UINT16 *)input) & 0xFF]; + WRITEUINT32(imgptr, out.rgba); + } + else // PICFMT_PATCH + { + RGBA_t out = pMasterPalette[*((UINT8 *)input) & 0xFF]; + WRITEUINT32(imgptr, out.rgba); + } + break; + } + case PICFMT_PATCH16: + if (inbpp == 32) + { + RGBA_t in = *(RGBA_t *)input; + UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); + WRITEUINT16(imgptr, (0xFF00 | out)); + } + else if (inbpp == 16) + WRITEUINT16(imgptr, *(UINT16 *)input); + else // PICFMT_PATCH + WRITEUINT16(imgptr, (0xFF00 | (*(UINT8 *)input))); + break; + default: // PICFMT_PATCH + { + if (inbpp == 32) + { + RGBA_t in = *(RGBA_t *)input; + UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); + WRITEUINT8(imgptr, out); + } + else if (inbpp == 16) + { + UINT16 out = *(UINT16 *)input; + WRITEUINT8(imgptr, (out & 0xFF)); + } + else // PICFMT_PATCH + WRITEUINT8(imgptr, *(UINT8 *)input); + break; + } + } + + spanSize++; + startofspan[1] = spanSize; + } + + if (startofspan) + WRITEUINT8(imgptr, 0); + + WRITEUINT8(imgptr, 0xFF); + } + + size = imgptr-imgbuf; + img = Z_Malloc(size, PU_STATIC, NULL); + memcpy(img, imgbuf, size); + + if (outsize != NULL) + *outsize = size; + return img; +} + +/** Converts a picture to a flat. + * + * \param informat Input picture format. + * \param picture Input picture data. + * \param outformat Output picture format. + * \param insize Input picture size. + * \param outsize Output picture size. + * \param inwidth Input picture width. + * \param inheight Input picture height. + * \param inleftoffset Input picture left offset, for patches. + * \param intopoffset Input picture top offset, for patches. + * \param flags Input picture flags. + * \return A pointer to the converted picture. + */ +void *Picture_FlatConvert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset, + pictureflags_t flags) +{ + void *outflat; + patch_t *inpatch = NULL; + INT32 inbpp = Picture_FormatBPP(informat); + INT32 outbpp = Picture_FormatBPP(outformat); + INT32 x, y; + size_t size; + + if (informat == outformat) + I_Error("Picture_FlatConvert: input and output formats are the same!"); + + if (!inbpp) + I_Error("Picture_FlatConvert: unknown input bits per pixel?!"); + if (!outbpp) + I_Error("Picture_FlatConvert: unknown output bits per pixel?!"); + + // If it's a patch, you can just figure out + // the dimensions from the header. + if (Picture_IsPatchFormat(informat)) + { + inpatch = (patch_t *)picture; + inwidth = SHORT(inpatch->width); + inheight = SHORT(inpatch->height); + inleftoffset = SHORT(inpatch->leftoffset); + intopoffset = SHORT(inpatch->topoffset); + } + + size = (inwidth * inheight) * (outbpp / 8); + outflat = Z_Calloc(size, PU_STATIC, NULL); + if (outsize) + *outsize = size; + + // Set transparency + if (outbpp == 8) + memset(outflat, TRANSPARENTPIXEL, size); + + for (y = 0; y < inheight; y++) + for (x = 0; x < inwidth; x++) + { + void *input; + size_t offs = ((y * inwidth) + x); + + // Read pixel + if (Picture_IsPatchFormat(informat)) + input = Picture_GetPatchPixel(inpatch, informat, x, y, flags); + else if (Picture_IsFlatFormat(informat)) + input = picture + (offs * (inbpp / 8)); + else + I_Error("Picture_FlatConvert: unsupported input format!"); + + if (!input) + continue; + + switch (outformat) + { + case PICFMT_FLAT32: + { + UINT32 *f32 = (UINT32 *)outflat; + if (inbpp == 32) + { + RGBA_t out = *(RGBA_t *)input; + f32[offs] = out.rgba; + } + else if (inbpp == 16) + { + RGBA_t out = pMasterPalette[*((UINT16 *)input) & 0xFF]; + f32[offs] = out.rgba; + } + else // PICFMT_PATCH + { + RGBA_t out = pMasterPalette[*((UINT8 *)input) & 0xFF]; + f32[offs] = out.rgba; + } + break; + } + case PICFMT_FLAT16: + { + UINT16 *f16 = (UINT16 *)outflat; + if (inbpp == 32) + { + RGBA_t in = *(RGBA_t *)input; + UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); + f16[offs] = (0xFF00 | out); + } + else if (inbpp == 16) + f16[offs] = *(UINT16 *)input; + else // PICFMT_PATCH + f16[offs] = (0xFF00 | *((UINT8 *)input)); + break; + } + case PICFMT_FLAT: + { + UINT8 *f8 = (UINT8 *)outflat; + if (inbpp == 32) + { + RGBA_t in = *(RGBA_t *)input; + UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); + f8[offs] = out; + } + else if (inbpp == 16) + { + UINT16 out = *(UINT16 *)input; + f8[offs] = (out & 0xFF); + } + else // PICFMT_PATCH + f8[offs] = *(UINT8 *)input; + break; + } + default: + I_Error("Picture_FlatConvert: unsupported output format!"); + } + } + + return outflat; +} + +/** Returns a pixel from a patch. + * + * \param patch Input patch. + * \param informat Input picture format. + * \param x Pixel X position. + * \param y Pixel Y position. + * \param flags Input picture flags. + * \return A pointer to a pixel in the patch. Returns NULL if not opaque. + */ +void *Picture_GetPatchPixel( + patch_t *patch, pictureformat_t informat, + INT32 x, INT32 y, + pictureflags_t flags) +{ + fixed_t ofs; + column_t *column; + UINT8 *s8; + UINT16 *s16; + UINT32 *s32; + + if (patch == NULL) + I_Error("Picture_GetPatchPixel: patch == NULL"); + + if (x >= 0 && x < SHORT(patch->width)) + { + INT32 topdelta, prevdelta = -1; + INT32 columnofs = 0; + + if (flags & PICFLAGS_XFLIP) + columnofs = LONG(patch->columnofs[(SHORT(patch->width)-1)-x]); + else + columnofs = LONG(patch->columnofs[x]); + + // Column offsets are pointers so no casting required + column = (column_t *)((UINT8 *)patch + columnofs); + + while (column->topdelta != 0xff) + { + topdelta = column->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + s8 = (UINT8 *)(column) + 3; + if (informat == PICFMT_PATCH32) + s32 = (UINT32 *)s8; + else if (informat == PICFMT_PATCH16) + s16 = (UINT16 *)s8; + for (ofs = 0; ofs < column->length; ofs++) + { + if ((topdelta + ofs) == y) + { + if (informat == PICFMT_PATCH32) + return (s32 + ofs); + else if (informat == PICFMT_PATCH16) + return (s16 + ofs); + else // PICFMT_PATCH + return (s8 + ofs); + } + } + column = (column_t *)((UINT8 *)column + column->length + 4); + } + } + + return NULL; +} + +/** Returns the amount of bits per pixel in the specified picture format. + * + * \param format Input picture format. + * \return The bits per pixel amount of the picture format. + */ +INT32 Picture_FormatBPP(pictureformat_t format) +{ + INT32 bpp = 0; + switch (format) + { + case PICFMT_PATCH32: + case PICFMT_FLAT32: + case PICFMT_PNG: + bpp = 32; + break; + case PICFMT_PATCH16: + case PICFMT_FLAT16: + bpp = 16; + break; + case PICFMT_PATCH: + case PICFMT_FLAT: + bpp = 8; + break; + default: + break; + } + return bpp; +} + +/** Checks if the specified picture format is a patch. + * + * \param format Input picture format. + * \return True if the picture format is a patch, false if not. + */ +boolean Picture_IsPatchFormat(pictureformat_t format) +{ + return (format == PICFMT_PATCH || format == PICFMT_PATCH16 || format == PICFMT_PATCH32); +} + +/** Checks if the specified picture format is a flat. + * + * \param format Input picture format. + * \return True if the picture format is a flat, false if not. + */ +boolean Picture_IsFlatFormat(pictureformat_t format) +{ + return (format == PICFMT_FLAT || format == PICFMT_FLAT16 || format == PICFMT_FLAT32); +} + +/** Returns true if the lump is a valid patch. + * PICFMT_PATCH only, I think?? + * + * \param patch Input patch. + * \param picture Input patch size. + * \return True if the input patch is valid. + */ +boolean R_CheckIfPatch(patch_t *patch, size_t size) +{ + INT16 width, height; + boolean result; // minimum length of a valid Doom patch if (size < 13) return false; - patch = (patch_t *)W_CacheLumpNum(lump, PU_STATIC); - width = SHORT(patch->width); height = SHORT(patch->height); - result = (height > 0 && height <= 16384 && width > 0 && width <= 16384 && width < (INT16)(size / 4)); if (result) @@ -168,36 +689,10 @@ void R_TextureToFlat(size_t tex, UINT8 *flat) // void R_PatchToFlat(patch_t *patch, UINT8 *flat) { - fixed_t col, ofs; - column_t *column; - UINT8 *desttop, *dest, *deststop; - UINT8 *source; - - desttop = flat; - deststop = desttop + (SHORT(patch->width) * SHORT(patch->height)); - - for (col = 0; col < SHORT(patch->width); col++, desttop++) - { - INT32 topdelta, prevdelta = -1; - column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[col])); - - while (column->topdelta != 0xff) - { - topdelta = column->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - - dest = desttop + (topdelta * SHORT(patch->width)); - source = (UINT8 *)(column) + 3; - for (ofs = 0; dest < deststop && ofs < column->length; ofs++) - { - *dest = source[ofs]; - dest += SHORT(patch->width); - } - column = (column_t *)((UINT8 *)column + column->length + 4); - } - } + size_t outsize = 0; + UINT8 *converted = Picture_FlatConvert(PICFMT_PATCH, patch, PICFMT_FLAT, 0, &outsize, 0, 0, 0, 0, 0); + M_Memcpy(flat, converted, outsize); + Z_Free(converted); } // @@ -207,34 +702,10 @@ void R_PatchToFlat(patch_t *patch, UINT8 *flat) // void R_PatchToFlat_16bpp(patch_t *patch, UINT16 *raw, boolean flip) { - fixed_t col, ofs; - column_t *column; - UINT16 *desttop, *dest, *deststop; - UINT8 *source; - - desttop = raw; - deststop = desttop + (SHORT(patch->width) * SHORT(patch->height)); - - for (col = 0; col < SHORT(patch->width); col++, desttop++) - { - INT32 topdelta, prevdelta = -1; - column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[flip ? (patch->width-1-col) : col])); - while (column->topdelta != 0xff) - { - topdelta = column->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - dest = desttop + (topdelta * SHORT(patch->width)); - source = (UINT8 *)(column) + 3; - for (ofs = 0; dest < deststop && ofs < column->length; ofs++) - { - *dest = source[ofs]; - dest += SHORT(patch->width); - } - column = (column_t *)((UINT8 *)column + column->length + 4); - } - } + size_t outsize = 0; + UINT16 *converted = Picture_FlatConvert(PICFMT_PATCH, patch, PICFMT_FLAT16, 0, &outsize, 0, 0, 0, 0, (flip) ? PICFLAGS_XFLIP : 0); + M_Memcpy(raw, converted, outsize); + Z_Free(converted); } // @@ -244,108 +715,8 @@ void R_PatchToFlat_16bpp(patch_t *patch, UINT16 *raw, boolean flip) // patch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency) { - UINT32 x, y; - UINT8 *img; - UINT8 *imgptr = imgbuf; - UINT8 *colpointers, *startofspan; - size_t size = 0; - - // Write image size and offset - WRITEINT16(imgptr, width); - WRITEINT16(imgptr, height); - WRITEINT16(imgptr, leftoffset); - WRITEINT16(imgptr, topoffset); - - // Leave placeholder to column pointers - colpointers = imgptr; - imgptr += width*4; - - // Write columns - for (x = 0; x < width; x++) - { - int lastStartY = 0; - int spanSize = 0; - startofspan = NULL; - - // Write column pointer - WRITEINT32(colpointers, imgptr - imgbuf); - - // Write pixels - for (y = 0; y < height; y++) - { - UINT8 paletteIndex = raw[((y * width) + x)]; - boolean opaque = transparency ? (paletteIndex != TRANSPARENTPIXEL) : true; - - // End span if we have a transparent pixel - if (!opaque) - { - if (startofspan) - WRITEUINT8(imgptr, 0); - startofspan = NULL; - continue; - } - - // Start new column if we need to - if (!startofspan || spanSize == 255) - { - int writeY = y; - - // If we reached the span size limit, finish the previous span - if (startofspan) - WRITEUINT8(imgptr, 0); - - if (y > 254) - { - // Make sure we're aligned to 254 - if (lastStartY < 254) - { - WRITEUINT8(imgptr, 254); - WRITEUINT8(imgptr, 0); - imgptr += 2; - lastStartY = 254; - } - - // Write stopgap empty spans if needed - writeY = y - lastStartY; - - while (writeY > 254) - { - WRITEUINT8(imgptr, 254); - WRITEUINT8(imgptr, 0); - imgptr += 2; - writeY -= 254; - } - } - - startofspan = imgptr; - WRITEUINT8(imgptr, writeY); - imgptr += 2; - spanSize = 0; - - lastStartY = y; - } - - // Write the pixel - WRITEUINT8(imgptr, paletteIndex); - spanSize++; - startofspan[1] = spanSize; - } - - if (startofspan) - WRITEUINT8(imgptr, 0); - - WRITEUINT8(imgptr, 0xFF); - } - - size = imgptr-imgbuf; - img = Z_Malloc(size, PU_STATIC, NULL); - memcpy(img, imgbuf, size); - - Z_Free(raw); - - if (destsize != NULL) - *destsize = size; - return (patch_t *)img; + (void)transparency; + return (patch_t *)Picture_Convert(PICFMT_FLAT, raw, PICFMT_PATCH, 0, destsize, width, height, leftoffset, topoffset, 0); } // @@ -355,107 +726,7 @@ patch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffse // patch_t *R_FlatToPatch_16bpp(UINT16 *raw, UINT16 width, UINT16 height, size_t *size) { - UINT32 x, y; - UINT8 *img; - UINT8 *imgptr = imgbuf; - UINT8 *colpointers, *startofspan; - - if (!raw) - return NULL; - - // Write image size and offset - WRITEINT16(imgptr, width); - WRITEINT16(imgptr, height); - // no offsets - WRITEINT16(imgptr, 0); - WRITEINT16(imgptr, 0); - - // Leave placeholder to column pointers - colpointers = imgptr; - imgptr += width*4; - - // Write columns - for (x = 0; x < width; x++) - { - int lastStartY = 0; - int spanSize = 0; - startofspan = NULL; - - // Write column pointer - WRITEINT32(colpointers, imgptr - imgbuf); - - // Write pixels - for (y = 0; y < height; y++) - { - UINT16 pixel = raw[((y * width) + x)]; - UINT8 paletteIndex = (pixel & 0xFF); - UINT8 opaque = (pixel != 0xFF00); // If 1, we have a pixel - - // End span if we have a transparent pixel - if (!opaque) - { - if (startofspan) - WRITEUINT8(imgptr, 0); - startofspan = NULL; - continue; - } - - // Start new column if we need to - if (!startofspan || spanSize == 255) - { - int writeY = y; - - // If we reached the span size limit, finish the previous span - if (startofspan) - WRITEUINT8(imgptr, 0); - - if (y > 254) - { - // Make sure we're aligned to 254 - if (lastStartY < 254) - { - WRITEUINT8(imgptr, 254); - WRITEUINT8(imgptr, 0); - imgptr += 2; - lastStartY = 254; - } - - // Write stopgap empty spans if needed - writeY = y - lastStartY; - - while (writeY > 254) - { - WRITEUINT8(imgptr, 254); - WRITEUINT8(imgptr, 0); - imgptr += 2; - writeY -= 254; - } - } - - startofspan = imgptr; - WRITEUINT8(imgptr, writeY); - imgptr += 2; - spanSize = 0; - - lastStartY = y; - } - - // Write the pixel - WRITEUINT8(imgptr, paletteIndex); - spanSize++; - startofspan[1] = spanSize; - } - - if (startofspan) - WRITEUINT8(imgptr, 0); - - WRITEUINT8(imgptr, 0xFF); - } - - *size = imgptr-imgbuf; - img = Z_Malloc(*size, PU_STATIC, NULL); - memcpy(img, imgbuf, *size); - return (patch_t *)img; + return (patch_t *)Picture_Convert(PICFMT_FLAT16, raw, PICFMT_PATCH, 0, size, width, height, 0, 0, 0); } // @@ -649,19 +920,24 @@ static png_bytep *PNG_Read(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoff } // Convert a PNG to a raw image. -static UINT8 *PNG_RawConvert(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) +static void *PNG_Convert(const UINT8 *png, INT32 outbpp, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) { - UINT8 *flat; + void *flat; png_uint_32 x, y; png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, size); png_uint_32 width = *w, height = *h; - if (!row_pointers) - I_Error("PNG_RawConvert: conversion failed"); + if (!outbpp) + I_Error("PNG_Convert: unknown output bits per pixel?!"); + + if (!row_pointers) + I_Error("PNG_Convert: conversion failed!"); + + // Convert the image + flat = Z_Malloc((width * height) * (outbpp / 8), PU_STATIC, NULL); + if (outbpp == 8) + memset(flat, TRANSPARENTPIXEL, (width * height)); - // Convert the image to 8bpp - flat = Z_Malloc(width * height, PU_LEVEL, NULL); - memset(flat, TRANSPARENTPIXEL, width * height); for (y = 0; y < height; y++) { png_bytep row = row_pointers[y]; @@ -669,7 +945,36 @@ static UINT8 *PNG_RawConvert(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topo { png_bytep px = &(row[x * 4]); if ((UINT8)px[3]) - flat[((y * width) + x)] = NearestColor((UINT8)px[0], (UINT8)px[1], (UINT8)px[2]); + { + UINT8 red = (UINT8)px[0]; + UINT8 green = (UINT8)px[1]; + UINT8 blue = (UINT8)px[2]; + UINT8 alpha = (UINT8)px[3]; + if (outbpp == 32) + { + UINT32 *outflat = (UINT32 *)flat; + RGBA_t out; + out.s.red = red; + out.s.green = green; + out.s.blue = blue; + out.s.alpha = alpha; + outflat[((y * width) + x)] = out.rgba; + } + else + { + UINT8 palidx = NearestColor(red, green, blue); + if (outbpp == 16) + { + UINT16 *outflat = (UINT16 *)flat; + outflat[((y * width) + x)] = (alpha << 8) | palidx; + } + else // 8bpp + { + UINT8 *outflat = (UINT8 *)flat; + outflat[((y * width) + x)] = palidx; + } + } + } } } free(row_pointers); @@ -684,7 +989,7 @@ static UINT8 *PNG_RawConvert(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topo // UINT8 *R_PNGToFlat(UINT16 *width, UINT16 *height, UINT8 *png, size_t size) { - return PNG_RawConvert(png, width, height, NULL, NULL, size); + return PNG_Convert(png, 8, width, height, NULL, NULL, size); } // @@ -696,12 +1001,16 @@ patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean t { UINT16 width, height; INT16 topoffset = 0, leftoffset = 0; - UINT8 *raw = PNG_RawConvert(png, &width, &height, &topoffset, &leftoffset, size); + UINT8 *raw = PNG_Convert(png, 32, &width, &height, &topoffset, &leftoffset, size); + patch_t *output; + (void)transparency; if (!raw) I_Error("R_PNGToPatch: conversion failed"); - return R_FlatToPatch(raw, width, height, leftoffset, topoffset, destsize, transparency); + output = Picture_Convert(PICFMT_FLAT32, raw, PICFMT_PATCH, 0, destsize, width, height, leftoffset, topoffset, 0); + Z_Free(raw); + return output; } // @@ -1110,13 +1419,12 @@ void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps) // void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, UINT8 flip) { - UINT32 i; INT32 angle; patch_t *patch; patch_t *newpatch; - UINT16 *rawsrc, *rawdst; - size_t size, size2; - INT32 bflip = (flip != 0x00); + UINT16 *rawdst; + size_t size; + pictureflags_t bflip = (flip) ? PICFLAGS_XFLIP : 0; #define SPRITE_XCENTER (leftoffset) #define SPRITE_YCENTER (height / 2) @@ -1130,14 +1438,18 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp INT32 width, height, leftoffset; fixed_t ca, sa; lumpnum_t lump = sprframe->lumppat[rot]; + size_t lumplength; if (lump == LUMPERROR) return; - // Because there's something wrong with SPR_DFLM, I guess - if (!R_CheckIfPatch(lump)) - return; patch = (patch_t *)W_CacheLumpNum(lump, PU_STATIC); + lumplength = W_LumpLength(lump); + + // Because there's something wrong with SPR_DFLM, I guess + if (!R_CheckIfPatch(patch, lumplength)) + return; + width = patch->width; height = patch->height; leftoffset = patch->leftoffset; @@ -1160,16 +1472,6 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp leftoffset = width - leftoffset; } - // Draw the sprite to a temporary buffer. - size = (width*height); - rawsrc = Z_Malloc(size * sizeof(UINT16), PU_STATIC, NULL); - - // can't memset here - for (i = 0; i < size; i++) - rawsrc[i] = 0xFF00; - - R_PatchToFlat_16bpp(patch, rawsrc, bflip); - // Don't cache angle = 0 for (angle = 1; angle < ROTANGLES; angle++) { @@ -1237,17 +1539,12 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp #undef BOUNDARYADJUST } - size2 = (newwidth * newheight); - if (!size2) - size2 = size; - - rawdst = Z_Malloc(size2 * sizeof(UINT16), PU_STATIC, NULL); - - // can't memset here - for (i = 0; i < size2; i++) - rawdst[i] = 0xFF00; - // Draw the rotated sprite to a temporary buffer. + size = (newwidth * newheight); + if (!size) + size = (width * height); + rawdst = Z_Calloc(size * sizeof(UINT16), PU_STATIC, NULL); + for (dy = 0; dy < newheight; dy++) { for (dx = 0; dx < newwidth; dx++) @@ -1259,7 +1556,11 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp sx >>= FRACBITS; sy >>= FRACBITS; if (sx >= 0 && sy >= 0 && sx < width && sy < height) - rawdst[(dy*newwidth)+dx] = rawsrc[(sy*width)+sx]; + { + void *input = Picture_GetPatchPixel(patch, PICFMT_PATCH, sx, sy, bflip); + if (input != NULL) + rawdst[(dy*newwidth)+dx] = *(UINT16 *)input; + } } } @@ -1296,7 +1597,6 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp sprframe->rotsprite.cached[rot] = true; // free image data - Z_Free(rawsrc); Z_Free(patch); } #undef SPRITE_XCENTER diff --git a/src/r_patch.h b/src/r_picformats.h similarity index 62% rename from src/r_patch.h rename to src/r_picformats.h index 53d306b88..cdc64a674 100644 --- a/src/r_patch.h +++ b/src/r_picformats.h @@ -1,14 +1,14 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. -// Copyright (C) 2018-2019 by Jaime "Lactozilla" Passos. -// Copyright (C) 2019 by Sonic Team Junior. +// Copyright (C) 2018-2020 by Jaime "Lactozilla" Passos. +// Copyright (C) 2019-2020 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file r_patch.h +/// \file r_picformats.h /// \brief Patch generation. #ifndef __R_PATCH__ @@ -17,6 +17,57 @@ #include "r_defs.h" #include "doomdef.h" +typedef enum +{ + PICFMT_NONE = 0, + + // Doom formats + PICFMT_PATCH, + PICFMT_FLAT, + + // PNG + PICFMT_PNG, + + // 16bpp + PICFMT_PATCH16, + PICFMT_FLAT16, + + // 32bpp + PICFMT_PATCH32, + PICFMT_FLAT32 +} pictureformat_t; + +typedef enum +{ + PICFLAGS_XFLIP = 1, + PICFLAGS_YFLIP = 1<<1 +} pictureflags_t; + +void *Picture_Convert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT32 inwidth, INT32 inheight, INT32 inleftoffset, INT32 intopoffset, + pictureflags_t flags); + +void *Picture_PatchConvert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset, + pictureflags_t flags); +void *Picture_FlatConvert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset, + pictureflags_t flags); +void *Picture_GetPatchPixel( + patch_t *patch, pictureformat_t informat, + INT32 x, INT32 y, + pictureflags_t flags); + +INT32 Picture_FormatBPP(pictureformat_t format); +boolean Picture_IsPatchFormat(pictureformat_t format); +boolean Picture_IsFlatFormat(pictureformat_t format); + // Structs #ifdef ROTSPRITE typedef enum @@ -42,7 +93,7 @@ typedef struct } spriteinfo_t; // Conversions between patches / flats / textures... -boolean R_CheckIfPatch(lumpnum_t lump); +boolean R_CheckIfPatch(patch_t *patch, size_t size); void R_TextureToFlat(size_t tex, UINT8 *flat); void R_PatchToFlat(patch_t *patch, UINT8 *flat); void R_PatchToFlat_16bpp(patch_t *patch, UINT16 *raw, boolean flip); diff --git a/src/r_things.c b/src/r_things.c index 2f01ac7f6..43b64bf37 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -24,7 +24,7 @@ #include "i_video.h" // rendermode #include "i_system.h" #include "r_things.h" -#include "r_patch.h" +#include "r_picformats.h" #include "r_plane.h" #include "r_portal.h" #include "p_tick.h" diff --git a/src/r_things.h b/src/r_things.h index 8e4a543c3..706d98c4e 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -16,7 +16,7 @@ #include "sounds.h" #include "r_plane.h" -#include "r_patch.h" +#include "r_picformats.h" #include "r_portal.h" #include "r_defs.h" diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index b334f6313..7efab1bc0 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -281,7 +281,7 @@ - + @@ -443,7 +443,7 @@ - + diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index 3f61e8709..97be5ad5f 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -465,7 +465,7 @@ Hw_Hardware - + R_Rend @@ -928,7 +928,7 @@ Hw_Hardware - + R_Rend diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj index 387d65da9..91a6aa348 100644 --- a/src/win32/Srb2win-vc10.vcxproj +++ b/src/win32/Srb2win-vc10.vcxproj @@ -300,7 +300,7 @@ - + @@ -456,7 +456,7 @@ - + diff --git a/src/win32/Srb2win-vc10.vcxproj.filters b/src/win32/Srb2win-vc10.vcxproj.filters index 2f380c473..00040f4a1 100644 --- a/src/win32/Srb2win-vc10.vcxproj.filters +++ b/src/win32/Srb2win-vc10.vcxproj.filters @@ -472,7 +472,7 @@ Hw_Hardware - + R_Rend @@ -892,7 +892,7 @@ Hw_Hardware - + R_Rend diff --git a/src/z_zone.c b/src/z_zone.c index 5a0ff638b..d02717007 100644 --- a/src/z_zone.c +++ b/src/z_zone.c @@ -27,7 +27,7 @@ #include "doomdef.h" #include "doomstat.h" -#include "r_patch.h" +#include "r_picformats.h" #include "i_system.h" // I_GetFreeMem #include "i_video.h" // rendermode #include "z_zone.h" From bc096d35bb8942d61e8551622b56c41b3fa910be Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 6 Jan 2020 18:31:08 -0300 Subject: [PATCH 002/136] Fix warnings --- src/r_picformats.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/r_picformats.c b/src/r_picformats.c index 8e229c4a6..03ccf6917 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -106,7 +106,7 @@ void *Picture_PatchConvert( INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset, pictureflags_t flags) { - UINT32 x, y; + INT16 x, y; UINT8 *img; UINT8 *imgptr = imgbuf; UINT8 *colpointers, *startofspan; @@ -114,14 +114,14 @@ void *Picture_PatchConvert( patch_t *inpatch = NULL; INT32 inbpp = Picture_FormatBPP(informat); + (void)insize; // ignore + if (informat == outformat) I_Error("Picture_PatchConvert: input and output formats are the same!"); if (!inbpp) I_Error("Picture_PatchConvert: unknown input bits per pixel?!"); - (void)insize; // ignore - // If it's a patch, you can just figure out // the dimensions from the header. if (Picture_IsPatchFormat(informat)) @@ -168,13 +168,13 @@ void *Picture_PatchConvert( switch (informat) { case PICFMT_FLAT32: - input = picture + (offs * 4); + input = (UINT32 *)picture + offs; break; case PICFMT_FLAT16: - input = picture + (offs * 2); + input = (UINT16 *)picture + offs; break; case PICFMT_FLAT: - input = picture + offs; + input = (UINT8 *)picture + offs; break; default: I_Error("Picture_PatchConvert: unsupported flat input format!"); @@ -355,6 +355,10 @@ void *Picture_FlatConvert( INT32 x, y; size_t size; + (void)insize; // ignore + (void)inleftoffset; // ignore + (void)intopoffset; // ignore + if (informat == outformat) I_Error("Picture_FlatConvert: input and output formats are the same!"); @@ -370,8 +374,6 @@ void *Picture_FlatConvert( inpatch = (patch_t *)picture; inwidth = SHORT(inpatch->width); inheight = SHORT(inpatch->height); - inleftoffset = SHORT(inpatch->leftoffset); - intopoffset = SHORT(inpatch->topoffset); } size = (inwidth * inheight) * (outbpp / 8); @@ -393,7 +395,7 @@ void *Picture_FlatConvert( if (Picture_IsPatchFormat(informat)) input = Picture_GetPatchPixel(inpatch, informat, x, y, flags); else if (Picture_IsFlatFormat(informat)) - input = picture + (offs * (inbpp / 8)); + input = (UINT8 *)picture + (offs * (inbpp / 8)); else I_Error("Picture_FlatConvert: unsupported input format!"); @@ -489,15 +491,15 @@ void *Picture_GetPatchPixel( if (x >= 0 && x < SHORT(patch->width)) { INT32 topdelta, prevdelta = -1; - INT32 columnofs = 0; + INT32 colofs = 0; if (flags & PICFLAGS_XFLIP) - columnofs = LONG(patch->columnofs[(SHORT(patch->width)-1)-x]); + colofs = LONG(patch->columnofs[(SHORT(patch->width)-1)-x]); else - columnofs = LONG(patch->columnofs[x]); + colofs = LONG(patch->columnofs[x]); // Column offsets are pointers so no casting required - column = (column_t *)((UINT8 *)patch + columnofs); + column = (column_t *)((UINT8 *)patch + colofs); while (column->topdelta != 0xff) { From 385a6cf3c3620abd23f93674dcf9006b97f8610d Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 6 Jan 2020 20:16:48 -0300 Subject: [PATCH 003/136] Cleanup, NOW --- src/hardware/hw_cache.c | 29 +++-- src/p_setup.c | 4 +- src/r_data.c | 24 ++-- src/r_picformats.c | 259 +++++++++++++++++++++------------------- src/r_picformats.h | 25 ++-- src/r_plane.c | 36 +++--- src/r_things.c | 10 +- src/w_wad.c | 18 +-- 8 files changed, 217 insertions(+), 188 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 90310a92f..6b2965846 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -653,13 +653,17 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex) realpatch = (patch_t *)pdata; #ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) - realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength, NULL, false); + if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) + { + // Dummy variables. + INT32 pngwidth, pngheight; + realpatch = (patch_t *)Picture_PNGConvert(pdata, PICFMT_PATCH, &pngwidth, &pngheight, NULL, NULL, lumplength, NULL, 0); + } else #endif #ifdef WALLFLATS if (texture->type == TEXTURETYPE_FLAT) - realpatch = R_FlatToPatch(pdata, texture->width, texture->height, 0, 0, NULL, false); + realpatch = (patch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_PATCH, 0, NULL, texture->width, texture->height, 0, 0, 0); else #endif { @@ -697,8 +701,12 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm #ifndef NO_PNG_LUMPS // lump is a png so convert it size_t len = W_LumpLengthPwad(grPatch->wadnum, grPatch->lumpnum); - if ((patch != NULL) && R_IsLumpPNG((const UINT8 *)patch, len)) - patch = R_PNGToPatch((const UINT8 *)patch, len, NULL, true); + if ((patch != NULL) && Picture_IsLumpPNG((const UINT8 *)patch, len)) + { + // Dummy variables. + INT32 pngwidth, pngheight; + patch = (patch_t *)Picture_PNGConvert((const UINT8 *)patch, PICFMT_PATCH, &pngwidth, &pngheight, NULL, NULL, len, NULL, 0); + } #endif // don't do it twice (like a cache) @@ -956,6 +964,8 @@ static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) static void HWR_CacheTextureAsFlat(GLMipmap_t *grMipmap, INT32 texturenum) { UINT8 *flat; + UINT8 *converted; + size_t size; if (needpatchflush) W_FlushCachedPatches(); @@ -971,11 +981,12 @@ static void HWR_CacheTextureAsFlat(GLMipmap_t *grMipmap, INT32 texturenum) grMipmap->width = (UINT16)textures[texturenum]->width; grMipmap->height = (UINT16)textures[texturenum]->height; + size = (grMipmap->width * grMipmap->height); - flat = Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->grInfo.data); - memset(flat, TRANSPARENTPIXEL, grMipmap->width * grMipmap->height); - - R_TextureToFlat(texturenum, flat); + flat = Z_Malloc(size, PU_HWRCACHE, &grMipmap->grInfo.data); + converted = (UINT8 *)Picture_TextureToFlat(texturenum); + M_Memcpy(flat, converted, size); + Z_Free(converted); } // Download a Doom 'flat' to the hardware cache and make it ready for use diff --git a/src/p_setup.c b/src/p_setup.c index eca5413b4..ec9f31e2f 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -639,7 +639,7 @@ flatfound: /* This could be a flat, patch, or PNG. */ flatpatch = W_CacheLumpNum(flatnum, PU_STATIC); lumplength = W_LumpLength(flatnum); - if (R_CheckIfPatch(flatpatch, lumplength)) + if (Picture_CheckIfPatch(flatpatch, lumplength)) levelflat->type = LEVELFLAT_PATCH; else { @@ -651,7 +651,7 @@ flatfound: if (flatpatch) Z_Free(flatpatch); W_ReadLumpHeader(flatnum, buffer, 8, 0); - if (R_IsLumpPNG(buffer, lumplength)) + if (Picture_IsLumpPNG(buffer, lumplength)) levelflat->type = LEVELFLAT_PNG; else #endif/*NO_PNG_LUMPS*/ diff --git a/src/r_data.c b/src/r_data.c index 14c04f15e..c6e89d2ad 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -473,7 +473,7 @@ static UINT8 *R_GenerateTexture(size_t texnum) realpatch = (patch_t *)pdata; #ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) + if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) goto multipatch; #endif #ifdef WALLFLATS @@ -570,13 +570,17 @@ static UINT8 *R_GenerateTexture(size_t texnum) dealloc = true; #ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) - realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength, NULL, false); + if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) + { + // Dummy variables. + INT32 pngwidth, pngheight; + realpatch = (patch_t *)Picture_PNGConvert((UINT8 *)realpatch, PICFMT_PATCH, &pngwidth, &pngheight, NULL, NULL, lumplength, NULL, 0); + } else #endif #ifdef WALLFLATS if (texture->type == TEXTURETYPE_FLAT) - realpatch = R_FlatToPatch(pdata, texture->width, texture->height, 0, 0, NULL, false); + realpatch = (patch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_PATCH, 0, NULL, texture->width, texture->height, 0, 0, 0); else #endif { @@ -896,10 +900,10 @@ countflats: M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); #ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)patchlump, lumplength)) + if (Picture_IsLumpPNG((UINT8 *)patchlump, lumplength)) { - INT16 width, height; - R_PNGDimensions((UINT8 *)patchlump, &width, &height, lumplength); + INT16 width = 0, height = 0; + Picture_PNGDimensions((UINT8 *)patchlump, &width, &height, lumplength); texture->width = width; texture->height = height; } @@ -999,10 +1003,10 @@ checkflats: M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); #ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)flatlump, lumplength)) + if (Picture_IsLumpPNG((UINT8 *)flatlump, lumplength)) { - INT16 width, height; - R_PNGDimensions((UINT8 *)flatlump, &width, &height, lumplength); + INT16 width = 0, height = 0; + Picture_PNGDimensions((UINT8 *)flatlump, &width, &height, lumplength); texture->width = width; texture->height = height; } diff --git a/src/r_picformats.c b/src/r_picformats.c index 03ccf6917..cbfdc3c90 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -59,7 +59,7 @@ fixed_t sinang2rad[ROTANGLES]; * \param picture Input picture data. * \param outformat Output picture format. * \param insize Input picture size. - * \param outsize Output picture size. + * \param outsize Output picture size, as a pointer. * \param inwidth Input picture width. * \param inheight Input picture height. * \param inleftoffset Input picture left offset, for patches. @@ -73,8 +73,12 @@ void *Picture_Convert( INT32 inwidth, INT32 inheight, INT32 inleftoffset, INT32 intopoffset, pictureflags_t flags) { - if (informat == outformat) // wut? - I_Error("Picture_Convert: input and output formats are the same!"); + if (informat == PICFMT_NONE) + I_Error("Picture_Convert: input format was PICFMT_NONE!"); + else if (outformat == PICFMT_NONE) + I_Error("Picture_Convert: output format was PICFMT_NONE!"); + else if (informat == outformat) + I_Error("Picture_Convert: input and output formats were the same!"); if (Picture_IsPatchFormat(outformat)) return Picture_PatchConvert(informat, picture, outformat, insize, outsize, inwidth, inheight, inleftoffset, intopoffset, flags); @@ -92,7 +96,7 @@ void *Picture_Convert( * \param picture Input picture data. * \param outformat Output picture format. * \param insize Input picture size. - * \param outsize Output picture size. + * \param outsize Output picture size, as a pointer. * \param inwidth Input picture width. * \param inheight Input picture height. * \param inleftoffset Input picture left offset, for patches. @@ -116,8 +120,12 @@ void *Picture_PatchConvert( (void)insize; // ignore - if (informat == outformat) - I_Error("Picture_PatchConvert: input and output formats are the same!"); + if (informat == PICFMT_NONE) + I_Error("Picture_PatchConvert: input format was PICFMT_NONE!"); + else if (outformat == PICFMT_NONE) + I_Error("Picture_PatchConvert: output format was PICFMT_NONE!"); + else if (informat == outformat) + I_Error("Picture_PatchConvert: input and output formats were the same!"); if (!inbpp) I_Error("Picture_PatchConvert: unknown input bits per pixel?!"); @@ -334,7 +342,7 @@ void *Picture_PatchConvert( * \param picture Input picture data. * \param outformat Output picture format. * \param insize Input picture size. - * \param outsize Output picture size. + * \param outsize Output picture size, as a pointer. * \param inwidth Input picture width. * \param inheight Input picture height. * \param inleftoffset Input picture left offset, for patches. @@ -359,8 +367,12 @@ void *Picture_FlatConvert( (void)inleftoffset; // ignore (void)intopoffset; // ignore - if (informat == outformat) - I_Error("Picture_FlatConvert: input and output formats are the same!"); + if (informat == PICFMT_NONE) + I_Error("Picture_FlatConvert: input format was PICFMT_NONE!"); + else if (outformat == PICFMT_NONE) + I_Error("Picture_FlatConvert: output format was PICFMT_NONE!"); + else if (informat == outformat) + I_Error("Picture_FlatConvert: input and output formats were the same!"); if (!inbpp) I_Error("Picture_FlatConvert: unknown input bits per pixel?!"); @@ -587,7 +599,7 @@ boolean Picture_IsFlatFormat(pictureformat_t format) * \param picture Input patch size. * \return True if the input patch is valid. */ -boolean R_CheckIfPatch(patch_t *patch, size_t size) +boolean Picture_CheckIfPatch(patch_t *patch, size_t size) { INT16 width, height; boolean result; @@ -624,26 +636,40 @@ boolean R_CheckIfPatch(patch_t *patch, size_t size) return result; } -// -// R_TextureToFlat -// -// Convert a texture to a flat. -// -void R_TextureToFlat(size_t tex, UINT8 *flat) +/** Converts a texture to a flat. + * + * \param trickytex The texture number. + * \return The converted flat. + */ +void *Picture_TextureToFlat(size_t trickytex) { - texture_t *texture = textures[tex]; + texture_t *texture; + size_t tex; + UINT8 *converted; + size_t flatsize; fixed_t col, ofs; column_t *column; UINT8 *desttop, *dest, *deststop; UINT8 *source; - // yea + if (trickytex >= (unsigned)numtextures) + I_Error("Picture_TextureToFlat: invalid texture number!"); + + // Check the texture cache + // If the texture's not there, it'll be generated right now + tex = trickytex; + texture = textures[tex]; R_CheckTextureCache(tex); - desttop = flat; - deststop = desttop + (texture->width * texture->height); + // Allocate the flat + flatsize = (texture->width * texture->height); + converted = Z_Malloc(flatsize, PU_STATIC, NULL); + memset(converted, TRANSPARENTPIXEL, flatsize); + // Now we're gonna write to it + desttop = converted; + deststop = desttop + flatsize; for (col = 0; col < texture->width; col++, desttop++) { // no post_t info @@ -682,61 +708,17 @@ void R_TextureToFlat(size_t tex, UINT8 *flat) } } } + + return converted; } -// -// R_PatchToFlat -// -// Convert a patch to a flat. -// -void R_PatchToFlat(patch_t *patch, UINT8 *flat) -{ - size_t outsize = 0; - UINT8 *converted = Picture_FlatConvert(PICFMT_PATCH, patch, PICFMT_FLAT, 0, &outsize, 0, 0, 0, 0, 0); - M_Memcpy(flat, converted, outsize); - Z_Free(converted); -} - -// -// R_PatchToFlat_16bpp -// -// Convert a patch to a 16-bit flat. -// -void R_PatchToFlat_16bpp(patch_t *patch, UINT16 *raw, boolean flip) -{ - size_t outsize = 0; - UINT16 *converted = Picture_FlatConvert(PICFMT_PATCH, patch, PICFMT_FLAT16, 0, &outsize, 0, 0, 0, 0, (flip) ? PICFLAGS_XFLIP : 0); - M_Memcpy(raw, converted, outsize); - Z_Free(converted); -} - -// -// R_FlatToPatch -// -// Convert a flat to a patch. -// -patch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency) -{ - (void)transparency; - return (patch_t *)Picture_Convert(PICFMT_FLAT, raw, PICFMT_PATCH, 0, destsize, width, height, leftoffset, topoffset, 0); -} - -// -// R_FlatToPatch_16bpp -// -// Convert a 16-bit flat to a patch. -// -patch_t *R_FlatToPatch_16bpp(UINT16 *raw, UINT16 width, UINT16 height, size_t *size) -{ - return (patch_t *)Picture_Convert(PICFMT_FLAT16, raw, PICFMT_PATCH, 0, size, width, height, 0, 0, 0); -} - -// -// R_IsLumpPNG -// -// Returns true if the lump is a valid PNG. -// -boolean R_IsLumpPNG(const UINT8 *d, size_t s) +/** Returns true if the lump is a valid PNG. + * + * \param d The lump to be checked. + * \param s The lump size. + * \return True if the lump is a PNG image. + */ +boolean Picture_IsLumpPNG(const UINT8 *d, size_t s) { if (s < 67) // http://garethrees.org/2007/11/14/pngcrush/ return false; @@ -803,7 +785,7 @@ static void PNG_warn(png_structp PNG, png_const_charp pngtext) CONS_Debug(DBG_RENDER, "libpng warning at %p: %s", PNG, pngtext); } -static png_bytep *PNG_Read(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) +static png_bytep *PNG_Read(const UINT8 *png, INT32 *w, INT32 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) { png_structp png_ptr; png_infop png_info_ptr; @@ -824,17 +806,13 @@ static png_bytep *PNG_Read(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoff png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, PNG_error, PNG_warn); if (!png_ptr) - { - CONS_Debug(DBG_RENDER, "PNG_Load: Error on initialize libpng\n"); - return NULL; - } + I_Error("PNG_Load: Couldn't initialize libpng!"); png_info_ptr = png_create_info_struct(png_ptr); if (!png_info_ptr) { - CONS_Debug(DBG_RENDER, "PNG_Load: Error on allocate for libpng\n"); png_destroy_read_struct(&png_ptr, NULL, NULL); - return NULL; + I_Error("PNG_Load: libpng couldn't allocate memory!"); } #ifdef USE_FAR_KEYWORD @@ -921,22 +899,44 @@ static png_bytep *PNG_Read(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoff return row_pointers; } -// Convert a PNG to a raw image. -static void *PNG_Convert(const UINT8 *png, INT32 outbpp, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) +/** Converts a PNG to a picture. + * + * \param png The PNG image. + * \param outformat The output picture's format. + * \param w The output picture's width, as a pointer. + * \param h The output picture's height, as a pointer. + * \param topoffset The output picture's top offset, for sprites, as a pointer. + * \param leftoffset The output picture's left offset, for sprites, as a pointer. + * \param insize The input picture's size. + * \param outsize A pointer to the output picture's size. + * \param flags Input picture flags. + * \return A pointer to the converted picture. + */ +void *Picture_PNGConvert( + const UINT8 *png, pictureformat_t outformat, + INT32 *w, INT32 *h, + INT16 *topoffset, INT16 *leftoffset, + size_t insize, size_t *outsize, + pictureflags_t flags) { void *flat; + INT32 outbpp; + size_t flatsize; png_uint_32 x, y; - png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, size); + png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, insize); png_uint_32 width = *w, height = *h; + outbpp = Picture_FormatBPP(outformat); if (!outbpp) - I_Error("PNG_Convert: unknown output bits per pixel?!"); + I_Error("Picture_PNGConvert: unknown output bits per pixel?!"); - if (!row_pointers) - I_Error("PNG_Convert: conversion failed!"); + // Figure out the size + flatsize = (width * height) * (outbpp / 8); + if (outsize) + *outsize = flatsize; // Convert the image - flat = Z_Malloc((width * height) * (outbpp / 8), PU_STATIC, NULL); + flat = Z_Malloc(flatsize, PU_STATIC, NULL); if (outbpp == 8) memset(flat, TRANSPARENTPIXEL, (width * height)); @@ -979,48 +979,55 @@ static void *PNG_Convert(const UINT8 *png, INT32 outbpp, UINT16 *w, UINT16 *h, I } } } + + // Free the row pointers that libpng allocated. free(row_pointers); + // But wait, there's more! + if (Picture_IsPatchFormat(outformat)) + { + void *converted; + pictureformat_t informat = PICFMT_NONE; + INT16 patleftoffset = 0, pattopoffset = 0; + + // Figure out the input format, from the bitdepth of the input format + switch (outbpp) + { + case 32: + informat = PICFMT_FLAT32; + break; + case 16: + informat = PICFMT_FLAT16; + break; + default: + informat = PICFMT_FLAT; // Assumed 8bpp + break; + } + + // Also find out if leftoffset and topoffset aren't pointing to NULL. + if (leftoffset) + patleftoffset = *leftoffset; + if (topoffset) + pattopoffset = *topoffset; + + // Now, convert it! + converted = Picture_PatchConvert(informat, flat, outformat, insize, outsize, (INT16)width, (INT16)height, patleftoffset, pattopoffset, flags); + Z_Free(flat); + return converted; + } + return flat; } -// -// R_PNGToFlat -// -// Convert a PNG to a flat. -// -UINT8 *R_PNGToFlat(UINT16 *width, UINT16 *height, UINT8 *png, size_t size) -{ - return PNG_Convert(png, 8, width, height, NULL, NULL, size); -} - -// -// R_PNGToPatch -// -// Convert a PNG to a patch. -// -patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean transparency) -{ - UINT16 width, height; - INT16 topoffset = 0, leftoffset = 0; - UINT8 *raw = PNG_Convert(png, 32, &width, &height, &topoffset, &leftoffset, size); - patch_t *output; - - (void)transparency; - if (!raw) - I_Error("R_PNGToPatch: conversion failed"); - - output = Picture_Convert(PICFMT_FLAT32, raw, PICFMT_PATCH, 0, destsize, width, height, leftoffset, topoffset, 0); - Z_Free(raw); - return output; -} - -// -// R_PNGDimensions -// -// Get the dimensions of a PNG file. -// -boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size) +/** Returns the dimensions of a PNG image, but doesn't perform any conversions. + * + * \param png The PNG image. + * \param width A pointer to the input picture's width. + * \param height A pointer to the input picture's height. + * \param size The input picture's size. + * \return True if reading the file succeeded, false if it failed. + */ +boolean Picture_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size) { png_structp png_ptr; png_infop png_info_ptr; @@ -1449,7 +1456,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp lumplength = W_LumpLength(lump); // Because there's something wrong with SPR_DFLM, I guess - if (!R_CheckIfPatch(patch, lumplength)) + if (!Picture_CheckIfPatch(patch, lumplength)) return; width = patch->width; @@ -1567,7 +1574,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp } // make patch - newpatch = R_FlatToPatch_16bpp(rawdst, newwidth, newheight, &size); + newpatch = (patch_t *)Picture_Convert(PICFMT_FLAT16, rawdst, PICFMT_PATCH, 0, &size, newwidth, newheight, 0, 0, 0); { newpatch->leftoffset = (newpatch->width / 2) + (leftoffset - px); newpatch->topoffset = (newpatch->height / 2) + (patch->topoffset - py); diff --git a/src/r_picformats.h b/src/r_picformats.h index cdc64a674..0dd4340cc 100644 --- a/src/r_picformats.h +++ b/src/r_picformats.h @@ -64,9 +64,12 @@ void *Picture_GetPatchPixel( INT32 x, INT32 y, pictureflags_t flags); +void *Picture_TextureToFlat(size_t trickytex); + INT32 Picture_FormatBPP(pictureformat_t format); boolean Picture_IsPatchFormat(pictureformat_t format); boolean Picture_IsFlatFormat(pictureformat_t format); +boolean Picture_CheckIfPatch(patch_t *patch, size_t size); // Structs #ifdef ROTSPRITE @@ -92,22 +95,18 @@ typedef struct boolean available; } spriteinfo_t; -// Conversions between patches / flats / textures... -boolean R_CheckIfPatch(patch_t *patch, size_t size); -void R_TextureToFlat(size_t tex, UINT8 *flat); -void R_PatchToFlat(patch_t *patch, UINT8 *flat); -void R_PatchToFlat_16bpp(patch_t *patch, UINT16 *raw, boolean flip); -patch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency); -patch_t *R_FlatToPatch_16bpp(UINT16 *raw, UINT16 width, UINT16 height, size_t *size); - // Portable Network Graphics -boolean R_IsLumpPNG(const UINT8 *d, size_t s); -#define W_ThrowPNGError(lumpname, wadfilename) I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", lumpname, wadfilename); // Fears Of LJ Sonic +boolean Picture_IsLumpPNG(const UINT8 *d, size_t s); +#define Picture_ThrowPNGError(lumpname, wadfilename) I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", lumpname, wadfilename); // Fears Of LJ Sonic #ifndef NO_PNG_LUMPS -UINT8 *R_PNGToFlat(UINT16 *width, UINT16 *height, UINT8 *png, size_t size); -patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean transparency); -boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size); +void *Picture_PNGConvert( + const UINT8 *png, pictureformat_t outformat, + INT32 *w, INT32 *h, + INT16 *topoffset, INT16 *leftoffset, + size_t insize, size_t *outsize, + pictureflags_t flags); +boolean Picture_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size); #endif // SpriteInfo diff --git a/src/r_plane.c b/src/r_plane.c index 5d5e1f20d..c11aeb138 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -764,18 +764,6 @@ void R_CheckFlatLength(size_t size) } } -// -// R_GenerateFlat -// -// Generate a flat from specified width and height. -// -static UINT8 *R_GenerateFlat(UINT16 width, UINT16 height) -{ - UINT8 *flat = Z_Malloc(width * height, PU_LEVEL, NULL); - memset(flat, TRANSPARENTPIXEL, width * height); - return flat; -} - // // R_GetTextureFlat // @@ -810,12 +798,17 @@ static UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boo // Level texture if (leveltexture) { + UINT8 *converted; + size_t size; texture_t *texture = textures[levelflat->u.texture.num]; texflat->width = ds_flatwidth = texture->width; texflat->height = ds_flatheight = texture->height; - texflat->flat = R_GenerateFlat(ds_flatwidth, ds_flatheight); - R_TextureToFlat(levelflat->u.texture.num, texflat->flat); + size = (texflat->width * texflat->height); + texflat->flat = Z_Malloc(size, PU_LEVEL, NULL); + converted = (UINT8 *)Picture_TextureToFlat(levelflat->u.texture.num); + M_Memcpy(texflat->flat, converted, size); + Z_Free(converted); flat = texflat->flat; levelflat->flatpatch = flat; @@ -829,22 +822,31 @@ static UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boo #ifndef NO_PNG_LUMPS if (ispng) { - levelflat->flatpatch = R_PNGToFlat(&levelflat->width, &levelflat->height, ds_source, W_LumpLength(levelflat->u.flat.lumpnum)); + INT32 pngwidth, pngheight; + + levelflat->flatpatch = Picture_PNGConvert(ds_source, PICFMT_FLAT32, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); levelflat->topoffset = levelflat->leftoffset = 0; + levelflat->width = (UINT16)pngwidth; + levelflat->height = (UINT16)pngheight; + ds_flatwidth = levelflat->width; ds_flatheight = levelflat->height; } else #endif { + UINT8 *converted; + size_t size; levelflat->width = ds_flatwidth = SHORT(patch->width); levelflat->height = ds_flatheight = SHORT(patch->height); levelflat->topoffset = patch->topoffset * FRACUNIT; levelflat->leftoffset = patch->leftoffset * FRACUNIT; - levelflat->flatpatch = R_GenerateFlat(ds_flatwidth, ds_flatheight); - R_PatchToFlat(patch, levelflat->flatpatch); + levelflat->flatpatch = Z_Malloc(levelflat->width * levelflat->height, PU_LEVEL, NULL); + converted = Picture_FlatConvert(PICFMT_PATCH, patch, PICFMT_FLAT, 0, &size, levelflat->width, levelflat->height, patch->topoffset, patch->leftoffset, 0); + M_Memcpy(levelflat->flatpatch, converted, size); + Z_Free(converted); } flat = levelflat->flatpatch; } diff --git a/src/r_things.c b/src/r_things.c index 43b64bf37..5b4dd0633 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -282,10 +282,14 @@ static boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, patch_t *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC); size_t len = W_LumpLengthPwad(wadnum, l); // lump is a png so convert it - if (R_IsLumpPNG((UINT8 *)png, len)) + if (Picture_IsLumpPNG((UINT8 *)png, len)) { - png = R_PNGToPatch((UINT8 *)png, len, NULL, true); - M_Memcpy(&patch, png, sizeof(INT16)*4); + // Dummy variables. + INT32 pngwidth, pngheight; + INT16 topoffset, leftoffset; + patch_t *converted = (patch_t *)Picture_PNGConvert((UINT8 *)png, PICFMT_PATCH, &pngwidth, &pngheight, &topoffset, &leftoffset, len, NULL, 0); + M_Memcpy(&patch, converted, sizeof(INT16)*4); // only copy the header because that's all we need + Z_Free(converted); } Z_Free(png); } diff --git a/src/w_wad.c b/src/w_wad.c index d8b362d49..465b5abd8 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1213,8 +1213,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si #ifdef NO_PNG_LUMPS { size_t bytesread = fread(dest, 1, size, handle); - if (R_IsLumpPNG((UINT8 *)dest, bytesread)) - W_ThrowPNGError(l->name2, wadfiles[wad]->filename); + if (Picture_IsLumpPNG((UINT8 *)dest, bytesread)) + Picture_ThrowPNGError(l->name2, wadfiles[wad]->filename); return bytesread; } #else @@ -1255,8 +1255,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si Z_Free(rawData); Z_Free(decData); #ifdef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)dest, size)) - W_ThrowPNGError(l->name2, wadfiles[wad]->filename); + if (Picture_IsLumpPNG((UINT8 *)dest, size)) + Picture_ThrowPNGError(l->name2, wadfiles[wad]->filename); #endif return size; #else @@ -1318,8 +1318,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si Z_Free(decData); #ifdef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)dest, size)) - W_ThrowPNGError(l->name2, wadfiles[wad]->filename); + if (Picture_IsLumpPNG((UINT8 *)dest, size)) + Picture_ThrowPNGError(l->name2, wadfiles[wad]->filename); #endif return size; } @@ -1538,10 +1538,12 @@ void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) #ifndef NO_PNG_LUMPS // lump is a png so convert it - if (R_IsLumpPNG((UINT8 *)lumpdata, len)) + if (Picture_IsLumpPNG((UINT8 *)lumpdata, len)) { + // Dummy variables. size_t newlen; - srcdata = R_PNGToPatch((UINT8 *)lumpdata, len, &newlen, true); + INT32 pngwidth, pngheight; + srcdata = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_PATCH, &pngwidth, &pngheight, NULL, NULL, len, &newlen, 0); ptr = Z_Realloc(ptr, newlen, tag, &lumpcache[lump]); M_Memcpy(ptr, srcdata, newlen); Z_Free(srcdata); From 66dedc3730085dc66de8bdad421947ad32331ded Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 6 Jan 2020 20:20:05 -0300 Subject: [PATCH 004/136] Initialise --- src/r_picformats.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/r_picformats.c b/src/r_picformats.c index cbfdc3c90..a8ae01f13 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -493,9 +493,9 @@ void *Picture_GetPatchPixel( { fixed_t ofs; column_t *column; - UINT8 *s8; - UINT16 *s16; - UINT32 *s32; + UINT8 *s8 = NULL; + UINT16 *s16 = NULL; + UINT32 *s32 = NULL; if (patch == NULL) I_Error("Picture_GetPatchPixel: patch == NULL"); From c4e082d56ece4947aa09a331ed290cfa762ac99f Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 6 Jan 2020 20:39:38 -0300 Subject: [PATCH 005/136] Fix patch conversion --- src/r_picformats.c | 23 ++++++++++++++++------- src/r_plane.c | 2 +- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/r_picformats.c b/src/r_picformats.c index a8ae01f13..ebeb35b7b 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -926,9 +926,15 @@ void *Picture_PNGConvert( png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, insize); png_uint_32 width = *w, height = *h; - outbpp = Picture_FormatBPP(outformat); - if (!outbpp) - I_Error("Picture_PNGConvert: unknown output bits per pixel?!"); + // Hack for patches because you'll want to preserve transparency. + if (Picture_IsPatchFormat(outformat)) + outbpp = 16; + else + { + outbpp = Picture_FormatBPP(outformat); + if (!outbpp) + I_Error("Picture_PNGConvert: unknown output bits per pixel?!"); + } // Figure out the size flatsize = (width * height) * (outbpp / 8); @@ -936,7 +942,9 @@ void *Picture_PNGConvert( *outsize = flatsize; // Convert the image - flat = Z_Malloc(flatsize, PU_STATIC, NULL); + flat = Z_Calloc(flatsize, PU_STATIC, NULL); + + // Set transparency if (outbpp == 8) memset(flat, TRANSPARENTPIXEL, (width * height)); @@ -980,7 +988,7 @@ void *Picture_PNGConvert( } } - // Free the row pointers that libpng allocated. + // Free the row pointers that we allocated for libpng. free(row_pointers); // But wait, there's more! @@ -990,7 +998,7 @@ void *Picture_PNGConvert( pictureformat_t informat = PICFMT_NONE; INT16 patleftoffset = 0, pattopoffset = 0; - // Figure out the input format, from the bitdepth of the input format + // Figure out the format of the flat, from the bit depth of the output format switch (outbpp) { case 32: @@ -1000,7 +1008,7 @@ void *Picture_PNGConvert( informat = PICFMT_FLAT16; break; default: - informat = PICFMT_FLAT; // Assumed 8bpp + informat = PICFMT_FLAT; break; } @@ -1016,6 +1024,7 @@ void *Picture_PNGConvert( return converted; } + // Return the converted flat! return flat; } diff --git a/src/r_plane.c b/src/r_plane.c index c11aeb138..43c097a23 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -824,7 +824,7 @@ static UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boo { INT32 pngwidth, pngheight; - levelflat->flatpatch = Picture_PNGConvert(ds_source, PICFMT_FLAT32, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); + levelflat->flatpatch = Picture_PNGConvert(ds_source, PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); levelflat->topoffset = levelflat->leftoffset = 0; levelflat->width = (UINT16)pngwidth; levelflat->height = (UINT16)pngheight; From fd2340d802192b0b5edee2b40107461baaa26de3 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 6 Jan 2020 20:42:46 -0300 Subject: [PATCH 006/136] Make error messages consistent --- src/r_picformats.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/r_picformats.c b/src/r_picformats.c index ebeb35b7b..bb3abcf6a 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -806,13 +806,13 @@ static png_bytep *PNG_Read(const UINT8 *png, INT32 *w, INT32 *h, INT16 *topoffse png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, PNG_error, PNG_warn); if (!png_ptr) - I_Error("PNG_Load: Couldn't initialize libpng!"); + I_Error("PNG_Read: Couldn't initialize libpng!"); png_info_ptr = png_create_info_struct(png_ptr); if (!png_info_ptr) { png_destroy_read_struct(&png_ptr, NULL, NULL); - I_Error("PNG_Load: libpng couldn't allocate memory!"); + I_Error("PNG_Read: libpng couldn't allocate memory!"); } #ifdef USE_FAR_KEYWORD @@ -821,9 +821,8 @@ static png_bytep *PNG_Read(const UINT8 *png, INT32 *w, INT32 *h, INT16 *topoffse if (setjmp(png_jmpbuf(png_ptr))) #endif { - //CONS_Debug(DBG_RENDER, "libpng load error on %s\n", filename); png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); - return NULL; + I_Error("PNG_Read: libpng load error!"); } #ifdef USE_FAR_KEYWORD png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); @@ -1053,17 +1052,13 @@ boolean Picture_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t si png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, PNG_error, PNG_warn); if (!png_ptr) - { - CONS_Debug(DBG_RENDER, "PNG_Load: Error on initialize libpng\n"); - return false; - } + I_Error("Picture_PNGDimensions: Couldn't initialize libpng!"); png_info_ptr = png_create_info_struct(png_ptr); if (!png_info_ptr) { - CONS_Debug(DBG_RENDER, "PNG_Load: Error on allocate for libpng\n"); png_destroy_read_struct(&png_ptr, NULL, NULL); - return false; + I_Error("Picture_PNGDimensions: libpng couldn't allocate memory!"); } #ifdef USE_FAR_KEYWORD @@ -1072,9 +1067,8 @@ boolean Picture_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t si if (setjmp(png_jmpbuf(png_ptr))) #endif { - //CONS_Debug(DBG_RENDER, "libpng load error on %s\n", filename); png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); - return false; + I_Error("Picture_PNGDimensions: libpng load error!"); } #ifdef USE_FAR_KEYWORD png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); From a24607a4524ba7e38f0ddc2cf70ac93d6de46758 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 6 Jan 2020 21:02:25 -0300 Subject: [PATCH 007/136] Don't force 16bpp if the bit depth was already higher! --- src/r_picformats.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/r_picformats.c b/src/r_picformats.c index bb3abcf6a..b55dac5ad 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -925,16 +925,21 @@ void *Picture_PNGConvert( png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, insize); png_uint_32 width = *w, height = *h; + // Find the output format's bits per pixel amount + outbpp = Picture_FormatBPP(outformat); + // Hack for patches because you'll want to preserve transparency. if (Picture_IsPatchFormat(outformat)) - outbpp = 16; - else { - outbpp = Picture_FormatBPP(outformat); - if (!outbpp) - I_Error("Picture_PNGConvert: unknown output bits per pixel?!"); + // Force a higher bit depth + if (outbpp == 8) + outbpp = 16; } + // Shouldn't happen. + if (!outbpp) + I_Error("Picture_PNGConvert: unknown output bits per pixel?!"); + // Figure out the size flatsize = (width * height) * (outbpp / 8); if (outsize) From d9b64c14c6f33b756fb022e4be20af2b3f8de81c Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 6 Jan 2020 21:16:33 -0300 Subject: [PATCH 008/136] Missing check --- src/r_picformats.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/r_picformats.c b/src/r_picformats.c index b55dac5ad..340fd5bf6 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -129,6 +129,8 @@ void *Picture_PatchConvert( if (!inbpp) I_Error("Picture_PatchConvert: unknown input bits per pixel?!"); + if (!Picture_FormatBPP(outformat)) + I_Error("Picture_PatchConvert: unknown output bits per pixel?!"); // If it's a patch, you can just figure out // the dimensions from the header. From d18b0dcde866f5ca9bfef156ecf9669fc881ec3f Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 6 Jan 2020 22:08:51 -0300 Subject: [PATCH 009/136] How did I mess this up? --- src/r_picformats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_picformats.c b/src/r_picformats.c index 340fd5bf6..cafe30d7e 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -1578,7 +1578,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp { void *input = Picture_GetPatchPixel(patch, PICFMT_PATCH, sx, sy, bflip); if (input != NULL) - rawdst[(dy*newwidth)+dx] = *(UINT16 *)input; + rawdst[(dy*newwidth)+dx] = (0xFF00 | (*(UINT8 *)input)); } } } From 96340ac80c84bab28947907d5be4e86f36d87605 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 7 Jan 2020 12:35:10 -0300 Subject: [PATCH 010/136] Move texture stuff to its own file --- src/CMakeLists.txt | 2 + src/Makefile | 1 + src/dehacked.c | 1 + src/hardware/hw_cache.c | 1 + src/p_maputl.c | 1 + src/p_saveg.c | 1 + src/p_setup.c | 1 + src/p_spec.c | 1 + src/r_data.c | 1462 +----------------------- src/r_data.h | 90 +- src/r_local.h | 1 + src/r_main.h | 1 + src/r_picformats.c | 3 + src/r_plane.c | 1 + src/r_plane.h | 1 + src/r_portal.h | 1 + src/r_textures.c | 1441 +++++++++++++++++++++++ src/r_textures.h | 109 ++ src/sdl/Srb2SDL-vc10.vcxproj | 6 +- src/sdl/Srb2SDL-vc10.vcxproj.filters | 6 + src/w_wad.c | 2 +- src/win32/Srb2win-vc10.vcxproj | 6 +- src/win32/Srb2win-vc10.vcxproj.filters | 3 + 23 files changed, 1594 insertions(+), 1548 deletions(-) create mode 100644 src/r_textures.c create mode 100644 src/r_textures.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7a6e7871a..9577f0e60 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -123,6 +123,7 @@ set(SRB2_CORE_RENDER_SOURCES r_sky.c r_splats.c r_things.c + r_textures.c r_picformats.c r_portal.c @@ -138,6 +139,7 @@ set(SRB2_CORE_RENDER_SOURCES r_splats.h r_state.h r_things.h + r_textures.h r_picformats.h r_portal.h ) diff --git a/src/Makefile b/src/Makefile index 282ff8f4f..878df680a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -471,6 +471,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/r_sky.o \ $(OBJDIR)/r_splats.o \ $(OBJDIR)/r_things.o \ + $(OBJDIR)/r_textures.o \ $(OBJDIR)/r_picformats.o \ $(OBJDIR)/r_portal.o \ $(OBJDIR)/screen.o \ diff --git a/src/dehacked.c b/src/dehacked.c index 81690cde9..e655e8a63 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -28,6 +28,7 @@ #include "p_local.h" // for var1 and var2, and some constants #include "p_setup.h" #include "r_data.h" +#include "r_textures.h" #include "r_draw.h" #include "r_picformats.h" #include "r_sky.h" diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 6b2965846..9bf7db1da 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -26,6 +26,7 @@ #include "../doomstat.h" //gamemode #include "../i_video.h" //rendermode #include "../r_data.h" +#include "../r_textures.h" #include "../w_wad.h" #include "../z_zone.h" #include "../v_video.h" diff --git a/src/p_maputl.c b/src/p_maputl.c index f598595e2..c6828db4a 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -18,6 +18,7 @@ #include "p_local.h" #include "r_main.h" #include "r_data.h" +#include "r_textures.h" #include "p_maputl.h" #include "p_polyobj.h" #include "p_slopes.h" diff --git a/src/p_saveg.c b/src/p_saveg.c index 89447db80..cca43db68 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -22,6 +22,7 @@ #include "p_setup.h" #include "p_saveg.h" #include "r_data.h" +#include "r_textures.h" #include "r_things.h" #include "r_state.h" #include "w_wad.h" diff --git a/src/p_setup.c b/src/p_setup.c index ec9f31e2f..0b43f76a5 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -28,6 +28,7 @@ #include "r_data.h" #include "r_things.h" +#include "r_textures.h" #include "r_picformats.h" #include "r_sky.h" #include "r_draw.h" diff --git a/src/p_spec.c b/src/p_spec.c index d7a2133c8..f19946646 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -20,6 +20,7 @@ #include "p_local.h" #include "p_setup.h" // levelflats for flat animation #include "r_data.h" +#include "r_textures.h" #include "m_random.h" #include "p_mobj.h" #include "i_system.h" diff --git a/src/r_data.c b/src/r_data.c index c6e89d2ad..db791ab16 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -19,6 +19,7 @@ #include "p_local.h" #include "m_misc.h" #include "r_data.h" +#include "r_textures.h" #include "r_picformats.h" #include "w_wad.h" #include "z_zone.h" @@ -32,64 +33,6 @@ #include // alloca(sizeof) #endif -#ifdef HWRENDER -#include "hardware/hw_main.h" // HWR_LoadTextures -#endif - -#if defined(_MSC_VER) -#pragma pack(1) -#endif - -// Not sure if this is necessary, but it was in w_wad.c, so I'm putting it here too -Shadow Hog -#if 0 -#define AVOID_ERRNO -#else -#include -#endif - -// -// Texture definition. -// 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. -// -typedef struct -{ - INT16 originx, originy; - INT16 patch, stepdir, colormap; -} ATTRPACK mappatch_t; - -// -// Texture definition. -// An SRB2 wall texture is a list of patches -// which are to be combined in a predefined order. -// -typedef struct -{ - char name[8]; - INT32 masked; - INT16 width; - INT16 height; - INT32 columndirectory; // FIXTHIS: OBSOLETE - INT16 patchcount; - mappatch_t patches[1]; -} ATTRPACK maptexture_t; - -#if defined(_MSC_VER) -#pragma pack() -#endif - - -// Store lists of lumps for F_START/F_END etc. -typedef struct -{ - UINT16 wadfile; - UINT16 firstlump; - size_t numlumps; -} lumplist_t; - // // Graphics. // SRB2 graphics for walls and sprites @@ -100,20 +43,6 @@ typedef struct size_t numspritelumps, max_spritelumps; -// textures -INT32 numtextures = 0; // total number of textures found, -// size of following tables - -texture_t **textures = NULL; -textureflat_t *texflats = NULL; -static UINT32 **texturecolumnofs; // column offset lookup table for each texture -static UINT8 **texturecache; // graphics data for each generated full-size texture - -INT32 *texturewidth; -fixed_t *textureheight; // needed for texture pegging - -INT32 *texturetranslation; - // needed for pre rendering sprcache_t *spritecachedinfo; @@ -127,106 +56,6 @@ size_t flatmemory, spritememory, texturememory; INT16 color8to16[256]; // remap color index to highcolor rgb value INT16 *hicolormaps; // test a 32k colormap remaps high -> high -// Painfully simple texture id cacheing to make maps load faster. :3 -static struct { - char name[9]; - INT32 id; -} *tidcache = NULL; -static INT32 tidcachelen = 0; - -// -// MAPTEXTURE_T CACHING -// When a texture is first needed, it counts the number of composite columns -// required in the texture and allocates space for a column directory and -// any new columns. -// The directory will simply point inside other patches if there is only one -// patch in a given column, but any columns with multiple patches will have -// new column_ts generated. -// - -// -// R_DrawColumnInCache -// Clip and draw a column from a patch into a cached post. -// -static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) -{ - INT32 count, position; - UINT8 *source; - INT32 topdelta, prevdelta = -1; - INT32 originy = originPatch->originy; - - (void)patchheight; // This parameter is unused - - while (patch->topdelta != 0xff) - { - topdelta = patch->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - source = (UINT8 *)patch + 3; - count = patch->length; - position = originy + topdelta; - - if (position < 0) - { - count += position; - source -= position; // start further down the column - position = 0; - } - - if (position + count > cacheheight) - count = cacheheight - position; - - if (count > 0) - M_Memcpy(cache + position, source, count); - - patch = (column_t *)((UINT8 *)patch + patch->length + 4); - } -} - -// -// R_DrawFlippedColumnInCache -// Similar to R_DrawColumnInCache; it draws the column inverted, however. -// -static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) -{ - INT32 count, position; - UINT8 *source, *dest; - INT32 topdelta, prevdelta = -1; - INT32 originy = originPatch->originy; - - while (patch->topdelta != 0xff) - { - topdelta = patch->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - topdelta = patchheight-patch->length-topdelta; - source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1) - count = patch->length; - position = originy + topdelta; - - if (position < 0) - { - count += position; - source += position; // start further UP the column - position = 0; - } - - if (position + count > cacheheight) - count = cacheheight - position; - - dest = cache + position; - if (count > 0) - { - for (; dest < cache + position + count; --source) - *dest++ = *source; - } - - patch = (column_t *)((UINT8 *)patch + patch->length + 4); - } -} - UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha) { RGBA_t output; @@ -334,1179 +163,6 @@ UINT8 ASTBlendPixel_8bpp(UINT8 background, UINT8 foreground, int style, UINT8 al return background; } -// -// R_DrawBlendColumnInCache -// Draws a translucent column into the cache, applying a half-cooked equation to get a proper translucency value (Needs code in R_GenerateTexture()). -// -static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) -{ - INT32 count, position; - UINT8 *source, *dest; - INT32 topdelta, prevdelta = -1; - INT32 originy = originPatch->originy; - - (void)patchheight; // This parameter is unused - - while (patch->topdelta != 0xff) - { - topdelta = patch->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - source = (UINT8 *)patch + 3; - count = patch->length; - position = originy + topdelta; - - if (position < 0) - { - count += position; - source -= position; // start further down the column - position = 0; - } - - if (position + count > cacheheight) - count = cacheheight - position; - - dest = cache + position; - if (count > 0) - { - for (; dest < cache + position + count; source++, dest++) - if (*source != 0xFF) - *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); - } - - patch = (column_t *)((UINT8 *)patch + patch->length + 4); - } -} - -// -// R_DrawBlendFlippedColumnInCache -// Similar to the one above except that the column is inverted. -// -static inline void R_DrawBlendFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) -{ - INT32 count, position; - UINT8 *source, *dest; - INT32 topdelta, prevdelta = -1; - INT32 originy = originPatch->originy; - - while (patch->topdelta != 0xff) - { - topdelta = patch->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - topdelta = patchheight-patch->length-topdelta; - source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1) - count = patch->length; - position = originy + topdelta; - - if (position < 0) - { - count += position; - source += position; // start further UP the column - position = 0; - } - - if (position + count > cacheheight) - count = cacheheight - position; - - dest = cache + position; - if (count > 0) - { - for (; dest < cache + position + count; --source, dest++) - if (*source != 0xFF) - *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); - } - - patch = (column_t *)((UINT8 *)patch + patch->length + 4); - } -} - -// -// R_GenerateTexture -// -// Allocate space for full size texture, either single patch or 'composite' -// Build the full textures from patches. -// The texture caching system is a little more hungry of memory, but has -// been simplified for the sake of highcolor (lol), dynamic ligthing, & speed. -// -// This is not optimised, but it's supposed to be executed only once -// per level, when enough memory is available. -// -static UINT8 *R_GenerateTexture(size_t texnum) -{ - UINT8 *block; - UINT8 *blocktex; - texture_t *texture; - texpatch_t *patch; - patch_t *realpatch; - UINT8 *pdata; - int x, x1, x2, i, width, height; - size_t blocksize; - column_t *patchcol; - UINT8 *colofs; - - UINT16 wadnum; - lumpnum_t lumpnum; - size_t lumplength; - - I_Assert(texnum <= (size_t)numtextures); - texture = textures[texnum]; - I_Assert(texture != NULL); - - // allocate texture column offset lookup - - // single-patch textures can have holes in them and may be used on - // 2sided lines so they need to be kept in 'packed' format - // BUT this is wrong for skies and walls with over 255 pixels, - // so check if there's holes and if not strip the posts. - if (texture->patchcount == 1) - { - boolean holey = false; - patch = texture->patches; - - wadnum = patch->wad; - lumpnum = patch->lump; - lumplength = W_LumpLengthPwad(wadnum, lumpnum); - pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); - realpatch = (patch_t *)pdata; - -#ifndef NO_PNG_LUMPS - if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) - goto multipatch; -#endif -#ifdef WALLFLATS - if (texture->type == TEXTURETYPE_FLAT) - goto multipatch; -#endif - - // Check the patch for holes. - if (texture->width > SHORT(realpatch->width) || texture->height > SHORT(realpatch->height)) - holey = true; - colofs = (UINT8 *)realpatch->columnofs; - for (x = 0; x < texture->width && !holey; x++) - { - column_t *col = (column_t *)((UINT8 *)realpatch + LONG(*(UINT32 *)&colofs[x<<2])); - INT32 topdelta, prevdelta = -1, y = 0; - while (col->topdelta != 0xff) - { - topdelta = col->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - if (topdelta > y) - break; - y = topdelta + col->length + 1; - col = (column_t *)((UINT8 *)col + col->length + 4); - } - if (y < texture->height) - holey = true; // this texture is HOLEy! D: - } - - // If the patch uses transparency, we have to save it this way. - if (holey) - { - texture->holes = true; - texture->flip = patch->flip; - blocksize = lumplength; - block = Z_Calloc(blocksize, PU_STATIC, // will change tag at end of this function - &texturecache[texnum]); - M_Memcpy(block, realpatch, blocksize); - texturememory += blocksize; - - // use the patch's column lookup - colofs = (block + 8); - texturecolumnofs[texnum] = (UINT32 *)colofs; - blocktex = block; - if (patch->flip & 1) // flip the patch horizontally - { - UINT8 *realcolofs = (UINT8 *)realpatch->columnofs; - for (x = 0; x < texture->width; x++) - *(UINT32 *)&colofs[x<<2] = realcolofs[( texture->width-1-x )<<2]; // swap with the offset of the other side of the texture - } - // we can't as easily flip the patch vertically sadly though, - // we have wait until the texture itself is drawn to do that - for (x = 0; x < texture->width; x++) - *(UINT32 *)&colofs[x<<2] = LONG(LONG(*(UINT32 *)&colofs[x<<2]) + 3); - goto done; - } - - // Otherwise, do multipatch format. - } - - // multi-patch textures (or 'composite') - multipatch: - texture->holes = false; - texture->flip = 0; - blocksize = (texture->width * 4) + (texture->width * texture->height); - texturememory += blocksize; - block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]); - - memset(block, TRANSPARENTPIXEL, blocksize+1); // Transparency hack - - // columns lookup table - colofs = block; - texturecolumnofs[texnum] = (UINT32 *)colofs; - - // texture data after the lookup table - blocktex = block + (texture->width*4); - - // Composite the columns together. - for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) - { - boolean dealloc = true; - static void (*ColumnDrawerPointer)(column_t *, UINT8 *, texpatch_t *, INT32, INT32); // Column drawing function pointer. - if (patch->style != AST_COPY) - ColumnDrawerPointer = (patch->flip & 2) ? R_DrawBlendFlippedColumnInCache : R_DrawBlendColumnInCache; - else - ColumnDrawerPointer = (patch->flip & 2) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache; - - wadnum = patch->wad; - lumpnum = patch->lump; - pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); - lumplength = W_LumpLengthPwad(wadnum, lumpnum); - realpatch = (patch_t *)pdata; - dealloc = true; - -#ifndef NO_PNG_LUMPS - if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) - { - // Dummy variables. - INT32 pngwidth, pngheight; - realpatch = (patch_t *)Picture_PNGConvert((UINT8 *)realpatch, PICFMT_PATCH, &pngwidth, &pngheight, NULL, NULL, lumplength, NULL, 0); - } - else -#endif -#ifdef WALLFLATS - if (texture->type == TEXTURETYPE_FLAT) - realpatch = (patch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_PATCH, 0, NULL, texture->width, texture->height, 0, 0, 0); - else -#endif - { - (void)lumplength; - dealloc = false; - } - - x1 = patch->originx; - width = SHORT(realpatch->width); - height = SHORT(realpatch->height); - x2 = x1 + width; - - if (x1 > texture->width || x2 < 0) - continue; // patch not located within texture's x bounds, ignore - - if (patch->originy > texture->height || (patch->originy + height) < 0) - continue; // patch not located within texture's y bounds, ignore - - // patch is actually inside the texture! - // now check if texture is partly off-screen and adjust accordingly - - // left edge - if (x1 < 0) - x = 0; - else - x = x1; - - // right edge - if (x2 > texture->width) - x2 = texture->width; - - for (; x < x2; x++) - { - if (patch->flip & 1) - patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[(x1+width-1)-x])); - else - patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1])); - - // generate column ofset lookup - *(UINT32 *)&colofs[x<<2] = LONG((x * texture->height) + (texture->width*4)); - ColumnDrawerPointer(patchcol, block + LONG(*(UINT32 *)&colofs[x<<2]), patch, texture->height, height); - } - - if (dealloc) - Z_Free(realpatch); - } - -done: - // Now that the texture has been built in column cache, it is purgable from zone memory. - Z_ChangeTag(block, PU_CACHE); - return blocktex; -} - -// -// R_GetTextureNum -// -// Returns the actual texture id that we should use. -// This can either be texnum, the current frame for texnum's anim (if animated), -// or 0 if not valid. -// -INT32 R_GetTextureNum(INT32 texnum) -{ - if (texnum < 0 || texnum >= numtextures) - return 0; - return texturetranslation[texnum]; -} - -// -// R_CheckTextureCache -// -// Use this if you need to make sure the texture is cached before R_GetColumn calls -// e.g.: midtextures and FOF walls -// -void R_CheckTextureCache(INT32 tex) -{ - if (!texturecache[tex]) - R_GenerateTexture(tex); -} - -// -// R_GetColumn -// -UINT8 *R_GetColumn(fixed_t tex, INT32 col) -{ - UINT8 *data; - INT32 width = texturewidth[tex]; - - if (width & (width - 1)) - col = (UINT32)col % width; - else - col &= (width - 1); - - data = texturecache[tex]; - if (!data) - data = R_GenerateTexture(tex); - - return data + LONG(texturecolumnofs[tex][col]); -} - -// convert flats to hicolor as they are requested -// -UINT8 *R_GetFlat(lumpnum_t flatlumpnum) -{ - return W_CacheLumpNum(flatlumpnum, PU_CACHE); -} - -// -// Empty the texture cache (used for load wad at runtime) -// -void R_FlushTextureCache(void) -{ - INT32 i; - - if (numtextures) - for (i = 0; i < numtextures; i++) - Z_Free(texturecache[i]); -} - -// Need these prototypes for later; defining them here instead of r_data.h so they're "private" -int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum); -void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index); - -// -// R_LoadTextures -// Initializes the texture list with the textures from the world map. -// -#define TX_START "TX_START" -#define TX_END "TX_END" -void R_LoadTextures(void) -{ - INT32 i, w; - UINT16 j; - UINT16 texstart, texend, texturesLumpPos; - patch_t *patchlump; - texpatch_t *patch; - texture_t *texture; - - // Free previous memory before numtextures change. - if (numtextures) - { - for (i = 0; i < numtextures; i++) - { - Z_Free(textures[i]); - Z_Free(texturecache[i]); - } - Z_Free(texturetranslation); - Z_Free(textures); - Z_Free(texflats); - } - - // Load patches and textures. - - // Get the number of textures to check. - // NOTE: Make SURE the system does not process - // the markers. - // This system will allocate memory for all duplicate/patched textures even if it never uses them, - // but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures. - for (w = 0, numtextures = 0; w < numwadfiles; w++) - { - // Count the textures from TEXTURES lumps - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); - while (texturesLumpPos != INT16_MAX) - { - numtextures += R_CountTexturesInTEXTURESLump((UINT16)w, (UINT16)texturesLumpPos); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); - } - - // Count single-patch textures - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); - } - else - { - texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0); - texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); - } - - if (texstart == INT16_MAX || texend == INT16_MAX) -#ifdef WALLFLATS - goto countflats; -#else - continue; -#endif - - texstart++; // Do not count the first marker - - // PK3s have subfolders, so we can't just make a simple sum - if (wadfiles[w]->type == RET_PK3) - { - for (j = texstart; j < texend; j++) - { - if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it - numtextures++; - } - } - else // Add all the textures between TX_START and TX_END - { - numtextures += (UINT32)(texend - texstart); - } - -#ifdef WALLFLATS -countflats: - // Count flats - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); - } - else - { - texstart = W_CheckNumForNamePwad("F_START", (UINT16)w, 0); - texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); - } - - if (texstart == INT16_MAX || texend == INT16_MAX) - continue; - - texstart++; // Do not count the first marker - - // PK3s have subfolders, so we can't just make a simple sum - if (wadfiles[w]->type == RET_PK3) - { - for (j = texstart; j < texend; j++) - { - if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it - numtextures++; - } - } - else // Add all the textures between F_START and F_END - { - numtextures += (UINT32)(texend - texstart); - } -#endif - } - - // If no textures found by this point, bomb out - if (!numtextures) - I_Error("No textures detected in any WADs!\n"); - - // Allocate memory and initialize to 0 for all the textures we are initialising. - // There are actually 5 buffers allocated in one for convenience. - textures = Z_Calloc((numtextures * sizeof(void *)) * 5, PU_STATIC, NULL); - texflats = Z_Calloc((numtextures * sizeof(*texflats)), PU_STATIC, NULL); - - // Allocate texture column offset table. - texturecolumnofs = (void *)((UINT8 *)textures + (numtextures * sizeof(void *))); - // Allocate texture referencing cache. - texturecache = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2)); - // Allocate texture width table. - texturewidth = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3)); - // Allocate texture height table. - textureheight = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 4)); - // Create translation table for global animation. - texturetranslation = Z_Malloc((numtextures + 1) * sizeof(*texturetranslation), PU_STATIC, NULL); - - for (i = 0; i < numtextures; i++) - texturetranslation[i] = i; - - for (i = 0, w = 0; w < numwadfiles; w++) - { - // Get the lump numbers for the markers in the WAD, if they exist. - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); - while (texturesLumpPos != INT16_MAX) - { - R_ParseTEXTURESLump(w, texturesLumpPos, &i); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); - } - } - else - { - texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0); - texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); - if (texturesLumpPos != INT16_MAX) - R_ParseTEXTURESLump(w, texturesLumpPos, &i); - } - - if (texstart == INT16_MAX || texend == INT16_MAX) -#ifdef WALLFLATS - goto checkflats; -#else - continue; -#endif - - texstart++; // Do not count the first marker - - // Work through each lump between the markers in the WAD. - for (j = 0; j < (texend - texstart); j++) - { - UINT16 wadnum = (UINT16)w; - lumpnum_t lumpnum = texstart + j; -#ifndef NO_PNG_LUMPS - size_t lumplength; -#endif - - if (wadfiles[w]->type == RET_PK3) - { - if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder - continue; // If it is then SKIP IT - } - - patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); -#ifndef NO_PNG_LUMPS - lumplength = W_LumpLengthPwad(wadnum, lumpnum); -#endif - - //CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height); - texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); - - // Set texture properties. - M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); - -#ifndef NO_PNG_LUMPS - if (Picture_IsLumpPNG((UINT8 *)patchlump, lumplength)) - { - INT16 width = 0, height = 0; - Picture_PNGDimensions((UINT8 *)patchlump, &width, &height, lumplength); - texture->width = width; - texture->height = height; - } - else -#endif - { - texture->width = SHORT(patchlump->width); - texture->height = SHORT(patchlump->height); - } - - texture->type = TEXTURETYPE_SINGLEPATCH; - texture->patchcount = 1; - texture->holes = false; - texture->flip = 0; - - // Allocate information for the texture's patches. - patch = &texture->patches[0]; - - patch->originx = patch->originy = 0; - patch->wad = (UINT16)w; - patch->lump = texstart + j; - patch->flip = 0; - - Z_Unlock(patchlump); - - texturewidth[i] = texture->width; - textureheight[i] = texture->height << FRACBITS; - i++; - } - -#ifdef WALLFLATS -checkflats: - // Yes - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); - } - else - { - texstart = W_CheckNumForNamePwad("F_START", (UINT16)w, 0); - texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); - } - - if (texstart == INT16_MAX || texend == INT16_MAX) - continue; - - texstart++; // Do not count the first marker - - // Work through each lump between the markers in the WAD. - for (j = 0; j < (texend - texstart); j++) - { - UINT8 *flatlump; - UINT16 wadnum = (UINT16)w; - lumpnum_t lumpnum = texstart + j; - size_t lumplength; - size_t flatsize = 0; - - if (wadfiles[w]->type == RET_PK3) - { - if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder - continue; // If it is then SKIP IT - } - - flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); - lumplength = W_LumpLengthPwad(wadnum, lumpnum); - - switch (lumplength) - { - case 4194304: // 2048x2048 lump - flatsize = 2048; - break; - case 1048576: // 1024x1024 lump - flatsize = 1024; - break; - case 262144:// 512x512 lump - flatsize = 512; - break; - case 65536: // 256x256 lump - flatsize = 256; - break; - case 16384: // 128x128 lump - flatsize = 128; - break; - case 1024: // 32x32 lump - flatsize = 32; - break; - default: // 64x64 lump - flatsize = 64; - break; - } - - //CONS_Printf("\n\"%s\" is a flat, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),flatsize,flatsize); - texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); - - // Set texture properties. - M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); - -#ifndef NO_PNG_LUMPS - if (Picture_IsLumpPNG((UINT8 *)flatlump, lumplength)) - { - INT16 width = 0, height = 0; - Picture_PNGDimensions((UINT8 *)flatlump, &width, &height, lumplength); - texture->width = width; - texture->height = height; - } - else -#endif - texture->width = texture->height = flatsize; - - texture->type = TEXTURETYPE_FLAT; - texture->patchcount = 1; - texture->holes = false; - texture->flip = 0; - - // Allocate information for the texture's patches. - patch = &texture->patches[0]; - - patch->originx = patch->originy = 0; - patch->wad = (UINT16)w; - patch->lump = texstart + j; - patch->flip = 0; - - Z_Unlock(flatlump); - - texturewidth[i] = texture->width; - textureheight[i] = texture->height << FRACBITS; - i++; - } -#endif - } - -#ifdef HWRENDER - if (rendermode == render_opengl) - HWR_LoadTextures(numtextures); -#endif -} - -static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch) -{ - char *texturesToken; - size_t texturesTokenLength; - char *endPos; - char *patchName = NULL; - INT16 patchXPos; - INT16 patchYPos; - UINT8 flip = 0; - UINT8 alpha = 255; - enum patchalphastyle style = AST_COPY; - texpatch_t *resultPatch = NULL; - lumpnum_t patchLumpNum; - - // Patch identifier - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch name should be"); - } - texturesTokenLength = strlen(texturesToken); - if (texturesTokenLength>8) - { - I_Error("Error parsing TEXTURES lump: Patch name \"%s\" exceeds 8 characters",texturesToken); - } - else - { - if (patchName != NULL) - { - Z_Free(patchName); - } - patchName = (char *)Z_Malloc((texturesTokenLength+1)*sizeof(char),PU_STATIC,NULL); - M_Memcpy(patchName,texturesToken,texturesTokenLength*sizeof(char)); - patchName[texturesTokenLength] = '\0'; - } - - // Comma 1 - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after \"%s\"'s patch name should be",patchName); - } - if (strcmp(texturesToken,",")!=0) - { - I_Error("Error parsing TEXTURES lump: Expected \",\" after %s's patch name, got \"%s\"",patchName,texturesToken); - } - - // XPos - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s x coordinate should be",patchName); - } - endPos = NULL; -#ifndef AVOID_ERRNO - errno = 0; -#endif - patchXPos = strtol(texturesToken,&endPos,10); - (void)patchXPos; //unused for now - if (endPos == texturesToken // Empty string - || *endPos != '\0' // Not end of string -#ifndef AVOID_ERRNO - || errno == ERANGE // Number out-of-range -#endif - ) - { - I_Error("Error parsing TEXTURES lump: Expected an integer for patch \"%s\"'s x coordinate, got \"%s\"",patchName,texturesToken); - } - - // Comma 2 - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after patch \"%s\"'s x coordinate should be",patchName); - } - if (strcmp(texturesToken,",")!=0) - { - I_Error("Error parsing TEXTURES lump: Expected \",\" after patch \"%s\"'s x coordinate, got \"%s\"",patchName,texturesToken); - } - - // YPos - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s y coordinate should be",patchName); - } - endPos = NULL; -#ifndef AVOID_ERRNO - errno = 0; -#endif - patchYPos = strtol(texturesToken,&endPos,10); - (void)patchYPos; //unused for now - if (endPos == texturesToken // Empty string - || *endPos != '\0' // Not end of string -#ifndef AVOID_ERRNO - || errno == ERANGE // Number out-of-range -#endif - ) - { - I_Error("Error parsing TEXTURES lump: Expected an integer for patch \"%s\"'s y coordinate, got \"%s\"",patchName,texturesToken); - } - Z_Free(texturesToken); - - // Patch parameters block (OPTIONAL) - // added by Monster Iestyn (22/10/16) - - // Left Curly Brace - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - ; // move on and ignore, R_ParseTextures will deal with this - else - { - if (strcmp(texturesToken,"{")==0) - { - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters should be",patchName); - } - while (strcmp(texturesToken,"}")!=0) - { - if (stricmp(texturesToken, "ALPHA")==0) - { - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - alpha = 255*strtof(texturesToken, NULL); - } - else if (stricmp(texturesToken, "STYLE")==0) - { - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (stricmp(texturesToken, "TRANSLUCENT")==0) - style = AST_TRANSLUCENT; - else if (stricmp(texturesToken, "ADD")==0) - style = AST_ADD; - else if (stricmp(texturesToken, "SUBTRACT")==0) - style = AST_SUBTRACT; - else if (stricmp(texturesToken, "REVERSESUBTRACT")==0) - style = AST_REVERSESUBTRACT; - else if (stricmp(texturesToken, "MODULATE")==0) - style = AST_MODULATE; - } - else if (stricmp(texturesToken, "FLIPX")==0) - flip |= 1; - else if (stricmp(texturesToken, "FLIPY")==0) - flip |= 2; - Z_Free(texturesToken); - - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters or right curly brace should be",patchName); - } - } - } - else - { - // this is not what we wanted... - // undo last read so R_ParseTextures can re-get the token for its own purposes - M_UnGetToken(); - } - Z_Free(texturesToken); - } - - if (actuallyLoadPatch == true) - { - // Check lump exists - patchLumpNum = W_GetNumForName(patchName); - // If so, allocate memory for texpatch_t and fill 'er up - resultPatch = (texpatch_t *)Z_Malloc(sizeof(texpatch_t),PU_STATIC,NULL); - resultPatch->originx = patchXPos; - resultPatch->originy = patchYPos; - resultPatch->lump = patchLumpNum & 65535; - resultPatch->wad = patchLumpNum>>16; - resultPatch->flip = flip; - resultPatch->alpha = alpha; - resultPatch->style = style; - // Clean up a little after ourselves - Z_Free(patchName); - // Then return it - return resultPatch; - } - else - { - Z_Free(patchName); - return NULL; - } -} - -static texture_t *R_ParseTexture(boolean actuallyLoadTexture) -{ - char *texturesToken; - size_t texturesTokenLength; - char *endPos; - INT32 newTextureWidth; - INT32 newTextureHeight; - texture_t *resultTexture = NULL; - texpatch_t *newPatch; - char newTextureName[9]; // no longer dynamically allocated - - // Texture name - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture name should be"); - } - texturesTokenLength = strlen(texturesToken); - if (texturesTokenLength>8) - { - I_Error("Error parsing TEXTURES lump: Texture name \"%s\" exceeds 8 characters",texturesToken); - } - else - { - memset(&newTextureName, 0, 9); - M_Memcpy(newTextureName, texturesToken, texturesTokenLength); - // ^^ we've confirmed that the token is <= 8 characters so it will never overflow a 9 byte char buffer - strupr(newTextureName); // Just do this now so we don't have to worry about it - } - Z_Free(texturesToken); - - // Comma 1 - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after texture \"%s\"'s name should be",newTextureName); - } - else if (strcmp(texturesToken,",")!=0) - { - I_Error("Error parsing TEXTURES lump: Expected \",\" after texture \"%s\"'s name, got \"%s\"",newTextureName,texturesToken); - } - Z_Free(texturesToken); - - // Width - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture \"%s\"'s width should be",newTextureName); - } - endPos = NULL; -#ifndef AVOID_ERRNO - errno = 0; -#endif - newTextureWidth = strtol(texturesToken,&endPos,10); - if (endPos == texturesToken // Empty string - || *endPos != '\0' // Not end of string -#ifndef AVOID_ERRNO - || errno == ERANGE // Number out-of-range -#endif - || newTextureWidth < 0) // Number is not positive - { - I_Error("Error parsing TEXTURES lump: Expected a positive integer for texture \"%s\"'s width, got \"%s\"",newTextureName,texturesToken); - } - Z_Free(texturesToken); - - // Comma 2 - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after texture \"%s\"'s width should be",newTextureName); - } - if (strcmp(texturesToken,",")!=0) - { - I_Error("Error parsing TEXTURES lump: Expected \",\" after texture \"%s\"'s width, got \"%s\"",newTextureName,texturesToken); - } - Z_Free(texturesToken); - - // Height - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture \"%s\"'s height should be",newTextureName); - } - endPos = NULL; -#ifndef AVOID_ERRNO - errno = 0; -#endif - newTextureHeight = strtol(texturesToken,&endPos,10); - if (endPos == texturesToken // Empty string - || *endPos != '\0' // Not end of string -#ifndef AVOID_ERRNO - || errno == ERANGE // Number out-of-range -#endif - || newTextureHeight < 0) // Number is not positive - { - I_Error("Error parsing TEXTURES lump: Expected a positive integer for texture \"%s\"'s height, got \"%s\"",newTextureName,texturesToken); - } - Z_Free(texturesToken); - - // Left Curly Brace - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where open curly brace for texture \"%s\" should be",newTextureName); - } - if (strcmp(texturesToken,"{")==0) - { - if (actuallyLoadTexture) - { - // Allocate memory for a zero-patch texture. Obviously, we'll be adding patches momentarily. - resultTexture = (texture_t *)Z_Calloc(sizeof(texture_t),PU_STATIC,NULL); - M_Memcpy(resultTexture->name, newTextureName, 8); - resultTexture->width = newTextureWidth; - resultTexture->height = newTextureHeight; - resultTexture->type = TEXTURETYPE_COMPOSITE; - } - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch definition for texture \"%s\" should be",newTextureName); - } - while (strcmp(texturesToken,"}")!=0) - { - if (stricmp(texturesToken, "PATCH")==0) - { - Z_Free(texturesToken); - if (resultTexture) - { - // Get that new patch - newPatch = R_ParsePatch(true); - // Make room for the new patch - resultTexture = Z_Realloc(resultTexture, sizeof(texture_t) + (resultTexture->patchcount+1)*sizeof(texpatch_t), PU_STATIC, NULL); - // Populate the uninitialized values in the new patch entry of our array - M_Memcpy(&resultTexture->patches[resultTexture->patchcount], newPatch, sizeof(texpatch_t)); - // Account for the new number of patches in the texture - resultTexture->patchcount++; - // Then free up the memory assigned to R_ParsePatch, as it's unneeded now - Z_Free(newPatch); - } - else - { - R_ParsePatch(false); - } - } - else - { - I_Error("Error parsing TEXTURES lump: Expected \"PATCH\" in texture \"%s\", got \"%s\"",newTextureName,texturesToken); - } - - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch declaration or right curly brace for texture \"%s\" should be",newTextureName); - } - } - if (resultTexture && resultTexture->patchcount == 0) - { - I_Error("Error parsing TEXTURES lump: Texture \"%s\" must have at least one patch",newTextureName); - } - } - else - { - I_Error("Error parsing TEXTURES lump: Expected \"{\" for texture \"%s\", got \"%s\"",newTextureName,texturesToken); - } - Z_Free(texturesToken); - - if (actuallyLoadTexture) return resultTexture; - else return NULL; -} - -// Parses the TEXTURES lump... but just to count the number of textures. -int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum) -{ - char *texturesLump; - size_t texturesLumpLength; - char *texturesText; - UINT32 numTexturesInLump = 0; - char *texturesToken; - - // Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll - // need to make a space of memory where I can ensure that it will terminate - // correctly. Start by loading the relevant data from the WAD. - texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); - // If that didn't exist, we have nothing to do here. - if (texturesLump == NULL) return 0; - // If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly. - texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum); - texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL); - // Now move the contents of the lump into this new location. - memmove(texturesText,texturesLump,texturesLumpLength); - // Make damn well sure the last character in our new memory location is \0. - texturesText[texturesLumpLength] = '\0'; - // Finally, free up the memory from the first data load, because we really - // don't need it. - Z_Free(texturesLump); - - texturesToken = M_GetToken(texturesText); - while (texturesToken != NULL) - { - if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) - { - numTexturesInLump++; - Z_Free(texturesToken); - R_ParseTexture(false); - } - else - { - I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); - } - texturesToken = M_GetToken(NULL); - } - Z_Free(texturesToken); - Z_Free((void *)texturesText); - - return numTexturesInLump; -} - -// Parses the TEXTURES lump... for real, this time. -void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex) -{ - char *texturesLump; - size_t texturesLumpLength; - char *texturesText; - char *texturesToken; - texture_t *newTexture; - - I_Assert(texindex != NULL); - - // Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll - // need to make a space of memory where I can ensure that it will terminate - // correctly. Start by loading the relevant data from the WAD. - texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); - // If that didn't exist, we have nothing to do here. - if (texturesLump == NULL) return; - // If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly. - texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum); - texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL); - // Now move the contents of the lump into this new location. - memmove(texturesText,texturesLump,texturesLumpLength); - // Make damn well sure the last character in our new memory location is \0. - texturesText[texturesLumpLength] = '\0'; - // Finally, free up the memory from the first data load, because we really - // don't need it. - Z_Free(texturesLump); - - texturesToken = M_GetToken(texturesText); - while (texturesToken != NULL) - { - if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) - { - Z_Free(texturesToken); - // Get the new texture - newTexture = R_ParseTexture(true); - // Store the new texture - textures[*texindex] = newTexture; - texturewidth[*texindex] = newTexture->width; - textureheight[*texindex] = newTexture->height << FRACBITS; - // Increment i back in R_LoadTextures() - (*texindex)++; - } - else - { - I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); - } - texturesToken = M_GetToken(NULL); - } - Z_Free(texturesToken); - Z_Free((void *)texturesText); -} - #ifdef EXTRACOLORMAPLUMPS static lumplist_t *colormaplumps = NULL; ///\todo free leak static size_t numcolormaplumps = 0; @@ -1561,54 +217,6 @@ static void R_InitExtraColormaps(void) } #endif -// Search for flat name. -lumpnum_t R_GetFlatNumForName(const char *name) -{ - INT32 i; - lumpnum_t lump; - lumpnum_t start; - lumpnum_t end; - - // Scan wad files backwards so patched flats take preference. - for (i = numwadfiles - 1; i >= 0; i--) - { - switch (wadfiles[i]->type) - { - case RET_WAD: - if ((start = W_CheckNumForNamePwad("F_START", (UINT16)i, 0)) == INT16_MAX) - { - if ((start = W_CheckNumForNamePwad("FF_START", (UINT16)i, 0)) == INT16_MAX) - continue; - else if ((end = W_CheckNumForNamePwad("FF_END", (UINT16)i, start)) == INT16_MAX) - continue; - } - else - if ((end = W_CheckNumForNamePwad("F_END", (UINT16)i, start)) == INT16_MAX) - continue; - break; - case RET_PK3: - if ((start = W_CheckNumForFolderStartPK3("Flats/", i, 0)) == INT16_MAX) - continue; - if ((end = W_CheckNumForFolderEndPK3("Flats/", i, start)) == INT16_MAX) - continue; - break; - default: - continue; - } - - // Now find lump with specified name in that range. - lump = W_CheckNumForNamePwad(name, (UINT16)i, start); - if (lump < end) - { - lump += (i<<16); // found it, in our constraints - break; - } - lump = LUMPERROR; - } - - return lump; -} - // // R_InitSpriteLumps // Finds the width and hoffset of all sprites in the wad, so the sprite does not need to be @@ -2566,74 +1174,6 @@ void R_InitData(void) R_InitColormaps(); } -void R_ClearTextureNumCache(boolean btell) -{ - if (tidcache) - Z_Free(tidcache); - tidcache = NULL; - if (btell) - CONS_Debug(DBG_SETUP, "Fun Fact: There are %d textures used in this map.\n", tidcachelen); - tidcachelen = 0; -} - -// -// R_CheckTextureNumForName -// -// Check whether texture is available. Filter out NoTexture indicator. -// -INT32 R_CheckTextureNumForName(const char *name) -{ - INT32 i; - - // "NoTexture" marker. - if (name[0] == '-') - return 0; - - for (i = 0; i < tidcachelen; i++) - if (!strncasecmp(tidcache[i].name, name, 8)) - return tidcache[i].id; - - // Need to parse the list backwards, so textures loaded more recently are used in lieu of ones loaded earlier - //for (i = 0; i < numtextures; i++) <- old - for (i = (numtextures - 1); i >= 0; i--) // <- new - if (!strncasecmp(textures[i]->name, name, 8)) - { - tidcachelen++; - Z_Realloc(tidcache, tidcachelen * sizeof(*tidcache), PU_STATIC, &tidcache); - strncpy(tidcache[tidcachelen-1].name, name, 8); - tidcache[tidcachelen-1].name[8] = '\0'; -#ifndef ZDEBUG - CONS_Debug(DBG_SETUP, "texture #%s: %s\n", sizeu1(tidcachelen), tidcache[tidcachelen-1].name); -#endif - tidcache[tidcachelen-1].id = i; - return i; - } - - return -1; -} - -// -// R_TextureNumForName -// -// Calls R_CheckTextureNumForName, aborts with error message. -// -INT32 R_TextureNumForName(const char *name) -{ - const INT32 i = R_CheckTextureNumForName(name); - - if (i == -1) - { - static INT32 redwall = -2; - CONS_Debug(DBG_SETUP, "WARNING: R_TextureNumForName: %.8s not found\n", name); - if (redwall == -2) - redwall = R_CheckTextureNumForName("REDWALL"); - if (redwall != -1) - return redwall; - return 1; - } - return i; -} - // // R_PrecacheLevel // diff --git a/src/r_data.h b/src/r_data.h index f028f2f5d..452f5e434 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -22,103 +22,31 @@ #pragma interface #endif +// Store lists of lumps for F_START/F_END etc. +typedef struct +{ + UINT16 wadfile; + UINT16 firstlump; + size_t numlumps; +} lumplist_t; + // Possible alpha types for a patch. enum patchalphastyle {AST_COPY, AST_TRANSLUCENT, AST_ADD, AST_SUBTRACT, AST_REVERSESUBTRACT, AST_MODULATE, AST_OVERLAY}; UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha); UINT8 ASTBlendPixel_8bpp(UINT8 background, UINT8 foreground, int style, UINT8 alpha); -UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); - -// moved here for r_sky.c (texpatch_t is used) - -// A single patch from a texture definition, -// basically a rectangular area within -// the texture rectangle. -typedef struct -{ - // Block origin (always UL), which has already accounted for the internal origin of the patch. - INT16 originx, originy; - UINT16 wad, lump; - UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both - UINT8 alpha; // Translucency value - enum patchalphastyle style; -} texpatch_t; - -// texture type -enum -{ - TEXTURETYPE_UNKNOWN, - TEXTURETYPE_SINGLEPATCH, - TEXTURETYPE_COMPOSITE, -#ifdef WALLFLATS - TEXTURETYPE_FLAT, -#endif -}; - -// A maptexturedef_t describes a rectangular texture, -// which is composed of one or more mappatch_t structures -// that arrange graphic patches. -typedef struct -{ - // Keep name for switch changing, etc. - char name[8]; - UINT8 type; // TEXTURETYPE_ - INT16 width, height; - boolean holes; - UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both - - // All the patches[patchcount] are drawn back to front into the cached texture. - INT16 patchcount; - texpatch_t patches[0]; -} texture_t; - -typedef struct -{ - UINT8 *flat; - INT16 width, height; -} textureflat_t; - -// all loaded and prepared textures from the start of the game -extern texture_t **textures; -extern textureflat_t *texflats; - -extern INT32 *texturewidth; -extern fixed_t *textureheight; // needed for texture pegging - extern INT16 color8to16[256]; // remap color index to highcolor extern INT16 *hicolormaps; // remap high colors to high colors.. extern CV_PossibleValue_t Color_cons_t[]; -// Load TEXTURES definitions, create lookup tables -void R_LoadTextures(void); -void R_FlushTextureCache(void); - -INT32 R_GetTextureNum(INT32 texnum); -void R_CheckTextureCache(INT32 tex); - -// Retrieve column data for span blitting. -UINT8 *R_GetColumn(fixed_t tex, INT32 col); -UINT8 *R_GetFlat(lumpnum_t flatnum); - // I/O, setting up the stuff. void R_InitData(void); void R_PrecacheLevel(void); extern size_t flatmemory, spritememory, texturememory; -// Retrieval. -// Floor/ceiling opaque texture tiles, -// lookup by name. For animation? -lumpnum_t R_GetFlatNumForName(const char *name); - -// Called by P_Ticker for switches and animations, -// returns the texture number for the texture name. -void R_ClearTextureNumCache(boolean btell); -INT32 R_TextureNumForName(const char *name); -INT32 R_CheckTextureNumForName(const char *name); - // Extra Colormap lumps (C_START/C_END) are not used anywhere // Uncomment to enable //#define EXTRACOLORMAPLUMPS @@ -173,6 +101,4 @@ const char *R_NameForColormap(extracolormap_t *extra_colormap); UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); -extern INT32 numtextures; - #endif diff --git a/src/r_local.h b/src/r_local.h index 379d55205..2c72624ff 100644 --- a/src/r_local.h +++ b/src/r_local.h @@ -31,6 +31,7 @@ #include "r_plane.h" #include "r_sky.h" #include "r_data.h" +#include "r_textures.h" #include "r_things.h" #include "r_draw.h" diff --git a/src/r_main.h b/src/r_main.h index 143cc1de0..e6f33d357 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -16,6 +16,7 @@ #include "d_player.h" #include "r_data.h" +#include "r_textures.h" // // POV related. diff --git a/src/r_picformats.c b/src/r_picformats.c index cafe30d7e..b907ac4d4 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -16,6 +16,7 @@ #include "dehacked.h" #include "i_video.h" #include "r_data.h" +#include "r_textures.h" #include "r_draw.h" #include "r_picformats.h" #include "r_things.h" @@ -66,6 +67,8 @@ fixed_t sinang2rad[ROTANGLES]; * \param intopoffset Input picture top offset, for patches. * \param flags Input picture flags. * \return A pointer to the converted picture. + * \sa Picture_PatchConvert + * \sa Picture_FlatConvert */ void *Picture_Convert( pictureformat_t informat, void *picture, pictureformat_t outformat, diff --git a/src/r_plane.c b/src/r_plane.c index 43c097a23..982354a5f 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -19,6 +19,7 @@ #include "p_setup.h" // levelflats #include "p_slopes.h" #include "r_data.h" +#include "r_textures.h" #include "r_local.h" #include "r_state.h" #include "r_splats.h" // faB(21jan):testing diff --git a/src/r_plane.h b/src/r_plane.h index d9ba5c56b..5d92d2e4b 100644 --- a/src/r_plane.h +++ b/src/r_plane.h @@ -16,6 +16,7 @@ #include "screen.h" // needs MAXVIDWIDTH/MAXVIDHEIGHT #include "r_data.h" +#include "r_textures.h" #include "p_polyobj.h" #define MAXVISPLANES 512 diff --git a/src/r_portal.h b/src/r_portal.h index c46ddfdab..642a5b211 100644 --- a/src/r_portal.h +++ b/src/r_portal.h @@ -15,6 +15,7 @@ #define __R_PORTAL__ #include "r_data.h" +#include "r_textures.h" #include "r_plane.h" // visplanes /** Portal structure for the software renderer. diff --git a/src/r_textures.c b/src/r_textures.c new file mode 100644 index 000000000..c9c8fd937 --- /dev/null +++ b/src/r_textures.c @@ -0,0 +1,1441 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_textures.c +/// \brief Texture generation. + +#include "doomdef.h" +#include "g_game.h" +#include "i_video.h" +#include "r_local.h" +#include "r_sky.h" +#include "p_local.h" +#include "m_misc.h" +#include "r_data.h" +#include "r_textures.h" +#include "r_picformats.h" +#include "w_wad.h" +#include "z_zone.h" +#include "p_setup.h" // levelflats +#include "byteptr.h" +#include "dehacked.h" + +// I don't know what this is even for, but r_data.c had it. +#ifdef _WIN32 +#include // alloca(sizeof) +#endif + +#ifdef HWRENDER +#include "hardware/hw_main.h" // HWR_LoadTextures +#endif + +#include + +// +// MAPTEXTURE_T CACHING +// When a texture is first needed, it counts the number of composite columns +// required in the texture and allocates space for a column directory and +// any new columns. +// The directory will simply point inside other patches if there is only one +// patch in a given column, but any columns with multiple patches will have +// new column_ts generated. +// + +INT32 numtextures = 0; // total number of textures found, +// size of following tables + +texture_t **textures = NULL; +textureflat_t *texflats = NULL; +UINT32 **texturecolumnofs; // column offset lookup table for each texture +UINT8 **texturecache; // graphics data for each generated full-size texture + +INT32 *texturewidth; +fixed_t *textureheight; // needed for texture pegging + +INT32 *texturetranslation; + +// Painfully simple texture id cacheing to make maps load faster. :3 +static struct { + char name[9]; + INT32 id; +} *tidcache = NULL; +static INT32 tidcachelen = 0; + +// +// R_DrawColumnInCache +// Clip and draw a column from a patch into a cached post. +// +static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +{ + INT32 count, position; + UINT8 *source; + INT32 topdelta, prevdelta = -1; + INT32 originy = originPatch->originy; + + (void)patchheight; // This parameter is unused + + while (patch->topdelta != 0xff) + { + topdelta = patch->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + source = (UINT8 *)patch + 3; + count = patch->length; + position = originy + topdelta; + + if (position < 0) + { + count += position; + source -= position; // start further down the column + position = 0; + } + + if (position + count > cacheheight) + count = cacheheight - position; + + if (count > 0) + M_Memcpy(cache + position, source, count); + + patch = (column_t *)((UINT8 *)patch + patch->length + 4); + } +} + +// +// R_DrawFlippedColumnInCache +// Similar to R_DrawColumnInCache; it draws the column inverted, however. +// +static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +{ + INT32 count, position; + UINT8 *source, *dest; + INT32 topdelta, prevdelta = -1; + INT32 originy = originPatch->originy; + + while (patch->topdelta != 0xff) + { + topdelta = patch->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + topdelta = patchheight-patch->length-topdelta; + source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1) + count = patch->length; + position = originy + topdelta; + + if (position < 0) + { + count += position; + source += position; // start further UP the column + position = 0; + } + + if (position + count > cacheheight) + count = cacheheight - position; + + dest = cache + position; + if (count > 0) + { + for (; dest < cache + position + count; --source) + *dest++ = *source; + } + + patch = (column_t *)((UINT8 *)patch + patch->length + 4); + } +} + +// +// R_DrawBlendColumnInCache +// Draws a translucent column into the cache, applying a half-cooked equation to get a proper translucency value (Needs code in R_GenerateTexture()). +// +static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +{ + INT32 count, position; + UINT8 *source, *dest; + INT32 topdelta, prevdelta = -1; + INT32 originy = originPatch->originy; + + (void)patchheight; // This parameter is unused + + while (patch->topdelta != 0xff) + { + topdelta = patch->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + source = (UINT8 *)patch + 3; + count = patch->length; + position = originy + topdelta; + + if (position < 0) + { + count += position; + source -= position; // start further down the column + position = 0; + } + + if (position + count > cacheheight) + count = cacheheight - position; + + dest = cache + position; + if (count > 0) + { + for (; dest < cache + position + count; source++, dest++) + if (*source != 0xFF) + *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); + } + + patch = (column_t *)((UINT8 *)patch + patch->length + 4); + } +} + +// +// R_DrawBlendFlippedColumnInCache +// Similar to the one above except that the column is inverted. +// +static inline void R_DrawBlendFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +{ + INT32 count, position; + UINT8 *source, *dest; + INT32 topdelta, prevdelta = -1; + INT32 originy = originPatch->originy; + + while (patch->topdelta != 0xff) + { + topdelta = patch->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + topdelta = patchheight-patch->length-topdelta; + source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1) + count = patch->length; + position = originy + topdelta; + + if (position < 0) + { + count += position; + source += position; // start further UP the column + position = 0; + } + + if (position + count > cacheheight) + count = cacheheight - position; + + dest = cache + position; + if (count > 0) + { + for (; dest < cache + position + count; --source, dest++) + if (*source != 0xFF) + *dest = ASTBlendPixel_8bpp(*dest, *source, originPatch->style, originPatch->alpha); + } + + patch = (column_t *)((UINT8 *)patch + patch->length + 4); + } +} + +// +// R_GenerateTexture +// +// Allocate space for full size texture, either single patch or 'composite' +// Build the full textures from patches. +// The texture caching system is a little more hungry of memory, but has +// been simplified for the sake of highcolor (lol), dynamic ligthing, & speed. +// +// This is not optimised, but it's supposed to be executed only once +// per level, when enough memory is available. +// +UINT8 *R_GenerateTexture(size_t texnum) +{ + UINT8 *block; + UINT8 *blocktex; + texture_t *texture; + texpatch_t *patch; + patch_t *realpatch; + UINT8 *pdata; + int x, x1, x2, i, width, height; + size_t blocksize; + column_t *patchcol; + UINT8 *colofs; + + UINT16 wadnum; + lumpnum_t lumpnum; + size_t lumplength; + + I_Assert(texnum <= (size_t)numtextures); + texture = textures[texnum]; + I_Assert(texture != NULL); + + // allocate texture column offset lookup + + // single-patch textures can have holes in them and may be used on + // 2sided lines so they need to be kept in 'packed' format + // BUT this is wrong for skies and walls with over 255 pixels, + // so check if there's holes and if not strip the posts. + if (texture->patchcount == 1) + { + boolean holey = false; + patch = texture->patches; + + wadnum = patch->wad; + lumpnum = patch->lump; + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + realpatch = (patch_t *)pdata; + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) + goto multipatch; +#endif +#ifdef WALLFLATS + if (texture->type == TEXTURETYPE_FLAT) + goto multipatch; +#endif + + // Check the patch for holes. + if (texture->width > SHORT(realpatch->width) || texture->height > SHORT(realpatch->height)) + holey = true; + colofs = (UINT8 *)realpatch->columnofs; + for (x = 0; x < texture->width && !holey; x++) + { + column_t *col = (column_t *)((UINT8 *)realpatch + LONG(*(UINT32 *)&colofs[x<<2])); + INT32 topdelta, prevdelta = -1, y = 0; + while (col->topdelta != 0xff) + { + topdelta = col->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + if (topdelta > y) + break; + y = topdelta + col->length + 1; + col = (column_t *)((UINT8 *)col + col->length + 4); + } + if (y < texture->height) + holey = true; // this texture is HOLEy! D: + } + + // If the patch uses transparency, we have to save it this way. + if (holey) + { + texture->holes = true; + texture->flip = patch->flip; + blocksize = lumplength; + block = Z_Calloc(blocksize, PU_STATIC, // will change tag at end of this function + &texturecache[texnum]); + M_Memcpy(block, realpatch, blocksize); + texturememory += blocksize; + + // use the patch's column lookup + colofs = (block + 8); + texturecolumnofs[texnum] = (UINT32 *)colofs; + blocktex = block; + if (patch->flip & 1) // flip the patch horizontally + { + UINT8 *realcolofs = (UINT8 *)realpatch->columnofs; + for (x = 0; x < texture->width; x++) + *(UINT32 *)&colofs[x<<2] = realcolofs[( texture->width-1-x )<<2]; // swap with the offset of the other side of the texture + } + // we can't as easily flip the patch vertically sadly though, + // we have wait until the texture itself is drawn to do that + for (x = 0; x < texture->width; x++) + *(UINT32 *)&colofs[x<<2] = LONG(LONG(*(UINT32 *)&colofs[x<<2]) + 3); + goto done; + } + + // Otherwise, do multipatch format. + } + + // multi-patch textures (or 'composite') + multipatch: + texture->holes = false; + texture->flip = 0; + blocksize = (texture->width * 4) + (texture->width * texture->height); + texturememory += blocksize; + block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]); + + memset(block, TRANSPARENTPIXEL, blocksize+1); // Transparency hack + + // columns lookup table + colofs = block; + texturecolumnofs[texnum] = (UINT32 *)colofs; + + // texture data after the lookup table + blocktex = block + (texture->width*4); + + // Composite the columns together. + for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) + { + boolean dealloc = true; + static void (*ColumnDrawerPointer)(column_t *, UINT8 *, texpatch_t *, INT32, INT32); // Column drawing function pointer. + if (patch->style != AST_COPY) + ColumnDrawerPointer = (patch->flip & 2) ? R_DrawBlendFlippedColumnInCache : R_DrawBlendColumnInCache; + else + ColumnDrawerPointer = (patch->flip & 2) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache; + + wadnum = patch->wad; + lumpnum = patch->lump; + pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + realpatch = (patch_t *)pdata; + dealloc = true; + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) + { + // Dummy variables. + INT32 pngwidth, pngheight; + realpatch = (patch_t *)Picture_PNGConvert((UINT8 *)realpatch, PICFMT_PATCH, &pngwidth, &pngheight, NULL, NULL, lumplength, NULL, 0); + } + else +#endif +#ifdef WALLFLATS + if (texture->type == TEXTURETYPE_FLAT) + realpatch = (patch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_PATCH, 0, NULL, texture->width, texture->height, 0, 0, 0); + else +#endif + { + (void)lumplength; + dealloc = false; + } + + x1 = patch->originx; + width = SHORT(realpatch->width); + height = SHORT(realpatch->height); + x2 = x1 + width; + + if (x1 > texture->width || x2 < 0) + continue; // patch not located within texture's x bounds, ignore + + if (patch->originy > texture->height || (patch->originy + height) < 0) + continue; // patch not located within texture's y bounds, ignore + + // patch is actually inside the texture! + // now check if texture is partly off-screen and adjust accordingly + + // left edge + if (x1 < 0) + x = 0; + else + x = x1; + + // right edge + if (x2 > texture->width) + x2 = texture->width; + + for (; x < x2; x++) + { + if (patch->flip & 1) + patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[(x1+width-1)-x])); + else + patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1])); + + // generate column ofset lookup + *(UINT32 *)&colofs[x<<2] = LONG((x * texture->height) + (texture->width*4)); + ColumnDrawerPointer(patchcol, block + LONG(*(UINT32 *)&colofs[x<<2]), patch, texture->height, height); + } + + if (dealloc) + Z_Free(realpatch); + } + +done: + // Now that the texture has been built in column cache, it is purgable from zone memory. + Z_ChangeTag(block, PU_CACHE); + return blocktex; +} + +// +// R_GetTextureNum +// +// Returns the actual texture id that we should use. +// This can either be texnum, the current frame for texnum's anim (if animated), +// or 0 if not valid. +// +INT32 R_GetTextureNum(INT32 texnum) +{ + if (texnum < 0 || texnum >= numtextures) + return 0; + return texturetranslation[texnum]; +} + +// +// R_CheckTextureCache +// +// Use this if you need to make sure the texture is cached before R_GetColumn calls +// e.g.: midtextures and FOF walls +// +void R_CheckTextureCache(INT32 tex) +{ + if (!texturecache[tex]) + R_GenerateTexture(tex); +} + +// +// R_GetColumn +// +UINT8 *R_GetColumn(fixed_t tex, INT32 col) +{ + UINT8 *data; + INT32 width = texturewidth[tex]; + + if (width & (width - 1)) + col = (UINT32)col % width; + else + col &= (width - 1); + + data = texturecache[tex]; + if (!data) + data = R_GenerateTexture(tex); + + return data + LONG(texturecolumnofs[tex][col]); +} + +// convert flats to hicolor as they are requested +// +UINT8 *R_GetFlat(lumpnum_t flatlumpnum) +{ + return W_CacheLumpNum(flatlumpnum, PU_CACHE); +} + +// +// Empty the texture cache (used for load wad at runtime) +// +void R_FlushTextureCache(void) +{ + INT32 i; + + if (numtextures) + for (i = 0; i < numtextures; i++) + Z_Free(texturecache[i]); +} + +// Need these prototypes for later; defining them here instead of r_data.h so they're "private" +int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum); +void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index); + +// +// R_LoadTextures +// Initializes the texture list with the textures from the world map. +// +#define TX_START "TX_START" +#define TX_END "TX_END" +void R_LoadTextures(void) +{ + INT32 i, w; + UINT16 j; + UINT16 texstart, texend, texturesLumpPos; + patch_t *patchlump; + texpatch_t *patch; + texture_t *texture; + + // Free previous memory before numtextures change. + if (numtextures) + { + for (i = 0; i < numtextures; i++) + { + Z_Free(textures[i]); + Z_Free(texturecache[i]); + } + Z_Free(texturetranslation); + Z_Free(textures); + Z_Free(texflats); + } + + // Load patches and textures. + + // Get the number of textures to check. + // NOTE: Make SURE the system does not process + // the markers. + // This system will allocate memory for all duplicate/patched textures even if it never uses them, + // but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures. + for (w = 0, numtextures = 0; w < numwadfiles; w++) + { + // Count the textures from TEXTURES lumps + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); + while (texturesLumpPos != INT16_MAX) + { + numtextures += R_CountTexturesInTEXTURESLump((UINT16)w, (UINT16)texturesLumpPos); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); + } + + // Count single-patch textures + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); + } + else + { + texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0); + texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); + } + + if (texstart == INT16_MAX || texend == INT16_MAX) +#ifdef WALLFLATS + goto countflats; +#else + continue; +#endif + + texstart++; // Do not count the first marker + + // PK3s have subfolders, so we can't just make a simple sum + if (wadfiles[w]->type == RET_PK3) + { + for (j = texstart; j < texend; j++) + { + if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it + numtextures++; + } + } + else // Add all the textures between TX_START and TX_END + { + numtextures += (UINT32)(texend - texstart); + } + +#ifdef WALLFLATS +countflats: + // Count flats + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); + } + else + { + texstart = W_CheckNumForNamePwad("F_START", (UINT16)w, 0); + texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); + } + + if (texstart == INT16_MAX || texend == INT16_MAX) + continue; + + texstart++; // Do not count the first marker + + // PK3s have subfolders, so we can't just make a simple sum + if (wadfiles[w]->type == RET_PK3) + { + for (j = texstart; j < texend; j++) + { + if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it + numtextures++; + } + } + else // Add all the textures between F_START and F_END + { + numtextures += (UINT32)(texend - texstart); + } +#endif + } + + // If no textures found by this point, bomb out + if (!numtextures) + I_Error("No textures detected in any WADs!\n"); + + // Allocate memory and initialize to 0 for all the textures we are initialising. + // There are actually 5 buffers allocated in one for convenience. + textures = Z_Calloc((numtextures * sizeof(void *)) * 5, PU_STATIC, NULL); + texflats = Z_Calloc((numtextures * sizeof(*texflats)), PU_STATIC, NULL); + + // Allocate texture column offset table. + texturecolumnofs = (void *)((UINT8 *)textures + (numtextures * sizeof(void *))); + // Allocate texture referencing cache. + texturecache = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2)); + // Allocate texture width table. + texturewidth = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3)); + // Allocate texture height table. + textureheight = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 4)); + // Create translation table for global animation. + texturetranslation = Z_Malloc((numtextures + 1) * sizeof(*texturetranslation), PU_STATIC, NULL); + + for (i = 0; i < numtextures; i++) + texturetranslation[i] = i; + + for (i = 0, w = 0; w < numwadfiles; w++) + { + // Get the lump numbers for the markers in the WAD, if they exist. + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); + while (texturesLumpPos != INT16_MAX) + { + R_ParseTEXTURESLump(w, texturesLumpPos, &i); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); + } + } + else + { + texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0); + texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); + if (texturesLumpPos != INT16_MAX) + R_ParseTEXTURESLump(w, texturesLumpPos, &i); + } + + if (texstart == INT16_MAX || texend == INT16_MAX) +#ifdef WALLFLATS + goto checkflats; +#else + continue; +#endif + + texstart++; // Do not count the first marker + + // Work through each lump between the markers in the WAD. + for (j = 0; j < (texend - texstart); j++) + { + UINT16 wadnum = (UINT16)w; + lumpnum_t lumpnum = texstart + j; +#ifndef NO_PNG_LUMPS + size_t lumplength; +#endif + + if (wadfiles[w]->type == RET_PK3) + { + if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder + continue; // If it is then SKIP IT + } + + patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); +#ifndef NO_PNG_LUMPS + lumplength = W_LumpLengthPwad(wadnum, lumpnum); +#endif + + //CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height); + texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); + + // Set texture properties. + M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)patchlump, lumplength)) + { + INT16 width = 0, height = 0; + Picture_PNGDimensions((UINT8 *)patchlump, &width, &height, lumplength); + texture->width = width; + texture->height = height; + } + else +#endif + { + texture->width = SHORT(patchlump->width); + texture->height = SHORT(patchlump->height); + } + + texture->type = TEXTURETYPE_SINGLEPATCH; + texture->patchcount = 1; + texture->holes = false; + texture->flip = 0; + + // Allocate information for the texture's patches. + patch = &texture->patches[0]; + + patch->originx = patch->originy = 0; + patch->wad = (UINT16)w; + patch->lump = texstart + j; + patch->flip = 0; + + Z_Unlock(patchlump); + + texturewidth[i] = texture->width; + textureheight[i] = texture->height << FRACBITS; + i++; + } + +#ifdef WALLFLATS +checkflats: + // Yes + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); + } + else + { + texstart = W_CheckNumForNamePwad("F_START", (UINT16)w, 0); + texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); + } + + if (texstart == INT16_MAX || texend == INT16_MAX) + continue; + + texstart++; // Do not count the first marker + + // Work through each lump between the markers in the WAD. + for (j = 0; j < (texend - texstart); j++) + { + UINT8 *flatlump; + UINT16 wadnum = (UINT16)w; + lumpnum_t lumpnum = texstart + j; + size_t lumplength; + size_t flatsize = 0; + + if (wadfiles[w]->type == RET_PK3) + { + if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder + continue; // If it is then SKIP IT + } + + flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + + switch (lumplength) + { + case 4194304: // 2048x2048 lump + flatsize = 2048; + break; + case 1048576: // 1024x1024 lump + flatsize = 1024; + break; + case 262144:// 512x512 lump + flatsize = 512; + break; + case 65536: // 256x256 lump + flatsize = 256; + break; + case 16384: // 128x128 lump + flatsize = 128; + break; + case 1024: // 32x32 lump + flatsize = 32; + break; + default: // 64x64 lump + flatsize = 64; + break; + } + + //CONS_Printf("\n\"%s\" is a flat, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),flatsize,flatsize); + texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); + + // Set texture properties. + M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)flatlump, lumplength)) + { + INT16 width = 0, height = 0; + Picture_PNGDimensions((UINT8 *)flatlump, &width, &height, lumplength); + texture->width = width; + texture->height = height; + } + else +#endif + texture->width = texture->height = flatsize; + + texture->type = TEXTURETYPE_FLAT; + texture->patchcount = 1; + texture->holes = false; + texture->flip = 0; + + // Allocate information for the texture's patches. + patch = &texture->patches[0]; + + patch->originx = patch->originy = 0; + patch->wad = (UINT16)w; + patch->lump = texstart + j; + patch->flip = 0; + + Z_Unlock(flatlump); + + texturewidth[i] = texture->width; + textureheight[i] = texture->height << FRACBITS; + i++; + } +#endif + } + +#ifdef HWRENDER + if (rendermode == render_opengl) + HWR_LoadTextures(numtextures); +#endif +} + +static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch) +{ + char *texturesToken; + size_t texturesTokenLength; + char *endPos; + char *patchName = NULL; + INT16 patchXPos; + INT16 patchYPos; + UINT8 flip = 0; + UINT8 alpha = 255; + enum patchalphastyle style = AST_COPY; + texpatch_t *resultPatch = NULL; + lumpnum_t patchLumpNum; + + // Patch identifier + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch name should be"); + } + texturesTokenLength = strlen(texturesToken); + if (texturesTokenLength>8) + { + I_Error("Error parsing TEXTURES lump: Patch name \"%s\" exceeds 8 characters",texturesToken); + } + else + { + if (patchName != NULL) + { + Z_Free(patchName); + } + patchName = (char *)Z_Malloc((texturesTokenLength+1)*sizeof(char),PU_STATIC,NULL); + M_Memcpy(patchName,texturesToken,texturesTokenLength*sizeof(char)); + patchName[texturesTokenLength] = '\0'; + } + + // Comma 1 + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after \"%s\"'s patch name should be",patchName); + } + if (strcmp(texturesToken,",")!=0) + { + I_Error("Error parsing TEXTURES lump: Expected \",\" after %s's patch name, got \"%s\"",patchName,texturesToken); + } + + // XPos + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s x coordinate should be",patchName); + } + endPos = NULL; +#ifndef AVOID_ERRNO + errno = 0; +#endif + patchXPos = strtol(texturesToken,&endPos,10); + (void)patchXPos; //unused for now + if (endPos == texturesToken // Empty string + || *endPos != '\0' // Not end of string +#ifndef AVOID_ERRNO + || errno == ERANGE // Number out-of-range +#endif + ) + { + I_Error("Error parsing TEXTURES lump: Expected an integer for patch \"%s\"'s x coordinate, got \"%s\"",patchName,texturesToken); + } + + // Comma 2 + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after patch \"%s\"'s x coordinate should be",patchName); + } + if (strcmp(texturesToken,",")!=0) + { + I_Error("Error parsing TEXTURES lump: Expected \",\" after patch \"%s\"'s x coordinate, got \"%s\"",patchName,texturesToken); + } + + // YPos + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s y coordinate should be",patchName); + } + endPos = NULL; +#ifndef AVOID_ERRNO + errno = 0; +#endif + patchYPos = strtol(texturesToken,&endPos,10); + (void)patchYPos; //unused for now + if (endPos == texturesToken // Empty string + || *endPos != '\0' // Not end of string +#ifndef AVOID_ERRNO + || errno == ERANGE // Number out-of-range +#endif + ) + { + I_Error("Error parsing TEXTURES lump: Expected an integer for patch \"%s\"'s y coordinate, got \"%s\"",patchName,texturesToken); + } + Z_Free(texturesToken); + + // Patch parameters block (OPTIONAL) + // added by Monster Iestyn (22/10/16) + + // Left Curly Brace + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + ; // move on and ignore, R_ParseTextures will deal with this + else + { + if (strcmp(texturesToken,"{")==0) + { + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters should be",patchName); + } + while (strcmp(texturesToken,"}")!=0) + { + if (stricmp(texturesToken, "ALPHA")==0) + { + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + alpha = 255*strtof(texturesToken, NULL); + } + else if (stricmp(texturesToken, "STYLE")==0) + { + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (stricmp(texturesToken, "TRANSLUCENT")==0) + style = AST_TRANSLUCENT; + else if (stricmp(texturesToken, "ADD")==0) + style = AST_ADD; + else if (stricmp(texturesToken, "SUBTRACT")==0) + style = AST_SUBTRACT; + else if (stricmp(texturesToken, "REVERSESUBTRACT")==0) + style = AST_REVERSESUBTRACT; + else if (stricmp(texturesToken, "MODULATE")==0) + style = AST_MODULATE; + } + else if (stricmp(texturesToken, "FLIPX")==0) + flip |= 1; + else if (stricmp(texturesToken, "FLIPY")==0) + flip |= 2; + Z_Free(texturesToken); + + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters or right curly brace should be",patchName); + } + } + } + else + { + // this is not what we wanted... + // undo last read so R_ParseTextures can re-get the token for its own purposes + M_UnGetToken(); + } + Z_Free(texturesToken); + } + + if (actuallyLoadPatch == true) + { + // Check lump exists + patchLumpNum = W_GetNumForName(patchName); + // If so, allocate memory for texpatch_t and fill 'er up + resultPatch = (texpatch_t *)Z_Malloc(sizeof(texpatch_t),PU_STATIC,NULL); + resultPatch->originx = patchXPos; + resultPatch->originy = patchYPos; + resultPatch->lump = patchLumpNum & 65535; + resultPatch->wad = patchLumpNum>>16; + resultPatch->flip = flip; + resultPatch->alpha = alpha; + resultPatch->style = style; + // Clean up a little after ourselves + Z_Free(patchName); + // Then return it + return resultPatch; + } + else + { + Z_Free(patchName); + return NULL; + } +} + +static texture_t *R_ParseTexture(boolean actuallyLoadTexture) +{ + char *texturesToken; + size_t texturesTokenLength; + char *endPos; + INT32 newTextureWidth; + INT32 newTextureHeight; + texture_t *resultTexture = NULL; + texpatch_t *newPatch; + char newTextureName[9]; // no longer dynamically allocated + + // Texture name + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture name should be"); + } + texturesTokenLength = strlen(texturesToken); + if (texturesTokenLength>8) + { + I_Error("Error parsing TEXTURES lump: Texture name \"%s\" exceeds 8 characters",texturesToken); + } + else + { + memset(&newTextureName, 0, 9); + M_Memcpy(newTextureName, texturesToken, texturesTokenLength); + // ^^ we've confirmed that the token is <= 8 characters so it will never overflow a 9 byte char buffer + strupr(newTextureName); // Just do this now so we don't have to worry about it + } + Z_Free(texturesToken); + + // Comma 1 + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after texture \"%s\"'s name should be",newTextureName); + } + else if (strcmp(texturesToken,",")!=0) + { + I_Error("Error parsing TEXTURES lump: Expected \",\" after texture \"%s\"'s name, got \"%s\"",newTextureName,texturesToken); + } + Z_Free(texturesToken); + + // Width + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture \"%s\"'s width should be",newTextureName); + } + endPos = NULL; +#ifndef AVOID_ERRNO + errno = 0; +#endif + newTextureWidth = strtol(texturesToken,&endPos,10); + if (endPos == texturesToken // Empty string + || *endPos != '\0' // Not end of string +#ifndef AVOID_ERRNO + || errno == ERANGE // Number out-of-range +#endif + || newTextureWidth < 0) // Number is not positive + { + I_Error("Error parsing TEXTURES lump: Expected a positive integer for texture \"%s\"'s width, got \"%s\"",newTextureName,texturesToken); + } + Z_Free(texturesToken); + + // Comma 2 + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after texture \"%s\"'s width should be",newTextureName); + } + if (strcmp(texturesToken,",")!=0) + { + I_Error("Error parsing TEXTURES lump: Expected \",\" after texture \"%s\"'s width, got \"%s\"",newTextureName,texturesToken); + } + Z_Free(texturesToken); + + // Height + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture \"%s\"'s height should be",newTextureName); + } + endPos = NULL; +#ifndef AVOID_ERRNO + errno = 0; +#endif + newTextureHeight = strtol(texturesToken,&endPos,10); + if (endPos == texturesToken // Empty string + || *endPos != '\0' // Not end of string +#ifndef AVOID_ERRNO + || errno == ERANGE // Number out-of-range +#endif + || newTextureHeight < 0) // Number is not positive + { + I_Error("Error parsing TEXTURES lump: Expected a positive integer for texture \"%s\"'s height, got \"%s\"",newTextureName,texturesToken); + } + Z_Free(texturesToken); + + // Left Curly Brace + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where open curly brace for texture \"%s\" should be",newTextureName); + } + if (strcmp(texturesToken,"{")==0) + { + if (actuallyLoadTexture) + { + // Allocate memory for a zero-patch texture. Obviously, we'll be adding patches momentarily. + resultTexture = (texture_t *)Z_Calloc(sizeof(texture_t),PU_STATIC,NULL); + M_Memcpy(resultTexture->name, newTextureName, 8); + resultTexture->width = newTextureWidth; + resultTexture->height = newTextureHeight; + resultTexture->type = TEXTURETYPE_COMPOSITE; + } + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch definition for texture \"%s\" should be",newTextureName); + } + while (strcmp(texturesToken,"}")!=0) + { + if (stricmp(texturesToken, "PATCH")==0) + { + Z_Free(texturesToken); + if (resultTexture) + { + // Get that new patch + newPatch = R_ParsePatch(true); + // Make room for the new patch + resultTexture = Z_Realloc(resultTexture, sizeof(texture_t) + (resultTexture->patchcount+1)*sizeof(texpatch_t), PU_STATIC, NULL); + // Populate the uninitialized values in the new patch entry of our array + M_Memcpy(&resultTexture->patches[resultTexture->patchcount], newPatch, sizeof(texpatch_t)); + // Account for the new number of patches in the texture + resultTexture->patchcount++; + // Then free up the memory assigned to R_ParsePatch, as it's unneeded now + Z_Free(newPatch); + } + else + { + R_ParsePatch(false); + } + } + else + { + I_Error("Error parsing TEXTURES lump: Expected \"PATCH\" in texture \"%s\", got \"%s\"",newTextureName,texturesToken); + } + + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch declaration or right curly brace for texture \"%s\" should be",newTextureName); + } + } + if (resultTexture && resultTexture->patchcount == 0) + { + I_Error("Error parsing TEXTURES lump: Texture \"%s\" must have at least one patch",newTextureName); + } + } + else + { + I_Error("Error parsing TEXTURES lump: Expected \"{\" for texture \"%s\", got \"%s\"",newTextureName,texturesToken); + } + Z_Free(texturesToken); + + if (actuallyLoadTexture) return resultTexture; + else return NULL; +} + +// Parses the TEXTURES lump... but just to count the number of textures. +int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum) +{ + char *texturesLump; + size_t texturesLumpLength; + char *texturesText; + UINT32 numTexturesInLump = 0; + char *texturesToken; + + // Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll + // need to make a space of memory where I can ensure that it will terminate + // correctly. Start by loading the relevant data from the WAD. + texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); + // If that didn't exist, we have nothing to do here. + if (texturesLump == NULL) return 0; + // If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly. + texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum); + texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL); + // Now move the contents of the lump into this new location. + memmove(texturesText,texturesLump,texturesLumpLength); + // Make damn well sure the last character in our new memory location is \0. + texturesText[texturesLumpLength] = '\0'; + // Finally, free up the memory from the first data load, because we really + // don't need it. + Z_Free(texturesLump); + + texturesToken = M_GetToken(texturesText); + while (texturesToken != NULL) + { + if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) + { + numTexturesInLump++; + Z_Free(texturesToken); + R_ParseTexture(false); + } + else + { + I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); + } + texturesToken = M_GetToken(NULL); + } + Z_Free(texturesToken); + Z_Free((void *)texturesText); + + return numTexturesInLump; +} + +// Parses the TEXTURES lump... for real, this time. +void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex) +{ + char *texturesLump; + size_t texturesLumpLength; + char *texturesText; + char *texturesToken; + texture_t *newTexture; + + I_Assert(texindex != NULL); + + // Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll + // need to make a space of memory where I can ensure that it will terminate + // correctly. Start by loading the relevant data from the WAD. + texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); + // If that didn't exist, we have nothing to do here. + if (texturesLump == NULL) return; + // If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly. + texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum); + texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL); + // Now move the contents of the lump into this new location. + memmove(texturesText,texturesLump,texturesLumpLength); + // Make damn well sure the last character in our new memory location is \0. + texturesText[texturesLumpLength] = '\0'; + // Finally, free up the memory from the first data load, because we really + // don't need it. + Z_Free(texturesLump); + + texturesToken = M_GetToken(texturesText); + while (texturesToken != NULL) + { + if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) + { + Z_Free(texturesToken); + // Get the new texture + newTexture = R_ParseTexture(true); + // Store the new texture + textures[*texindex] = newTexture; + texturewidth[*texindex] = newTexture->width; + textureheight[*texindex] = newTexture->height << FRACBITS; + // Increment i back in R_LoadTextures() + (*texindex)++; + } + else + { + I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); + } + texturesToken = M_GetToken(NULL); + } + Z_Free(texturesToken); + Z_Free((void *)texturesText); +} + +// Search for flat name. +lumpnum_t R_GetFlatNumForName(const char *name) +{ + INT32 i; + lumpnum_t lump; + lumpnum_t start; + lumpnum_t end; + + // Scan wad files backwards so patched flats take preference. + for (i = numwadfiles - 1; i >= 0; i--) + { + switch (wadfiles[i]->type) + { + case RET_WAD: + if ((start = W_CheckNumForNamePwad("F_START", (UINT16)i, 0)) == INT16_MAX) + { + if ((start = W_CheckNumForNamePwad("FF_START", (UINT16)i, 0)) == INT16_MAX) + continue; + else if ((end = W_CheckNumForNamePwad("FF_END", (UINT16)i, start)) == INT16_MAX) + continue; + } + else + if ((end = W_CheckNumForNamePwad("F_END", (UINT16)i, start)) == INT16_MAX) + continue; + break; + case RET_PK3: + if ((start = W_CheckNumForFolderStartPK3("Flats/", i, 0)) == INT16_MAX) + continue; + if ((end = W_CheckNumForFolderEndPK3("Flats/", i, start)) == INT16_MAX) + continue; + break; + default: + continue; + } + + // Now find lump with specified name in that range. + lump = W_CheckNumForNamePwad(name, (UINT16)i, start); + if (lump < end) + { + lump += (i<<16); // found it, in our constraints + break; + } + lump = LUMPERROR; + } + + return lump; +} + +void R_ClearTextureNumCache(boolean btell) +{ + if (tidcache) + Z_Free(tidcache); + tidcache = NULL; + if (btell) + CONS_Debug(DBG_SETUP, "Fun Fact: There are %d textures used in this map.\n", tidcachelen); + tidcachelen = 0; +} + +// +// R_CheckTextureNumForName +// +// Check whether texture is available. Filter out NoTexture indicator. +// +INT32 R_CheckTextureNumForName(const char *name) +{ + INT32 i; + + // "NoTexture" marker. + if (name[0] == '-') + return 0; + + for (i = 0; i < tidcachelen; i++) + if (!strncasecmp(tidcache[i].name, name, 8)) + return tidcache[i].id; + + // Need to parse the list backwards, so textures loaded more recently are used in lieu of ones loaded earlier + //for (i = 0; i < numtextures; i++) <- old + for (i = (numtextures - 1); i >= 0; i--) // <- new + if (!strncasecmp(textures[i]->name, name, 8)) + { + tidcachelen++; + Z_Realloc(tidcache, tidcachelen * sizeof(*tidcache), PU_STATIC, &tidcache); + strncpy(tidcache[tidcachelen-1].name, name, 8); + tidcache[tidcachelen-1].name[8] = '\0'; +#ifndef ZDEBUG + CONS_Debug(DBG_SETUP, "texture #%s: %s\n", sizeu1(tidcachelen), tidcache[tidcachelen-1].name); +#endif + tidcache[tidcachelen-1].id = i; + return i; + } + + return -1; +} + +// +// R_TextureNumForName +// +// Calls R_CheckTextureNumForName, aborts with error message. +// +INT32 R_TextureNumForName(const char *name) +{ + const INT32 i = R_CheckTextureNumForName(name); + + if (i == -1) + { + static INT32 redwall = -2; + CONS_Debug(DBG_SETUP, "WARNING: R_TextureNumForName: %.8s not found\n", name); + if (redwall == -2) + redwall = R_CheckTextureNumForName("REDWALL"); + if (redwall != -1) + return redwall; + return 1; + } + return i; +} diff --git a/src/r_textures.h b/src/r_textures.h new file mode 100644 index 000000000..d9f5e9e1d --- /dev/null +++ b/src/r_textures.h @@ -0,0 +1,109 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_textures.h +/// \brief Texture generation. + +#ifndef __R_TEXTURES__ +#define __R_TEXTURES__ + +#include "r_defs.h" +#include "r_state.h" +#include "p_setup.h" // levelflats +#include "r_data.h" +#include "r_textures.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// A single patch from a texture definition, +// basically a rectangular area within +// the texture rectangle. +typedef struct +{ + // Block origin (always UL), which has already accounted for the internal origin of the patch. + INT16 originx, originy; + UINT16 wad, lump; + UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both + UINT8 alpha; // Translucency value + enum patchalphastyle style; +} texpatch_t; + +// texture type +enum +{ + TEXTURETYPE_UNKNOWN, + TEXTURETYPE_SINGLEPATCH, + TEXTURETYPE_COMPOSITE, +#ifdef WALLFLATS + TEXTURETYPE_FLAT, +#endif +}; + +// A maptexturedef_t describes a rectangular texture, +// which is composed of one or more mappatch_t structures +// that arrange graphic patches. +typedef struct +{ + // Keep name for switch changing, etc. + char name[8]; + UINT8 type; // TEXTURETYPE_ + INT16 width, height; + boolean holes; + UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both + + // All the patches[patchcount] are drawn back to front into the cached texture. + INT16 patchcount; + texpatch_t patches[0]; +} texture_t; + +typedef struct +{ + UINT8 *flat; + INT16 width, height; +} textureflat_t; + +// all loaded and prepared textures from the start of the game +extern texture_t **textures; +extern textureflat_t *texflats; + +extern INT32 *texturewidth; +extern fixed_t *textureheight; // needed for texture pegging + +extern UINT32 **texturecolumnofs; // column offset lookup table for each texture +extern UINT8 **texturecache; // graphics data for each generated full-size texture + +// Load TEXTURES definitions, create lookup tables +void R_LoadTextures(void); +void R_FlushTextureCache(void); + +UINT8 *R_GenerateTexture(size_t texnum); +INT32 R_GetTextureNum(INT32 texnum); +void R_CheckTextureCache(INT32 tex); + +// Retrieve column data for span blitting. +UINT8 *R_GetColumn(fixed_t tex, INT32 col); +UINT8 *R_GetFlat(lumpnum_t flatnum); + +// Retrieval. +// Floor/ceiling opaque texture tiles, +// lookup by name. For animation? +lumpnum_t R_GetFlatNumForName(const char *name); + +// Called by P_Ticker for switches and animations, +// returns the texture number for the texture name. +void R_ClearTextureNumCache(boolean btell); +INT32 R_TextureNumForName(const char *name); +INT32 R_CheckTextureNumForName(const char *name); + +extern INT32 numtextures; + +#endif diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index 7efab1bc0..594095cbe 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -280,13 +280,14 @@ - + + @@ -442,12 +443,13 @@ true + - + diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index 97be5ad5f..0248908a4 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -465,6 +465,9 @@ Hw_Hardware + + R_Rend + R_Rend @@ -928,6 +931,9 @@ Hw_Hardware + + R_Rend + R_Rend diff --git a/src/w_wad.c b/src/w_wad.c index 465b5abd8..2916d9825 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -56,6 +56,7 @@ #include "d_clisrv.h" #include "r_defs.h" #include "r_data.h" +#include "r_textures.h" #include "i_system.h" #include "md5.h" #include "lua_script.h" @@ -65,7 +66,6 @@ #include "m_misc.h" // M_MapNumber #ifdef HWRENDER -#include "r_data.h" #include "hardware/hw_main.h" #include "hardware/hw_glob.h" #endif diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj index 91a6aa348..69aee41e8 100644 --- a/src/win32/Srb2win-vc10.vcxproj +++ b/src/win32/Srb2win-vc10.vcxproj @@ -299,12 +299,13 @@ true - + + @@ -455,13 +456,14 @@ + - + diff --git a/src/win32/Srb2win-vc10.vcxproj.filters b/src/win32/Srb2win-vc10.vcxproj.filters index 00040f4a1..34994132e 100644 --- a/src/win32/Srb2win-vc10.vcxproj.filters +++ b/src/win32/Srb2win-vc10.vcxproj.filters @@ -892,6 +892,9 @@ Hw_Hardware + + R_Rend + R_Rend From b6ced0884e89eb21ebb013fd0cb9567422b3639d Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 7 Jan 2020 12:43:27 -0300 Subject: [PATCH 011/136] Organise header files, use R_GetFlat --- src/r_plane.c | 4 ++-- src/r_textures.c | 2 -- src/r_textures.h | 14 +++++--------- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/r_plane.c b/src/r_plane.c index 982354a5f..cc304043e 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -1127,7 +1127,7 @@ void R_DrawSinglePlane(visplane_t *pl) case LEVELFLAT_NONE: return; case LEVELFLAT_FLAT: - ds_source = W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE); + ds_source = R_GetFlat(levelflat->u.flat.lumpnum); R_CheckFlatLength(W_LumpLength(levelflat->u.flat.lumpnum)); // Raw flats always have dimensions that are powers-of-two numbers. ds_powersoftwo = true; @@ -1140,7 +1140,7 @@ void R_DrawSinglePlane(visplane_t *pl) ds_source = R_GetTextureFlat(levelflat, true, false); break; default: - ds_source = W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_STATIC); + ds_source = R_GetFlat(levelflat->u.flat.lumpnum); flat = R_GetTextureFlat(levelflat, false, #ifndef NO_PNG_LUMPS ( type == LEVELFLAT_PNG ) diff --git a/src/r_textures.c b/src/r_textures.c index c9c8fd937..96f2590a6 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -497,8 +497,6 @@ UINT8 *R_GetColumn(fixed_t tex, INT32 col) return data + LONG(texturecolumnofs[tex][col]); } -// convert flats to hicolor as they are requested -// UINT8 *R_GetFlat(lumpnum_t flatlumpnum) { return W_CacheLumpNum(flatlumpnum, PU_CACHE); diff --git a/src/r_textures.h b/src/r_textures.h index d9f5e9e1d..fd35926cc 100644 --- a/src/r_textures.h +++ b/src/r_textures.h @@ -85,24 +85,20 @@ extern UINT8 **texturecache; // graphics data for each generated full-size textu void R_LoadTextures(void); void R_FlushTextureCache(void); +// Texture generation UINT8 *R_GenerateTexture(size_t texnum); INT32 R_GetTextureNum(INT32 texnum); void R_CheckTextureCache(INT32 tex); +void R_ClearTextureNumCache(boolean btell); -// Retrieve column data for span blitting. +// Retrieve texture data. UINT8 *R_GetColumn(fixed_t tex, INT32 col); UINT8 *R_GetFlat(lumpnum_t flatnum); -// Retrieval. -// Floor/ceiling opaque texture tiles, -// lookup by name. For animation? -lumpnum_t R_GetFlatNumForName(const char *name); - -// Called by P_Ticker for switches and animations, -// returns the texture number for the texture name. -void R_ClearTextureNumCache(boolean btell); +// Returns the texture number for the texture name. INT32 R_TextureNumForName(const char *name); INT32 R_CheckTextureNumForName(const char *name); +lumpnum_t R_GetFlatNumForName(const char *name); extern INT32 numtextures; From fb0d9cdede11ab52141ba47fb43c5af9b2725c23 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 7 Jan 2020 12:45:22 -0300 Subject: [PATCH 012/136] Move location of R_GetTextureFlat --- src/r_plane.c | 101 ----------------------------------------------- src/r_textures.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++ src/r_textures.h | 1 + 3 files changed, 102 insertions(+), 101 deletions(-) diff --git a/src/r_plane.c b/src/r_plane.c index cc304043e..292b5b2d4 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -765,107 +765,6 @@ void R_CheckFlatLength(size_t size) } } -// -// R_GetTextureFlat -// -// Convert a texture or patch to a flat. -// -static UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng) -{ - UINT8 *flat; - textureflat_t *texflat = &texflats[levelflat->u.texture.num]; - patch_t *patch = NULL; - boolean texturechanged = (leveltexture ? (levelflat->u.texture.num != levelflat->u.texture.lastnum) : false); - - (void)ispng; - - // Check if the texture changed. - if (leveltexture && (!texturechanged)) - { - if (texflat != NULL && texflat->flat) - { - flat = texflat->flat; - ds_flatwidth = texflat->width; - ds_flatheight = texflat->height; - texturechanged = false; - } - else - texturechanged = true; - } - - // If the texture changed, or the patch doesn't exist, convert either of them to a flat. - if (levelflat->flatpatch == NULL || texturechanged) - { - // Level texture - if (leveltexture) - { - UINT8 *converted; - size_t size; - texture_t *texture = textures[levelflat->u.texture.num]; - texflat->width = ds_flatwidth = texture->width; - texflat->height = ds_flatheight = texture->height; - - size = (texflat->width * texflat->height); - texflat->flat = Z_Malloc(size, PU_LEVEL, NULL); - converted = (UINT8 *)Picture_TextureToFlat(levelflat->u.texture.num); - M_Memcpy(texflat->flat, converted, size); - Z_Free(converted); - flat = texflat->flat; - - levelflat->flatpatch = flat; - levelflat->width = ds_flatwidth; - levelflat->height = ds_flatheight; - } - // Patch (never happens yet) - else - { - patch = (patch_t *)ds_source; -#ifndef NO_PNG_LUMPS - if (ispng) - { - INT32 pngwidth, pngheight; - - levelflat->flatpatch = Picture_PNGConvert(ds_source, PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); - levelflat->topoffset = levelflat->leftoffset = 0; - levelflat->width = (UINT16)pngwidth; - levelflat->height = (UINT16)pngheight; - - ds_flatwidth = levelflat->width; - ds_flatheight = levelflat->height; - } - else -#endif - { - UINT8 *converted; - size_t size; - levelflat->width = ds_flatwidth = SHORT(patch->width); - levelflat->height = ds_flatheight = SHORT(patch->height); - - levelflat->topoffset = patch->topoffset * FRACUNIT; - levelflat->leftoffset = patch->leftoffset * FRACUNIT; - - levelflat->flatpatch = Z_Malloc(levelflat->width * levelflat->height, PU_LEVEL, NULL); - converted = Picture_FlatConvert(PICFMT_PATCH, patch, PICFMT_FLAT, 0, &size, levelflat->width, levelflat->height, patch->topoffset, patch->leftoffset, 0); - M_Memcpy(levelflat->flatpatch, converted, size); - Z_Free(converted); - } - flat = levelflat->flatpatch; - } - } - else - { - flat = levelflat->flatpatch; - ds_flatwidth = levelflat->width; - ds_flatheight = levelflat->height; - } - - xoffs += levelflat->leftoffset; - yoffs += levelflat->topoffset; - - levelflat->u.texture.lastnum = levelflat->u.texture.num; - return flat; -} - #ifdef ESLOPE static void R_SlopeVectors(visplane_t *pl, INT32 i, float fudge) { diff --git a/src/r_textures.c b/src/r_textures.c index 96f2590a6..9d70b9d5c 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -502,6 +502,107 @@ UINT8 *R_GetFlat(lumpnum_t flatlumpnum) return W_CacheLumpNum(flatlumpnum, PU_CACHE); } +// +// R_GetTextureFlat +// +// Convert a texture or patch to a flat. +// +UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng) +{ + UINT8 *flat; + textureflat_t *texflat = &texflats[levelflat->u.texture.num]; + patch_t *patch = NULL; + boolean texturechanged = (leveltexture ? (levelflat->u.texture.num != levelflat->u.texture.lastnum) : false); + + (void)ispng; + + // Check if the texture changed. + if (leveltexture && (!texturechanged)) + { + if (texflat != NULL && texflat->flat) + { + flat = texflat->flat; + ds_flatwidth = texflat->width; + ds_flatheight = texflat->height; + texturechanged = false; + } + else + texturechanged = true; + } + + // If the texture changed, or the patch doesn't exist, convert either of them to a flat. + if (levelflat->flatpatch == NULL || texturechanged) + { + // Level texture + if (leveltexture) + { + UINT8 *converted; + size_t size; + texture_t *texture = textures[levelflat->u.texture.num]; + texflat->width = ds_flatwidth = texture->width; + texflat->height = ds_flatheight = texture->height; + + size = (texflat->width * texflat->height); + texflat->flat = Z_Malloc(size, PU_LEVEL, NULL); + converted = (UINT8 *)Picture_TextureToFlat(levelflat->u.texture.num); + M_Memcpy(texflat->flat, converted, size); + Z_Free(converted); + flat = texflat->flat; + + levelflat->flatpatch = flat; + levelflat->width = ds_flatwidth; + levelflat->height = ds_flatheight; + } + // Patch (never happens yet) + else + { + patch = (patch_t *)ds_source; +#ifndef NO_PNG_LUMPS + if (ispng) + { + INT32 pngwidth, pngheight; + + levelflat->flatpatch = Picture_PNGConvert(ds_source, PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); + levelflat->topoffset = levelflat->leftoffset = 0; + levelflat->width = (UINT16)pngwidth; + levelflat->height = (UINT16)pngheight; + + ds_flatwidth = levelflat->width; + ds_flatheight = levelflat->height; + } + else +#endif + { + UINT8 *converted; + size_t size; + levelflat->width = ds_flatwidth = SHORT(patch->width); + levelflat->height = ds_flatheight = SHORT(patch->height); + + levelflat->topoffset = patch->topoffset * FRACUNIT; + levelflat->leftoffset = patch->leftoffset * FRACUNIT; + + levelflat->flatpatch = Z_Malloc(levelflat->width * levelflat->height, PU_LEVEL, NULL); + converted = Picture_FlatConvert(PICFMT_PATCH, patch, PICFMT_FLAT, 0, &size, levelflat->width, levelflat->height, patch->topoffset, patch->leftoffset, 0); + M_Memcpy(levelflat->flatpatch, converted, size); + Z_Free(converted); + } + flat = levelflat->flatpatch; + } + } + else + { + flat = levelflat->flatpatch; + ds_flatwidth = levelflat->width; + ds_flatheight = levelflat->height; + } + + //xoffs += levelflat->leftoffset; + //yoffs += levelflat->topoffset; + + levelflat->u.texture.lastnum = levelflat->u.texture.num; + return flat; +} + // // Empty the texture cache (used for load wad at runtime) // diff --git a/src/r_textures.h b/src/r_textures.h index fd35926cc..d42509bdd 100644 --- a/src/r_textures.h +++ b/src/r_textures.h @@ -94,6 +94,7 @@ void R_ClearTextureNumCache(boolean btell); // Retrieve texture data. UINT8 *R_GetColumn(fixed_t tex, INT32 col); UINT8 *R_GetFlat(lumpnum_t flatnum); +UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng); // Returns the texture number for the texture name. INT32 R_TextureNumForName(const char *name); From 82998100d3154d47fc7956dfcc04e935369dd86c Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 7 Jan 2020 12:46:46 -0300 Subject: [PATCH 013/136] Move location of R_CheckPowersOfTwo and R_CheckFlatLength --- src/r_plane.c | 83 ------------------------------------------------ src/r_textures.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++ src/r_textures.h | 3 ++ 3 files changed, 86 insertions(+), 83 deletions(-) diff --git a/src/r_plane.c b/src/r_plane.c index 292b5b2d4..8e1ddb8db 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -682,89 +682,6 @@ static void R_DrawSkyPlane(visplane_t *pl) } } -// -// R_CheckPowersOfTwo -// -// Self-explanatory? -// -boolean R_CheckPowersOfTwo(void) -{ - boolean wpow2 = (!(ds_flatwidth & (ds_flatwidth - 1))); - boolean hpow2 = (!(ds_flatheight & (ds_flatheight - 1))); - - // Initially, the flat isn't powers-of-two-sized. - ds_powersoftwo = false; - - // But if the width and height are powers of two, - // and are EQUAL, then it's okay :] - if ((ds_flatwidth == ds_flatheight) && (wpow2 && hpow2)) - ds_powersoftwo = true; - - // Just return ds_powersoftwo. - return ds_powersoftwo; -} - -// -// R_CheckFlatLength -// -// Determine the flat's dimensions from the lump length. -// -void R_CheckFlatLength(size_t size) -{ - switch (size) - { - case 4194304: // 2048x2048 lump - nflatmask = 0x3FF800; - nflatxshift = 21; - nflatyshift = 10; - nflatshiftup = 5; - ds_flatwidth = ds_flatheight = 2048; - break; - case 1048576: // 1024x1024 lump - nflatmask = 0xFFC00; - nflatxshift = 22; - nflatyshift = 12; - nflatshiftup = 6; - ds_flatwidth = ds_flatheight = 1024; - break; - case 262144:// 512x512 lump - nflatmask = 0x3FE00; - nflatxshift = 23; - nflatyshift = 14; - nflatshiftup = 7; - ds_flatwidth = ds_flatheight = 512; - break; - case 65536: // 256x256 lump - nflatmask = 0xFF00; - nflatxshift = 24; - nflatyshift = 16; - nflatshiftup = 8; - ds_flatwidth = ds_flatheight = 256; - break; - case 16384: // 128x128 lump - nflatmask = 0x3F80; - nflatxshift = 25; - nflatyshift = 18; - nflatshiftup = 9; - ds_flatwidth = ds_flatheight = 128; - break; - case 1024: // 32x32 lump - nflatmask = 0x3E0; - nflatxshift = 27; - nflatyshift = 22; - nflatshiftup = 11; - ds_flatwidth = ds_flatheight = 32; - break; - default: // 64x64 lump - nflatmask = 0xFC0; - nflatxshift = 26; - nflatyshift = 20; - nflatshiftup = 10; - ds_flatwidth = ds_flatheight = 64; - break; - } -} - #ifdef ESLOPE static void R_SlopeVectors(visplane_t *pl, INT32 i, float fudge) { diff --git a/src/r_textures.c b/src/r_textures.c index 9d70b9d5c..60d9a802e 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -603,6 +603,89 @@ UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boolean is return flat; } +// +// R_CheckPowersOfTwo +// +// Self-explanatory? +// +boolean R_CheckPowersOfTwo(void) +{ + boolean wpow2 = (!(ds_flatwidth & (ds_flatwidth - 1))); + boolean hpow2 = (!(ds_flatheight & (ds_flatheight - 1))); + + // Initially, the flat isn't powers-of-two-sized. + ds_powersoftwo = false; + + // But if the width and height are powers of two, + // and are EQUAL, then it's okay :] + if ((ds_flatwidth == ds_flatheight) && (wpow2 && hpow2)) + ds_powersoftwo = true; + + // Just return ds_powersoftwo. + return ds_powersoftwo; +} + +// +// R_CheckFlatLength +// +// Determine the flat's dimensions from its lump length. +// +void R_CheckFlatLength(size_t size) +{ + switch (size) + { + case 4194304: // 2048x2048 lump + nflatmask = 0x3FF800; + nflatxshift = 21; + nflatyshift = 10; + nflatshiftup = 5; + ds_flatwidth = ds_flatheight = 2048; + break; + case 1048576: // 1024x1024 lump + nflatmask = 0xFFC00; + nflatxshift = 22; + nflatyshift = 12; + nflatshiftup = 6; + ds_flatwidth = ds_flatheight = 1024; + break; + case 262144:// 512x512 lump + nflatmask = 0x3FE00; + nflatxshift = 23; + nflatyshift = 14; + nflatshiftup = 7; + ds_flatwidth = ds_flatheight = 512; + break; + case 65536: // 256x256 lump + nflatmask = 0xFF00; + nflatxshift = 24; + nflatyshift = 16; + nflatshiftup = 8; + ds_flatwidth = ds_flatheight = 256; + break; + case 16384: // 128x128 lump + nflatmask = 0x3F80; + nflatxshift = 25; + nflatyshift = 18; + nflatshiftup = 9; + ds_flatwidth = ds_flatheight = 128; + break; + case 1024: // 32x32 lump + nflatmask = 0x3E0; + nflatxshift = 27; + nflatyshift = 22; + nflatshiftup = 11; + ds_flatwidth = ds_flatheight = 32; + break; + default: // 64x64 lump + nflatmask = 0xFC0; + nflatxshift = 26; + nflatyshift = 20; + nflatshiftup = 10; + ds_flatwidth = ds_flatheight = 64; + break; + } +} + // // Empty the texture cache (used for load wad at runtime) // diff --git a/src/r_textures.h b/src/r_textures.h index d42509bdd..e2bb40274 100644 --- a/src/r_textures.h +++ b/src/r_textures.h @@ -96,6 +96,9 @@ UINT8 *R_GetColumn(fixed_t tex, INT32 col); UINT8 *R_GetFlat(lumpnum_t flatnum); UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng); +boolean R_CheckPowersOfTwo(void); +void R_CheckFlatLength(size_t size); + // Returns the texture number for the texture name. INT32 R_TextureNumForName(const char *name); INT32 R_CheckTextureNumForName(const char *name); From e2d0ddfb549a2e55389d5f3da4517f126039d1e6 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 7 Jan 2020 13:27:59 -0300 Subject: [PATCH 014/136] R_GetTextureFlat -> R_GetLevelFlat --- src/p_setup.c | 2 +- src/p_setup.h | 1 - src/r_picformats.c | 5 ++++- src/r_plane.c | 26 +++++--------------------- src/r_textures.c | 42 +++++++++++++++++------------------------- src/r_textures.h | 4 ++-- 6 files changed, 29 insertions(+), 51 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 0b43f76a5..5014f0a7e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -638,7 +638,7 @@ texturefound: { flatfound: /* This could be a flat, patch, or PNG. */ - flatpatch = W_CacheLumpNum(flatnum, PU_STATIC); + flatpatch = W_CacheLumpNum(flatnum, PU_CACHE); lumplength = W_LumpLength(flatnum); if (Picture_CheckIfPatch(flatpatch, lumplength)) levelflat->type = LEVELFLAT_PATCH; diff --git a/src/p_setup.h b/src/p_setup.h index ff28e36e4..77c932afc 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -72,7 +72,6 @@ typedef struct u; UINT16 width, height; - fixed_t topoffset, leftoffset; // for flat animation INT32 animseq; // start pos. in the anim sequence diff --git a/src/r_picformats.c b/src/r_picformats.c index b907ac4d4..ec953dfe9 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -615,7 +615,7 @@ boolean Picture_CheckIfPatch(patch_t *patch, size_t size) width = SHORT(patch->width); height = SHORT(patch->height); - result = (height > 0 && height <= 16384 && width > 0 && width <= 16384 && width < (INT16)(size / 4)); + result = (height > 0 && height <= 16384 && width > 0 && width <= 16384); if (result) { @@ -930,6 +930,9 @@ void *Picture_PNGConvert( png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, insize); png_uint_32 width = *w, height = *h; + if (png == NULL) + I_Error("Picture_PNGConvert: picture was NULL!"); + // Find the output format's bits per pixel amount outbpp = Picture_FormatBPP(outformat); diff --git a/src/r_plane.c b/src/r_plane.c index 8e1ddb8db..9e90aba15 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -777,12 +777,11 @@ d.z = (v1.x * v2.y) - (v1.y * v2.x) void R_DrawSinglePlane(visplane_t *pl) { - UINT8 *flat; + levelflat_t *levelflat; INT32 light = 0; INT32 x; INT32 stop, angle; ffloor_t *rover; - levelflat_t *levelflat; int type; int spanfunctype = BASEDRAWFUNC; @@ -943,30 +942,15 @@ void R_DrawSinglePlane(visplane_t *pl) case LEVELFLAT_NONE: return; case LEVELFLAT_FLAT: - ds_source = R_GetFlat(levelflat->u.flat.lumpnum); + ds_source = (UINT8 *)R_GetFlat(levelflat->u.flat.lumpnum); R_CheckFlatLength(W_LumpLength(levelflat->u.flat.lumpnum)); // Raw flats always have dimensions that are powers-of-two numbers. ds_powersoftwo = true; break; default: - switch (type) - { - case LEVELFLAT_TEXTURE: - /* Textures get cached differently and don't need ds_source */ - ds_source = R_GetTextureFlat(levelflat, true, false); - break; - default: - ds_source = R_GetFlat(levelflat->u.flat.lumpnum); - flat = R_GetTextureFlat(levelflat, false, -#ifndef NO_PNG_LUMPS - ( type == LEVELFLAT_PNG ) -#else - false -#endif - ); - Z_ChangeTag(ds_source, PU_CACHE); - ds_source = flat; - } + ds_source = (UINT8 *)R_GetLevelFlat(levelflat); + if (!ds_source) + return; // Check if this texture or patch has power-of-two dimensions. if (R_CheckPowersOfTwo()) R_CheckFlatLength(ds_flatwidth * ds_flatheight); diff --git a/src/r_textures.c b/src/r_textures.c index 60d9a802e..5e03f57fc 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -497,31 +497,29 @@ UINT8 *R_GetColumn(fixed_t tex, INT32 col) return data + LONG(texturecolumnofs[tex][col]); } -UINT8 *R_GetFlat(lumpnum_t flatlumpnum) +void *R_GetFlat(lumpnum_t flatlumpnum) { return W_CacheLumpNum(flatlumpnum, PU_CACHE); } // -// R_GetTextureFlat +// R_GetLevelFlat // -// Convert a texture or patch to a flat. +// If needed, convert a texture or patch to a flat. // -UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng) +void *R_GetLevelFlat(levelflat_t *levelflat) { - UINT8 *flat; + UINT8 *flatdata = NULL; + boolean leveltexture = (levelflat->type == LEVELFLAT_TEXTURE); textureflat_t *texflat = &texflats[levelflat->u.texture.num]; - patch_t *patch = NULL; boolean texturechanged = (leveltexture ? (levelflat->u.texture.num != levelflat->u.texture.lastnum) : false); - (void)ispng; - // Check if the texture changed. if (leveltexture && (!texturechanged)) { if (texflat != NULL && texflat->flat) { - flat = texflat->flat; + flatdata = texflat->flat; ds_flatwidth = texflat->width; ds_flatheight = texflat->height; texturechanged = false; @@ -547,23 +545,19 @@ UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boolean is converted = (UINT8 *)Picture_TextureToFlat(levelflat->u.texture.num); M_Memcpy(texflat->flat, converted, size); Z_Free(converted); - flat = texflat->flat; - levelflat->flatpatch = flat; + levelflat->flatpatch = texflat->flat; levelflat->width = ds_flatwidth; levelflat->height = ds_flatheight; } - // Patch (never happens yet) else { - patch = (patch_t *)ds_source; #ifndef NO_PNG_LUMPS - if (ispng) + if (levelflat->type == LEVELFLAT_PNG) { INT32 pngwidth, pngheight; - levelflat->flatpatch = Picture_PNGConvert(ds_source, PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); - levelflat->topoffset = levelflat->leftoffset = 0; + levelflat->flatpatch = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); levelflat->width = (UINT16)pngwidth; levelflat->height = (UINT16)pngheight; @@ -572,35 +566,33 @@ UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boolean is } else #endif + if (levelflat->type == LEVELFLAT_PATCH) { UINT8 *converted; size_t size; + patch_t *patch = W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE); + levelflat->width = ds_flatwidth = SHORT(patch->width); levelflat->height = ds_flatheight = SHORT(patch->height); - levelflat->topoffset = patch->topoffset * FRACUNIT; - levelflat->leftoffset = patch->leftoffset * FRACUNIT; - levelflat->flatpatch = Z_Malloc(levelflat->width * levelflat->height, PU_LEVEL, NULL); converted = Picture_FlatConvert(PICFMT_PATCH, patch, PICFMT_FLAT, 0, &size, levelflat->width, levelflat->height, patch->topoffset, patch->leftoffset, 0); M_Memcpy(levelflat->flatpatch, converted, size); Z_Free(converted); } - flat = levelflat->flatpatch; } } else { - flat = levelflat->flatpatch; ds_flatwidth = levelflat->width; ds_flatheight = levelflat->height; } - //xoffs += levelflat->leftoffset; - //yoffs += levelflat->topoffset; - levelflat->u.texture.lastnum = levelflat->u.texture.num; - return flat; + + if (flatdata == NULL) + flatdata = levelflat->flatpatch; + return flatdata; } // diff --git a/src/r_textures.h b/src/r_textures.h index e2bb40274..62ef523b2 100644 --- a/src/r_textures.h +++ b/src/r_textures.h @@ -93,8 +93,8 @@ void R_ClearTextureNumCache(boolean btell); // Retrieve texture data. UINT8 *R_GetColumn(fixed_t tex, INT32 col); -UINT8 *R_GetFlat(lumpnum_t flatnum); -UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng); +void *R_GetFlat(lumpnum_t flatnum); +void *R_GetLevelFlat(levelflat_t *levelflat); boolean R_CheckPowersOfTwo(void); void R_CheckFlatLength(size_t size); From 769c1a4e3885463d00276b1f7609d6b38f4b4063 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 7 Jan 2020 14:07:14 -0300 Subject: [PATCH 015/136] Fix OpenGL --- src/hardware/hw_cache.c | 54 ++++++++++++++++++++++++++++++++++++++++- src/hardware/hw_main.c | 54 ++++++++++++++++++++++++----------------- src/p_setup.c | 4 +-- src/p_setup.h | 9 ++++--- src/r_textures.c | 12 ++++----- 5 files changed, 98 insertions(+), 35 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 9bf7db1da..df1e18f9d 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -1024,7 +1024,7 @@ void HWR_GetLevelFlat(levelflat_t *levelflat) INT32 texturenum = levelflat->u.texture.num; #ifdef PARANOIA if ((unsigned)texturenum >= gr_numtextures) - I_Error("HWR_GetLevelFlat: texturenum >= numtextures\n"); + I_Error("HWR_GetLevelFlat: texturenum >= numtextures"); #endif // Who knows? @@ -1044,6 +1044,58 @@ void HWR_GetLevelFlat(levelflat_t *levelflat) // The system-memory data can be purged now. Z_ChangeTag(grtex->mipmap.grInfo.data, PU_HWRCACHE_UNLOCKED); } + else if (levelflat->type == LEVELFLAT_PATCH) + { + GLPatch_t *patch = W_CachePatchNum(levelflat->u.flat.lumpnum, PU_CACHE); + levelflat->width = (UINT16)SHORT(patch->width); + levelflat->height = (UINT16)SHORT(patch->height); + HWR_GetPatch(patch); + } +#ifndef NO_PNG_LUMPS + else if (levelflat->type == LEVELFLAT_PNG) + { + INT32 pngwidth, pngheight; + GLMipmap_t *mipmap = levelflat->mipmap; + UINT8 *flat; + size_t size; + + // Cache the picture. + if (!levelflat->picture) + { + levelflat->picture = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); + levelflat->width = (UINT16)pngwidth; + levelflat->height = (UINT16)pngheight; + } + + // Make the mipmap. + if (mipmap == NULL) + { + mipmap = Z_Calloc(sizeof(GLMipmap_t), PU_LEVEL, NULL); + mipmap->grInfo.format = GR_TEXFMT_P_8; + mipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; +#ifdef GLIDE_API_COMPATIBILITY + mipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64; + mipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64; + mipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1; +#endif + levelflat->mipmap = mipmap; + } + + if (!mipmap->grInfo.data && !mipmap->downloaded) + { + mipmap->width = levelflat->width; + mipmap->height = levelflat->height; + size = (mipmap->width * mipmap->height); + flat = Z_Malloc(size, PU_LEVEL, &mipmap->grInfo.data); + if (levelflat->picture == NULL) + I_Error("HWR_GetLevelFlat: levelflat->picture == NULL"); + M_Memcpy(flat, levelflat->picture, size); + } + + // Tell the hardware driver to bind the current texture to the flat's mipmap + HWD.pfnSetTexture(mipmap); + } +#endif else // set no texture HWD.pfnSetTexture(NULL); } diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 23e52d19b..0194675b9 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -480,7 +480,6 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is float fflatwidth = 64.0f, fflatheight = 64.0f; INT32 flatflag = 63; boolean texflat = false; - size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; FSurfaceInfo Surf; @@ -546,16 +545,9 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is // set texture for polygon if (levelflat != NULL) { - if (levelflat->type == LEVELFLAT_TEXTURE) + if (levelflat->type == LEVELFLAT_FLAT) { - fflatwidth = textures[levelflat->u.texture.num]->width; - fflatheight = textures[levelflat->u.texture.num]->height; - texflat = true; - } - else if (levelflat->type == LEVELFLAT_FLAT) - { - len = W_LumpLength(levelflat->u.flat.lumpnum); - + size_t len = W_LumpLength(levelflat->u.flat.lumpnum); switch (len) { case 4194304: // 2048x2048 lump @@ -580,9 +572,22 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, boolean is fflatwidth = fflatheight = 64.0f; break; } - flatflag = ((INT32)fflatwidth)-1; } + else + { + if (levelflat->type == LEVELFLAT_TEXTURE) + { + fflatwidth = textures[levelflat->u.texture.num]->width; + fflatheight = textures[levelflat->u.texture.num]->height; + } + else if (levelflat->type == LEVELFLAT_PATCH || levelflat->type == LEVELFLAT_PNG) + { + fflatwidth = levelflat->width; + fflatheight = levelflat->height; + } + texflat = true; + } } else // set no texture HWD.pfnSetTexture(NULL); @@ -3142,7 +3147,6 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, float fflatwidth = 64.0f, fflatheight = 64.0f; INT32 flatflag = 63; boolean texflat = false; - size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; FSurfaceInfo Surf; @@ -3176,16 +3180,9 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, // set texture for polygon if (levelflat != NULL) { - if (levelflat->type == LEVELFLAT_TEXTURE) + if (levelflat->type == LEVELFLAT_FLAT) { - fflatwidth = textures[levelflat->u.texture.num]->width; - fflatheight = textures[levelflat->u.texture.num]->height; - texflat = true; - } - else if (levelflat->type == LEVELFLAT_FLAT) - { - len = W_LumpLength(levelflat->u.flat.lumpnum); - + size_t len = W_LumpLength(levelflat->u.flat.lumpnum); switch (len) { case 4194304: // 2048x2048 lump @@ -3210,9 +3207,22 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, fflatwidth = fflatheight = 64.0f; break; } - flatflag = ((INT32)fflatwidth)-1; } + else + { + if (levelflat->type == LEVELFLAT_TEXTURE) + { + fflatwidth = textures[levelflat->u.texture.num]->width; + fflatheight = textures[levelflat->u.texture.num]->height; + } + else if (levelflat->type == LEVELFLAT_PATCH || levelflat->type == LEVELFLAT_PNG) + { + fflatwidth = levelflat->width; + fflatheight = levelflat->height; + } + texflat = true; + } } else // set no texture HWD.pfnSetTexture(NULL); diff --git a/src/p_setup.c b/src/p_setup.c index 5014f0a7e..bdc979f9e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -649,8 +649,6 @@ flatfound: Only need eight bytes for PNG headers. FIXME: Put this elsewhere. */ - if (flatpatch) - Z_Free(flatpatch); W_ReadLumpHeader(flatnum, buffer, 8, 0); if (Picture_IsLumpPNG(buffer, lumplength)) levelflat->type = LEVELFLAT_PNG; @@ -658,6 +656,8 @@ flatfound: #endif/*NO_PNG_LUMPS*/ levelflat->type = LEVELFLAT_FLAT;/* phew */ } + if (flatpatch) + Z_Free(flatpatch); levelflat->u.flat. lumpnum = flatnum; levelflat->u.flat.baselumpnum = LUMPERROR; diff --git a/src/p_setup.h b/src/p_setup.h index 77c932afc..75ba99a8f 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -37,9 +37,7 @@ enum LEVELFLAT_NONE,/* HOM time my friend */ LEVELFLAT_FLAT, LEVELFLAT_PATCH, -#ifndef NO_PNG_LUMPS LEVELFLAT_PNG, -#endif LEVELFLAT_TEXTURE, }; @@ -78,8 +76,11 @@ typedef struct INT32 numpics; INT32 speed; - // for patchflats - UINT8 *flatpatch; + // for textures + UINT8 *picture; +#ifdef HWRENDER + void *mipmap; +#endif } levelflat_t; extern size_t numlevelflats; diff --git a/src/r_textures.c b/src/r_textures.c index 5e03f57fc..2e9c65e5c 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -529,7 +529,7 @@ void *R_GetLevelFlat(levelflat_t *levelflat) } // If the texture changed, or the patch doesn't exist, convert either of them to a flat. - if (levelflat->flatpatch == NULL || texturechanged) + if (levelflat->picture == NULL || texturechanged) { // Level texture if (leveltexture) @@ -546,7 +546,7 @@ void *R_GetLevelFlat(levelflat_t *levelflat) M_Memcpy(texflat->flat, converted, size); Z_Free(converted); - levelflat->flatpatch = texflat->flat; + levelflat->picture = texflat->flat; levelflat->width = ds_flatwidth; levelflat->height = ds_flatheight; } @@ -557,7 +557,7 @@ void *R_GetLevelFlat(levelflat_t *levelflat) { INT32 pngwidth, pngheight; - levelflat->flatpatch = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); + levelflat->picture = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); levelflat->width = (UINT16)pngwidth; levelflat->height = (UINT16)pngheight; @@ -575,9 +575,9 @@ void *R_GetLevelFlat(levelflat_t *levelflat) levelflat->width = ds_flatwidth = SHORT(patch->width); levelflat->height = ds_flatheight = SHORT(patch->height); - levelflat->flatpatch = Z_Malloc(levelflat->width * levelflat->height, PU_LEVEL, NULL); + levelflat->picture = Z_Malloc(levelflat->width * levelflat->height, PU_LEVEL, NULL); converted = Picture_FlatConvert(PICFMT_PATCH, patch, PICFMT_FLAT, 0, &size, levelflat->width, levelflat->height, patch->topoffset, patch->leftoffset, 0); - M_Memcpy(levelflat->flatpatch, converted, size); + M_Memcpy(levelflat->picture, converted, size); Z_Free(converted); } } @@ -591,7 +591,7 @@ void *R_GetLevelFlat(levelflat_t *levelflat) levelflat->u.texture.lastnum = levelflat->u.texture.num; if (flatdata == NULL) - flatdata = levelflat->flatpatch; + flatdata = levelflat->picture; return flatdata; } From 679eb3902febbf4dfa9fd58814f91f311a038c93 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 7 Jan 2020 14:11:57 -0300 Subject: [PATCH 016/136] How did I even manage to do this? --- src/r_textures.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/r_textures.h b/src/r_textures.h index 62ef523b2..6b7fc9f1c 100644 --- a/src/r_textures.h +++ b/src/r_textures.h @@ -18,7 +18,6 @@ #include "r_state.h" #include "p_setup.h" // levelflats #include "r_data.h" -#include "r_textures.h" #ifdef __GNUG__ #pragma interface From 9c66740e2b64937327eeab491e4dcaace62d8df7 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Fri, 3 Jan 2020 23:25:26 -0300 Subject: [PATCH 017/136] AA trees are not needed at all for rotated patches --- src/hardware/hw_cache.c | 17 ----------------- src/hardware/hw_glob.h | 3 --- src/r_defs.h | 3 --- src/r_picformats.c | 6 ++++-- src/r_things.c | 3 --- 5 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index df1e18f9d..d8f491f48 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -1388,23 +1388,6 @@ GLPatch_t *HWR_GetCachedGLPatch(lumpnum_t lumpnum) return HWR_GetCachedGLPatchPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum)); } -#ifdef ROTSPRITE -GLPatch_t *HWR_GetCachedGLRotSprite(aatree_t *hwrcache, UINT16 rollangle, patch_t *rawpatch) -{ - GLPatch_t *grpatch; - - if (!(grpatch = M_AATreeGet(hwrcache, rollangle))) - { - grpatch = Z_Calloc(sizeof(GLPatch_t), PU_HWRPATCHINFO, NULL); - grpatch->rawpatch = rawpatch; - grpatch->mipmap = Z_Calloc(sizeof(GLMipmap_t), PU_HWRPATCHINFO, NULL); - M_AATreeSet(hwrcache, rollangle, grpatch); - } - - return grpatch; -} -#endif - // Need to do this because they aren't powers of 2 static void HWR_DrawFadeMaskInCache(GLMipmap_t *mipmap, INT32 pblockwidth, INT32 pblockheight, lumpnum_t fademasklumpnum, UINT16 fmwidth, UINT16 fmheight) diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h index cf98e7317..a2bf79817 100644 --- a/src/hardware/hw_glob.h +++ b/src/hardware/hw_glob.h @@ -113,9 +113,6 @@ GLPatch_t *HWR_GetPic(lumpnum_t lumpnum); void HWR_SetPalette(RGBA_t *palette); GLPatch_t *HWR_GetCachedGLPatchPwad(UINT16 wad, UINT16 lump); GLPatch_t *HWR_GetCachedGLPatch(lumpnum_t lumpnum); -#ifdef ROTSPRITE -GLPatch_t *HWR_GetCachedGLRotSprite(aatree_t *hwrcache, UINT16 rollangle, patch_t *rawpatch); -#endif void HWR_GetFadeMask(lumpnum_t fademasklumpnum); // -------- diff --git a/src/r_defs.h b/src/r_defs.h index c7c198d66..176f33c4f 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -729,9 +729,6 @@ typedef struct { patch_t *patch[8][ROTANGLES]; boolean cached[8]; -#ifdef HWRENDER - aatree_t *hardware_patch[8]; -#endif/*HWRENDER*/ } rotsprite_t; #endif/*ROTSPRITE*/ diff --git a/src/r_picformats.c b/src/r_picformats.c index 35fb0c055..e0688e31a 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -1613,9 +1613,11 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp #ifdef HWRENDER if (rendermode == render_opengl) { - GLPatch_t *grPatch = HWR_GetCachedGLRotSprite(sprframe->rotsprite.hardware_patch[rot], angle, newpatch); - HWR_MakePatch(newpatch, grPatch, grPatch->mipmap, false); + GLPatch_t *grPatch = Z_Calloc(sizeof(GLPatch_t), PU_HWRPATCHINFO, NULL); + grPatch->mipmap = Z_Calloc(sizeof(GLMipmap_t), PU_HWRPATCHINFO, NULL); + grPatch->rawpatch = newpatch; sprframe->rotsprite.patch[rot][angle] = (patch_t *)grPatch; + HWR_MakePatch(newpatch, grPatch, grPatch->mipmap, false); } else #endif // HWRENDER diff --git a/src/r_things.c b/src/r_things.c index 5cd74ef7b..0f1dc87cd 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -125,9 +125,6 @@ static void R_InstallSpriteLump(UINT16 wad, // graphics patch sprtemp[frame].rotsprite.cached[r] = false; for (ang = 0; ang < ROTANGLES; ang++) sprtemp[frame].rotsprite.patch[r][ang] = NULL; -#ifdef HWRENDER - sprtemp[frame].rotsprite.hardware_patch[r] = M_AATreeAlloc(AATREE_ZUSER); -#endif/*HWRENDER*/ } #endif/*ROTSPRITE*/ From c7eed473403ffb116269cc1a57663ee8687d1bba Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 7 Jan 2020 16:51:02 -0300 Subject: [PATCH 018/136] Provide some cleaner method to generate textures as flats. R_GenerateTextureAsFlat --- src/r_textures.c | 71 ++++++++++++++++++++++++++++-------------------- src/r_textures.h | 15 ++++------ 2 files changed, 47 insertions(+), 39 deletions(-) diff --git a/src/r_textures.c b/src/r_textures.c index 2e9c65e5c..48049bc42 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -39,7 +39,7 @@ #include // -// MAPTEXTURE_T CACHING +// TEXTURE_T CACHING // When a texture is first needed, it counts the number of composite columns // required in the texture and allocates space for a column directory and // any new columns. @@ -52,7 +52,6 @@ INT32 numtextures = 0; // total number of textures found, // size of following tables texture_t **textures = NULL; -textureflat_t *texflats = NULL; UINT32 **texturecolumnofs; // column offset lookup table for each texture UINT8 **texturecache; // graphics data for each generated full-size texture @@ -451,6 +450,32 @@ done: return blocktex; } +// +// R_GenerateTextureAsFlat +// +// Generates a flat picture for a texture. +// +UINT8 *R_GenerateTextureAsFlat(size_t texnum) +{ + texture_t *texture = textures[texnum]; + UINT8 *converted = NULL; + size_t size = (texture->width * texture->height); + + // The flat picture for this texture was not generated yet. + if (!texture->flat) + { + // Well, let's do it now, then. + texture->flat = Z_Malloc(size, PU_STATIC, NULL); + + // Picture_TextureToFlat handles everything for us. + converted = (UINT8 *)Picture_TextureToFlat(texnum); + M_Memcpy(texture->flat, converted, size); + Z_Free(converted); + } + + return texture->flat; +} + // // R_GetTextureNum // @@ -509,46 +534,34 @@ void *R_GetFlat(lumpnum_t flatlumpnum) // void *R_GetLevelFlat(levelflat_t *levelflat) { + boolean isleveltexture = (levelflat->type == LEVELFLAT_TEXTURE); + texture_t *texture = (isleveltexture ? textures[levelflat->u.texture.num] : NULL); + boolean texturechanged = (isleveltexture ? (levelflat->u.texture.num != levelflat->u.texture.lastnum) : false); UINT8 *flatdata = NULL; - boolean leveltexture = (levelflat->type == LEVELFLAT_TEXTURE); - textureflat_t *texflat = &texflats[levelflat->u.texture.num]; - boolean texturechanged = (leveltexture ? (levelflat->u.texture.num != levelflat->u.texture.lastnum) : false); // Check if the texture changed. - if (leveltexture && (!texturechanged)) + if (isleveltexture && (!texturechanged)) { - if (texflat != NULL && texflat->flat) + if (texture->flat) { - flatdata = texflat->flat; - ds_flatwidth = texflat->width; - ds_flatheight = texflat->height; + flatdata = texture->flat; + ds_flatwidth = texture->width; + ds_flatheight = texture->height; texturechanged = false; } else texturechanged = true; } - // If the texture changed, or the patch doesn't exist, convert either of them to a flat. + // If the texture changed, or the flat wasn't generated, convert. if (levelflat->picture == NULL || texturechanged) { // Level texture - if (leveltexture) + if (isleveltexture) { - UINT8 *converted; - size_t size; - texture_t *texture = textures[levelflat->u.texture.num]; - texflat->width = ds_flatwidth = texture->width; - texflat->height = ds_flatheight = texture->height; - - size = (texflat->width * texflat->height); - texflat->flat = Z_Malloc(size, PU_LEVEL, NULL); - converted = (UINT8 *)Picture_TextureToFlat(levelflat->u.texture.num); - M_Memcpy(texflat->flat, converted, size); - Z_Free(converted); - - levelflat->picture = texflat->flat; - levelflat->width = ds_flatwidth; - levelflat->height = ds_flatheight; + levelflat->picture = R_GenerateTextureAsFlat(levelflat->u.texture.num); + ds_flatwidth = levelflat->width = texture->width; + ds_flatheight = levelflat->height = texture->height; } else { @@ -714,12 +727,13 @@ void R_LoadTextures(void) { for (i = 0; i < numtextures; i++) { + if (textures[i]->flat) + Z_Free(textures[i]->flat); Z_Free(textures[i]); Z_Free(texturecache[i]); } Z_Free(texturetranslation); Z_Free(textures); - Z_Free(texflats); } // Load patches and textures. @@ -816,7 +830,6 @@ countflats: // Allocate memory and initialize to 0 for all the textures we are initialising. // There are actually 5 buffers allocated in one for convenience. textures = Z_Calloc((numtextures * sizeof(void *)) * 5, PU_STATIC, NULL); - texflats = Z_Calloc((numtextures * sizeof(*texflats)), PU_STATIC, NULL); // Allocate texture column offset table. texturecolumnofs = (void *)((UINT8 *)textures + (numtextures * sizeof(void *))); diff --git a/src/r_textures.h b/src/r_textures.h index 6b7fc9f1c..74a94a9ed 100644 --- a/src/r_textures.h +++ b/src/r_textures.h @@ -47,8 +47,8 @@ enum #endif }; -// A maptexturedef_t describes a rectangular texture, -// which is composed of one or more mappatch_t structures +// A texture_t describes a rectangular texture, +// which is composed of one or more texpatch_t structures // that arrange graphic patches. typedef struct { @@ -58,21 +58,15 @@ typedef struct INT16 width, height; boolean holes; UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both + void *flat; // The texture, as a flat. // All the patches[patchcount] are drawn back to front into the cached texture. INT16 patchcount; texpatch_t patches[0]; } texture_t; -typedef struct -{ - UINT8 *flat; - INT16 width, height; -} textureflat_t; - // all loaded and prepared textures from the start of the game extern texture_t **textures; -extern textureflat_t *texflats; extern INT32 *texturewidth; extern fixed_t *textureheight; // needed for texture pegging @@ -86,14 +80,15 @@ void R_FlushTextureCache(void); // Texture generation UINT8 *R_GenerateTexture(size_t texnum); +UINT8 *R_GenerateTextureAsFlat(size_t texnum); INT32 R_GetTextureNum(INT32 texnum); void R_CheckTextureCache(INT32 tex); void R_ClearTextureNumCache(boolean btell); // Retrieve texture data. +void *R_GetLevelFlat(levelflat_t *levelflat); UINT8 *R_GetColumn(fixed_t tex, INT32 col); void *R_GetFlat(lumpnum_t flatnum); -void *R_GetLevelFlat(levelflat_t *levelflat); boolean R_CheckPowersOfTwo(void); void R_CheckFlatLength(size_t size); From f39368dfb0c88128fb4de5190d90b65d07fce1b9 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 7 Jan 2020 18:52:12 -0300 Subject: [PATCH 019/136] use enum for bpp --- src/hardware/hw_cache.c | 14 +++++----- src/r_picformats.c | 60 ++++++++++++++++++++--------------------- src/r_picformats.h | 8 ++++++ 3 files changed, 46 insertions(+), 36 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index d8f491f48..c81c4bd4a 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -118,6 +118,10 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm count--; texel = source[yfrac>>FRACBITS]; + alpha = 0xFF; + // Make pixel transparent if chroma keyed + if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)) + alpha = 0x00; //Hurdler: 25/04/2000: now support colormap in hardware mode if (mipmap->colormap) @@ -230,17 +234,15 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, count--; texel = source[yfrac>>FRACBITS]; + alpha = 0xFF; + // Make pixel transparent if chroma keyed + if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)) + alpha = 0x00; //Hurdler: 25/04/2000: now support colormap in hardware mode if (mipmap->colormap) texel = mipmap->colormap[texel]; - // transparent pixel - if (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX) - alpha = 0x00; - else - alpha = 0xff; - // hope compiler will get this switch out of the loops (dreams...) // gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?) // Alam: SRB2 uses Mingw, HUGS diff --git a/src/r_picformats.c b/src/r_picformats.c index e0688e31a..9cac5384d 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -128,9 +128,9 @@ void *Picture_PatchConvert( else if (informat == outformat) I_Error("Picture_PatchConvert: input and output formats were the same!"); - if (!inbpp) + if (inbpp == PICDEPTH_NONE) I_Error("Picture_PatchConvert: unknown input bits per pixel?!"); - if (!Picture_FormatBPP(outformat)) + if (Picture_FormatBPP(outformat) == PICDEPTH_NONE) I_Error("Picture_PatchConvert: unknown output bits per pixel?!"); // If it's a patch, you can just figure out @@ -199,17 +199,17 @@ void *Picture_PatchConvert( if (input != NULL) { UINT8 alpha = 0xFF; - if (inbpp == 32) + if (inbpp == PICDEPTH_32BPP) { RGBA_t px = *(RGBA_t *)input; alpha = px.s.alpha; } - else if (inbpp == 16) + else if (inbpp == PICDEPTH_16BPP) { UINT16 px = *(UINT16 *)input; alpha = (px & 0xFF00) >> 8; } - else if (inbpp == 8) + else if (inbpp == PICDEPTH_8BPP) { UINT8 px = *(UINT8 *)input; if (px == TRANSPARENTPIXEL) @@ -272,12 +272,12 @@ void *Picture_PatchConvert( { case PICFMT_PATCH32: { - if (inbpp == 32) + if (inbpp == PICDEPTH_32BPP) { RGBA_t out = *(RGBA_t *)input; WRITEUINT32(imgptr, out.rgba); } - else if (inbpp == 16) + else if (inbpp == PICDEPTH_16BPP) { RGBA_t out = pMasterPalette[*((UINT16 *)input) & 0xFF]; WRITEUINT32(imgptr, out.rgba); @@ -290,26 +290,26 @@ void *Picture_PatchConvert( break; } case PICFMT_PATCH16: - if (inbpp == 32) + if (inbpp == PICDEPTH_32BPP) { RGBA_t in = *(RGBA_t *)input; UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); WRITEUINT16(imgptr, (0xFF00 | out)); } - else if (inbpp == 16) + else if (inbpp == PICDEPTH_16BPP) WRITEUINT16(imgptr, *(UINT16 *)input); else // PICFMT_PATCH WRITEUINT16(imgptr, (0xFF00 | (*(UINT8 *)input))); break; default: // PICFMT_PATCH { - if (inbpp == 32) + if (inbpp == PICDEPTH_32BPP) { RGBA_t in = *(RGBA_t *)input; UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); WRITEUINT8(imgptr, out); } - else if (inbpp == 16) + else if (inbpp == PICDEPTH_16BPP) { UINT16 out = *(UINT16 *)input; WRITEUINT8(imgptr, (out & 0xFF)); @@ -377,9 +377,9 @@ void *Picture_FlatConvert( else if (informat == outformat) I_Error("Picture_FlatConvert: input and output formats were the same!"); - if (!inbpp) + if (inbpp == PICDEPTH_NONE) I_Error("Picture_FlatConvert: unknown input bits per pixel?!"); - if (!outbpp) + if (outbpp == PICDEPTH_NONE) I_Error("Picture_FlatConvert: unknown output bits per pixel?!"); // If it's a patch, you can just figure out @@ -397,7 +397,7 @@ void *Picture_FlatConvert( *outsize = size; // Set transparency - if (outbpp == 8) + if (outbpp == PICDEPTH_8BPP) memset(outflat, TRANSPARENTPIXEL, size); for (y = 0; y < inheight; y++) @@ -422,12 +422,12 @@ void *Picture_FlatConvert( case PICFMT_FLAT32: { UINT32 *f32 = (UINT32 *)outflat; - if (inbpp == 32) + if (inbpp == PICDEPTH_32BPP) { RGBA_t out = *(RGBA_t *)input; f32[offs] = out.rgba; } - else if (inbpp == 16) + else if (inbpp == PICDEPTH_16BPP) { RGBA_t out = pMasterPalette[*((UINT16 *)input) & 0xFF]; f32[offs] = out.rgba; @@ -442,13 +442,13 @@ void *Picture_FlatConvert( case PICFMT_FLAT16: { UINT16 *f16 = (UINT16 *)outflat; - if (inbpp == 32) + if (inbpp == PICDEPTH_32BPP) { RGBA_t in = *(RGBA_t *)input; UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); f16[offs] = (0xFF00 | out); } - else if (inbpp == 16) + else if (inbpp == PICDEPTH_16BPP) f16[offs] = *(UINT16 *)input; else // PICFMT_PATCH f16[offs] = (0xFF00 | *((UINT8 *)input)); @@ -457,13 +457,13 @@ void *Picture_FlatConvert( case PICFMT_FLAT: { UINT8 *f8 = (UINT8 *)outflat; - if (inbpp == 32) + if (inbpp == PICDEPTH_32BPP) { RGBA_t in = *(RGBA_t *)input; UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); f8[offs] = out; } - else if (inbpp == 16) + else if (inbpp == PICDEPTH_16BPP) { UINT16 out = *(UINT16 *)input; f8[offs] = (out & 0xFF); @@ -553,21 +553,21 @@ void *Picture_GetPatchPixel( */ INT32 Picture_FormatBPP(pictureformat_t format) { - INT32 bpp = 0; + INT32 bpp = PICDEPTH_NONE; switch (format) { case PICFMT_PATCH32: case PICFMT_FLAT32: case PICFMT_PNG: - bpp = 32; + bpp = PICDEPTH_32BPP; break; case PICFMT_PATCH16: case PICFMT_FLAT16: - bpp = 16; + bpp = PICDEPTH_16BPP; break; case PICFMT_PATCH: case PICFMT_FLAT: - bpp = 8; + bpp = PICDEPTH_8BPP; break; default: break; @@ -938,12 +938,12 @@ void *Picture_PNGConvert( if (Picture_IsPatchFormat(outformat)) { // Force a higher bit depth - if (outbpp == 8) - outbpp = 16; + if (outbpp == PICDEPTH_8BPP) + outbpp = PICDEPTH_16BPP; } // Shouldn't happen. - if (!outbpp) + if (outbpp == PICDEPTH_NONE) I_Error("Picture_PNGConvert: unknown output bits per pixel?!"); // Figure out the size @@ -955,7 +955,7 @@ void *Picture_PNGConvert( flat = Z_Calloc(flatsize, PU_STATIC, NULL); // Set transparency - if (outbpp == 8) + if (outbpp == PICDEPTH_8BPP) memset(flat, TRANSPARENTPIXEL, (width * height)); for (y = 0; y < height; y++) @@ -970,7 +970,7 @@ void *Picture_PNGConvert( UINT8 green = (UINT8)px[1]; UINT8 blue = (UINT8)px[2]; UINT8 alpha = (UINT8)px[3]; - if (outbpp == 32) + if (outbpp == PICDEPTH_32BPP) { UINT32 *outflat = (UINT32 *)flat; RGBA_t out; @@ -983,7 +983,7 @@ void *Picture_PNGConvert( else { UINT8 palidx = NearestColor(red, green, blue); - if (outbpp == 16) + if (outbpp == PICDEPTH_16BPP) { UINT16 *outflat = (UINT16 *)flat; outflat[((y * width) + x)] = (alpha << 8) | palidx; diff --git a/src/r_picformats.h b/src/r_picformats.h index 137ddbdd4..84a88e12f 100644 --- a/src/r_picformats.h +++ b/src/r_picformats.h @@ -43,6 +43,14 @@ typedef enum PICFLAGS_YFLIP = 1<<1 } pictureflags_t; +enum +{ + PICDEPTH_NONE = 0, + PICDEPTH_8BPP = 8, + PICDEPTH_16BPP = 16, + PICDEPTH_32BPP = 32 +}; + void *Picture_Convert( pictureformat_t informat, void *picture, pictureformat_t outformat, size_t insize, size_t *outsize, From 058ad19433b0d25146ec392b15e1b75f8f847f73 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 7 Jan 2020 19:00:57 -0300 Subject: [PATCH 020/136] Fix patch generation --- src/hardware/hw_cache.c | 3 ++- src/r_picformats.c | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index c81c4bd4a..59504be73 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -708,7 +708,8 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm { // Dummy variables. INT32 pngwidth, pngheight; - patch = (patch_t *)Picture_PNGConvert((const UINT8 *)patch, PICFMT_PATCH, &pngwidth, &pngheight, NULL, NULL, len, NULL, 0); + INT16 topoffset, leftoffset; + patch = (patch_t *)Picture_PNGConvert((const UINT8 *)patch, PICFMT_PATCH, &pngwidth, &pngheight, &topoffset, &leftoffset, len, NULL, 0); } #endif diff --git a/src/r_picformats.c b/src/r_picformats.c index 9cac5384d..27d4c010f 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -1478,6 +1478,15 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp patch = (patch_t *)W_CacheLumpNum(lump, PU_STATIC); lumplength = W_LumpLength(lump); +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((const UINT8 *)patch, lumplength)) + { + INT32 pngwidth, pngheight; + INT16 topoffset, leftoffset; + patch = (patch_t *)Picture_PNGConvert((const UINT8 *)patch, PICFMT_PATCH, &pngwidth, &pngheight, &topoffset, &leftoffset, lumplength, NULL, 0); + } + else +#endif // Because there's something wrong with SPR_DFLM, I guess if (!Picture_CheckIfPatch(patch, lumplength)) return; From c85c1550c3e062e049d86741286ff7eadb1b8841 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 7 Jan 2020 19:22:47 -0300 Subject: [PATCH 021/136] Fix Picture_GetPatchPixel --- src/r_picformats.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/r_picformats.c b/src/r_picformats.c index 27d4c010f..cb235590a 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -532,14 +532,20 @@ void *Picture_GetPatchPixel( if ((topdelta + ofs) == y) { if (informat == PICFMT_PATCH32) - return (s32 + ofs); + return &s32[ofs]; else if (informat == PICFMT_PATCH16) - return (s16 + ofs); + return &s16[ofs]; else // PICFMT_PATCH - return (s8 + ofs); + return &s8[ofs]; } } - column = (column_t *)((UINT8 *)column + column->length + 4); + if (informat == PICFMT_PATCH32) + column = (column_t *)((UINT32 *)column + column->length); + else if (informat == PICFMT_PATCH16) + column = (column_t *)((UINT16 *)column + column->length); + else + column = (column_t *)((UINT8 *)column + column->length); + column = (column_t *)((UINT8 *)column + 4); } } From e459a4484839c8334bc1f97f6621a97b97bc294b Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 7 Jan 2020 20:07:35 -0300 Subject: [PATCH 022/136] Fix shadowed declarations --- src/r_picformats.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/r_picformats.c b/src/r_picformats.c index cb235590a..c7e54cf4e 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -1488,8 +1488,8 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp if (Picture_IsLumpPNG((const UINT8 *)patch, lumplength)) { INT32 pngwidth, pngheight; - INT16 topoffset, leftoffset; - patch = (patch_t *)Picture_PNGConvert((const UINT8 *)patch, PICFMT_PATCH, &pngwidth, &pngheight, &topoffset, &leftoffset, lumplength, NULL, 0); + INT16 toffs, loffs; + patch = (patch_t *)Picture_PNGConvert((const UINT8 *)patch, PICFMT_PATCH, &pngwidth, &pngheight, &toffs, &loffs, lumplength, NULL, 0); } else #endif From 2b9b3d15613867da7efa38d859dc397eebb92e68 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 20 Jan 2020 17:03:38 -0300 Subject: [PATCH 023/136] Remove W_FlushCachedPatches --- src/console.c | 3 --- src/d_main.c | 8 -------- src/hardware/hw_cache.c | 28 ++-------------------------- src/hu_stuff.c | 3 --- src/m_menu.c | 3 --- src/r_main.c | 2 -- src/w_wad.c | 18 ------------------ src/w_wad.h | 1 - src/z_zone.c | 5 ----- 9 files changed, 2 insertions(+), 69 deletions(-) diff --git a/src/console.c b/src/console.c index ba5ba71df..890d424fa 100644 --- a/src/console.c +++ b/src/console.c @@ -1615,10 +1615,7 @@ void CON_Drawer(void) return; if (needpatchrecache) - { - W_FlushCachedPatches(); HU_LoadGraphics(); - } if (con_recalc) { diff --git a/src/d_main.c b/src/d_main.c index 15d3c8041..9891a525c 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -594,18 +594,10 @@ static void D_Display(void) needpatchrecache = false; } -// Lactozilla: Check the renderer's state -// after a possible renderer switch. void D_CheckRendererState(void) { - // flush all patches from memory - // (also frees memory tagged with PU_CACHE) - // (which are not necessarily patches but I don't care) if (needpatchflush) Z_FlushCachedPatches(); - - // some patches have been freed, - // so cache them again if (needpatchrecache) R_ReloadHUDGraphics(); } diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index b998b3360..5f364a3c5 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -971,9 +971,6 @@ static void HWR_CacheTextureAsFlat(GLMipmap_t *grMipmap, INT32 texturenum) UINT8 *converted; size_t size; - if (needpatchflush) - W_FlushCachedPatches(); - // setup the texture info #ifdef GLIDE_API_COMPATIBILITY grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64; @@ -1000,9 +997,6 @@ void HWR_LiterallyGetFlat(lumpnum_t flatlumpnum) if (flatlumpnum == LUMPERROR) return; - if (needpatchflush) - W_FlushCachedPatches(); - grmip = HWR_GetCachedGLPatch(flatlumpnum)->mipmap; if (!grmip->downloaded && !grmip->grInfo.data) HWR_CacheFlat(grmip, flatlumpnum); @@ -1133,9 +1127,6 @@ static void HWR_LoadMappedPatch(GLMipmap_t *grmip, GLPatch_t *gpatch) // -----------------+ void HWR_GetPatch(GLPatch_t *gpatch) { - if (needpatchflush) - W_FlushCachedPatches(); - // is it in hardware cache if (!gpatch->mipmap->downloaded && !gpatch->mipmap->grInfo.data) { @@ -1166,9 +1157,6 @@ void HWR_GetMappedPatch(GLPatch_t *gpatch, const UINT8 *colormap) { GLMipmap_t *grmip, *newmip; - if (needpatchflush) - W_FlushCachedPatches(); - if (colormap == colormaps || colormap == NULL) { // Load the default (green) color in doom cache (temporary?) AND hardware cache @@ -1292,13 +1280,7 @@ static void HWR_DrawPicInCache(UINT8 *block, INT32 pblockwidth, INT32 pblockheig // -----------------+ GLPatch_t *HWR_GetPic(lumpnum_t lumpnum) { - GLPatch_t *grpatch; - - if (needpatchflush) - W_FlushCachedPatches(); - - grpatch = HWR_GetCachedGLPatch(lumpnum); - + GLPatch_t *grpatch = HWR_GetCachedGLPatch(lumpnum); if (!grpatch->mipmap->downloaded && !grpatch->mipmap->grInfo.data) { pic_t *pic; @@ -1481,13 +1463,7 @@ static void HWR_CacheFadeMask(GLMipmap_t *grMipmap, lumpnum_t fademasklumpnum) void HWR_GetFadeMask(lumpnum_t fademasklumpnum) { - GLMipmap_t *grmip; - - if (needpatchflush) - W_FlushCachedPatches(); - - grmip = HWR_GetCachedGLPatch(fademasklumpnum)->mipmap; - + GLMipmap_t *grmip = HWR_GetCachedGLPatch(fademasklumpnum)->mipmap; if (!grmip->downloaded && !grmip->grInfo.data) HWR_CacheFadeMask(grmip, fademasklumpnum); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 073b21b92..cf0204016 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2100,9 +2100,6 @@ static void HU_DrawDemoInfo(void) // void HU_Drawer(void) { - if (needpatchrecache) - R_ReloadHUDGraphics(); - #ifndef NONET // draw chat string plus cursor if (chat_on) diff --git a/src/m_menu.c b/src/m_menu.c index 62bea7ae0..44ce674b4 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8641,8 +8641,6 @@ void M_ForceSaveSlotSelected(INT32 sslot) // ================ // CHARACTER SELECT // ================ - -// lactozilla: sometimes the renderer changes and these patches don't exist anymore static void M_CacheCharacterSelectEntry(INT32 i, INT32 skinnum) { if (!(description[i].picname[0])) @@ -8874,7 +8872,6 @@ static void M_DrawSetupChoosePlayerMenu(void) INT32 x, y; INT32 w = (vid.width/vid.dupx); - // lactozilla: the renderer changed so recache patches if (needpatchrecache) M_CacheCharacterSelect(); diff --git a/src/r_main.c b/src/r_main.c index 4b044c6cc..0942d39d0 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1176,7 +1176,6 @@ void R_RenderPlayerView(player_t *player) free(masks); } -// Lactozilla: Renderer switching #ifdef HWRENDER void R_InitHardwareMode(void) { @@ -1190,7 +1189,6 @@ void R_InitHardwareMode(void) void R_ReloadHUDGraphics(void) { - CONS_Debug(DBG_RENDER, "R_ReloadHUDGraphics()...\n"); ST_LoadGraphics(); HU_LoadGraphics(); ST_ReloadSkinFaceGraphics(); diff --git a/src/w_wad.c b/src/w_wad.c index 44a4854f8..ed98e4ad1 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1465,21 +1465,6 @@ boolean W_IsPatchCached(lumpnum_t lumpnum, void *ptr) return W_IsPatchCachedPWAD(WADFILENUM(lumpnum),LUMPNUM(lumpnum), ptr); } -void W_FlushCachedPatches(void) -{ - if (needpatchflush) - { - Z_FreeTag(PU_CACHE); - Z_FreeTag(PU_PATCH); - Z_FreeTag(PU_HUDGFX); - Z_FreeTag(PU_HWRPATCHINFO); - Z_FreeTag(PU_HWRMODELTEXTURE); - Z_FreeTag(PU_HWRCACHE); - Z_FreeTags(PU_HWRCACHE_UNLOCKED, PU_HWRPATCHINFO_UNLOCKED); - } - needpatchflush = false; -} - // ========================================================================== // W_CacheLumpName // ========================================================================== @@ -1509,9 +1494,6 @@ void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) GLPatch_t *grPatch; #endif - if (needpatchflush) - W_FlushCachedPatches(); - if (!TestValidLump(wad, lump)) return NULL; diff --git a/src/w_wad.h b/src/w_wad.h index aca67c00f..8008a577c 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -196,7 +196,6 @@ void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag); // return a patch void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag); // return a patch_t void W_UnlockCachedPatch(void *patch); -void W_FlushCachedPatches(void); void W_VerifyFileMD5(UINT16 wadfilenum, const char *matchmd5); diff --git a/src/z_zone.c b/src/z_zone.c index d02717007..edff5a60e 100644 --- a/src/z_zone.c +++ b/src/z_zone.c @@ -498,13 +498,9 @@ void Z_FreeTags(INT32 lowtag, INT32 hightag) // Utility functions // ----------------- -// for renderer switching, free a bunch of stuff boolean needpatchflush = false; boolean needpatchrecache = false; -// flush all patches from memory -// (also frees memory tagged with PU_CACHE) -// (which are not necessarily patches but I don't care) void Z_FlushCachedPatches(void) { CONS_Debug(DBG_RENDER, "Z_FlushCachedPatches()...\n"); @@ -518,7 +514,6 @@ void Z_FlushCachedPatches(void) Z_FreeTag(PU_HWRPATCHINFO_UNLOCKED); } -// happens before a renderer switch void Z_PreparePatchFlush(void) { CONS_Debug(DBG_RENDER, "Z_PreparePatchFlush()...\n"); From 1bf1da749e2079273e3f8dbeeaea73fc6ffe662a Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Fri, 15 May 2020 15:43:40 -0300 Subject: [PATCH 024/136] Fix memory leak --- src/r_textures.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/r_textures.c b/src/r_textures.c index 8ff85de43..7e3d8445d 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -410,10 +410,18 @@ UINT8 *R_GenerateTexture(size_t texnum) x2 = x1 + width; if (x1 > texture->width || x2 < 0) + { + if (dealloc) + Z_Free(realpatch); continue; // patch not located within texture's x bounds, ignore + } if (patch->originy > texture->height || (patch->originy + height) < 0) + { + if (dealloc) + Z_Free(realpatch); continue; // patch not located within texture's y bounds, ignore + } // patch is actually inside the texture! // now check if texture is partly off-screen and adjust accordingly From 36e1c30e19fd5fe639a344c6451dbdd6d515fc8c Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 6 Jun 2020 04:36:34 -0500 Subject: [PATCH 025/136] Replace all occurances of BT_USE with BT_SPIN (2.3?) I'd use `next-major` but it doesn't exist lol --- src/b_bot.c | 4 ++-- src/d_ticcmd.h | 2 +- src/dehacked.c | 2 +- src/f_finale.c | 6 +++--- src/g_game.c | 2 +- src/m_cheat.c | 6 +++--- src/p_map.c | 2 +- src/p_spec.c | 2 +- src/p_user.c | 40 ++++++++++++++++++++-------------------- src/st_stuff.c | 2 +- src/y_inter.c | 4 ++-- 11 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/b_bot.c b/src/b_bot.c index 4397938c1..d3635f32c 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -193,7 +193,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) { cmd->forwardmove = pcmd->forwardmove; cmd->sidemove = pcmd->sidemove; - if (pcmd->buttons & BT_USE) + if (pcmd->buttons & BT_SPIN) { spin = true; jump = false; @@ -441,7 +441,7 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward if (jump) cmd->buttons |= BT_JUMP; if (spin) - cmd->buttons |= BT_USE; + cmd->buttons |= BT_SPIN; } void B_MoveBlocked(player_t *player) diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 0a8012bb1..74a92a823 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -31,7 +31,7 @@ typedef enum BT_WEAPONPREV = 1<<5, BT_ATTACK = 1<<6, // shoot rings - BT_USE = 1<<7, // spin + BT_SPIN = 1<<7, // spin BT_CAMLEFT = 1<<8, // turn camera left BT_CAMRIGHT = 1<<9, // turn camera right BT_TOSSFLAG = 1<<10, diff --git a/src/dehacked.c b/src/dehacked.c index 99d4883f6..f1e977f35 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -9938,7 +9938,7 @@ struct { {"BT_WEAPONNEXT",BT_WEAPONNEXT}, {"BT_WEAPONPREV",BT_WEAPONPREV}, {"BT_ATTACK",BT_ATTACK}, // shoot rings - {"BT_USE",BT_USE}, // spin + {"BT_SPIN",BT_SPIN}, // spin {"BT_CAMLEFT",BT_CAMLEFT}, // turn camera left {"BT_CAMRIGHT",BT_CAMRIGHT}, // turn camera right {"BT_TOSSFLAG",BT_TOSSFLAG}, diff --git a/src/f_finale.c b/src/f_finale.c index f47c6c1a7..11f4ac475 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -4097,7 +4097,7 @@ void F_CutsceneTicker(void) if (netgame && i != serverplayer && !IsPlayerAdmin(i)) continue; - if (players[i].cmd.buttons & BT_USE) + if (players[i].cmd.buttons & BT_SPIN) { keypressed = false; cutscene_boostspeed = 1; @@ -4698,7 +4698,7 @@ void F_TextPromptTicker(void) else continue; - if ((players[i].cmd.buttons & BT_USE) || (players[i].cmd.buttons & BT_JUMP)) + if ((players[i].cmd.buttons & BT_SPIN) || (players[i].cmd.buttons & BT_JUMP)) { if (timetonext > 1) timetonext--; @@ -4721,7 +4721,7 @@ void F_TextPromptTicker(void) } keypressed = true; // prevent repeat events } - else if (!(players[i].cmd.buttons & BT_USE) && !(players[i].cmd.buttons & BT_JUMP)) + else if (!(players[i].cmd.buttons & BT_SPIN) && !(players[i].cmd.buttons & BT_JUMP)) keypressed = false; if (!splitscreen) diff --git a/src/g_game.c b/src/g_game.c index 69aac5065..ab0bdd265 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1341,7 +1341,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // use with any button/key axis = PlayerJoyAxis(ssplayer, AXISSPIN); if (PLAYERINPUTDOWN(ssplayer, gc_use) || (usejoystick && axis > 0)) - cmd->buttons |= BT_USE; + cmd->buttons |= BT_SPIN; // Centerview can be a toggle in simple mode! { diff --git a/src/m_cheat.c b/src/m_cheat.c index c42763afd..ab1454503 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -1144,7 +1144,7 @@ void OP_NightsObjectplace(player_t *player) if (player->pflags & PF_ATTACKDOWN) { // Are ANY objectplace buttons pressed? If no, remove flag. - if (!(cmd->buttons & (BT_ATTACK|BT_TOSSFLAG|BT_USE|BT_WEAPONNEXT|BT_WEAPONPREV))) + if (!(cmd->buttons & (BT_ATTACK|BT_TOSSFLAG|BT_SPIN|BT_WEAPONNEXT|BT_WEAPONPREV))) player->pflags &= ~PF_ATTACKDOWN; // Do nothing. @@ -1251,7 +1251,7 @@ void OP_NightsObjectplace(player_t *player) } // This places a custom object as defined in the console cv_mapthingnum. - if (cmd->buttons & BT_USE) + if (cmd->buttons & BT_SPIN) { UINT16 angle; @@ -1306,7 +1306,7 @@ void OP_ObjectplaceMovement(player_t *player) if (cmd->buttons & BT_JUMP) player->mo->z += FRACUNIT*cv_speed.value; - else if (cmd->buttons & BT_USE) + else if (cmd->buttons & BT_SPIN) player->mo->z -= FRACUNIT*cv_speed.value; if (cmd->forwardmove != 0) diff --git a/src/p_map.c b/src/p_map.c index f7db52f6a..74c2790f7 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -380,7 +380,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) if ((spring->info->painchance == 3)) { if (!(pflags = (object->player->pflags & PF_SPINNING)) && - (((object->player->charability2 == CA2_SPINDASH) && (object->player->cmd.buttons & BT_USE)) + (((object->player->charability2 == CA2_SPINDASH) && (object->player->cmd.buttons & BT_SPIN)) || (spring->flags2 & MF2_AMBUSH))) { pflags = PF_SPINNING; diff --git a/src/p_spec.c b/src/p_spec.c index b7fdedfd0..eb745a4e2 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -5000,7 +5000,7 @@ DoneSection2: if (player->mo->momz > 0) break; - if (player->cmd.buttons & BT_USE) + if (player->cmd.buttons & BT_SPIN) break; if (!(player->pflags & PF_SLIDING) && player->mo->state == &states[player->mo->info->painstate]) diff --git a/src/p_user.c b/src/p_user.c index d426277ff..a4aff1499 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2303,7 +2303,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) if (dorollstuff) { if ((player->charability2 == CA2_SPINDASH) && !((player->pflags & (PF_SPINNING|PF_THOKKED)) == PF_THOKKED) && !(player->charability == CA_THOK && player->secondjump) - && (player->cmd.buttons & BT_USE) && (FixedHypot(player->mo->momx, player->mo->momy) > (5*player->mo->scale))) + && (player->cmd.buttons & BT_SPIN) && (FixedHypot(player->mo->momx, player->mo->momy) > (5*player->mo->scale))) player->pflags = (player->pflags|PF_SPINNING) & ~PF_THOKKED; else if (!(player->pflags & PF_STARTDASH)) player->pflags &= ~PF_SPINNING; @@ -2368,7 +2368,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) } } else if (player->charability2 == CA2_MELEE - && ((player->panim == PA_ABILITY2) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY && player->cmd.buttons & (BT_JUMP|BT_USE)))) + && ((player->panim == PA_ABILITY2) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY && player->cmd.buttons & (BT_JUMP|BT_SPIN)))) { if (player->mo->state-states != S_PLAY_MELEE_LANDING) { @@ -3576,7 +3576,7 @@ static void P_DoClimbing(player_t *player) else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && player->mo->state-states != S_PLAY_CLING) P_SetPlayerMobjState(player->mo, S_PLAY_CLING); - if (cmd->buttons & BT_USE && !(player->pflags & PF_JUMPSTASIS)) + if (cmd->buttons & BT_SPIN && !(player->pflags & PF_JUMPSTASIS)) { player->climbing = 0; player->pflags |= P_GetJumpFlags(player); @@ -4574,7 +4574,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) && (player->pflags & PF_JUMPSTASIS || player->mo->state-states != S_PLAY_GLIDE_LANDING)) return; - if (cmd->buttons & BT_USE) + if (cmd->buttons & BT_SPIN) { if (LUAh_SpinSpecial(player)) return; @@ -4591,7 +4591,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) { case CA2_SPINDASH: // Spinning and Spindashing // Start revving - if ((cmd->buttons & BT_USE) && (player->speed < FixedMul(5<mo->scale) || player->mo->state - states == S_PLAY_GLIDE_LANDING) + if ((cmd->buttons & BT_SPIN) && (player->speed < FixedMul(5<mo->scale) || player->mo->state - states == S_PLAY_GLIDE_LANDING) && !player->mo->momz && onground && !(player->pflags & (PF_USEDOWN|PF_SPINNING)) && canstand) { @@ -4604,7 +4604,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) S_StartSound(player->mo, sfx_spndsh); // Make the rev sound! } // Revving - else if ((cmd->buttons & BT_USE) && (player->pflags & PF_STARTDASH)) + else if ((cmd->buttons & BT_SPIN) && (player->pflags & PF_STARTDASH)) { if (player->speed > 5*player->mo->scale) { @@ -4637,7 +4637,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) // If not moving up or down, and travelling faster than a speed of five while not holding // down the spin button and not spinning. // AKA Just go into a spin on the ground, you idiot. ;) - else if ((cmd->buttons & BT_USE || ((twodlevel || (player->mo->flags2 & MF2_TWOD)) && cmd->forwardmove < -20)) + else if ((cmd->buttons & BT_SPIN || ((twodlevel || (player->mo->flags2 & MF2_TWOD)) && cmd->forwardmove < -20)) && !player->climbing && !player->mo->momz && onground && (player->speed > FixedMul(5<mo->scale) || !canstand) && !(player->pflags & (PF_USEDOWN|PF_SPINNING))) { @@ -4690,7 +4690,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) P_SetTarget(&visual->target, lockon); } } - if ((cmd->buttons & BT_USE) && !(player->pflags & PF_USEDOWN)) + if ((cmd->buttons & BT_SPIN) && !(player->pflags & PF_USEDOWN)) { mobj_t *bullet; @@ -4726,7 +4726,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) } break; case CA2_MELEE: // Melee attack - if (player->panim != PA_ABILITY2 && (cmd->buttons & BT_USE) + if (player->panim != PA_ABILITY2 && (cmd->buttons & BT_SPIN) && !player->mo->momz && onground && !(player->pflags & PF_USEDOWN) && canstand) { @@ -5066,7 +5066,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) } } } - if (cmd->buttons & BT_USE && !LUAh_ShieldSpecial(player)) // Spin button effects + if (cmd->buttons & BT_SPIN && !LUAh_ShieldSpecial(player)) // Spin button effects { // Force stop if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE) @@ -5140,7 +5140,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) } } } - else if ((cmd->buttons & BT_USE)) + else if ((cmd->buttons & BT_SPIN)) { if (!(player->pflags & PF_USEDOWN) && P_SuperReady(player)) { @@ -5193,7 +5193,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) { if (player->pflags & PF_JUMPED) { - if (cmd->buttons & BT_USE && player->secondjump < 42) // speed up falling down + if (cmd->buttons & BT_SPIN && player->secondjump < 42) // speed up falling down player->secondjump++; if (player->flyangle > 0 && player->pflags & PF_THOKKED) @@ -6188,7 +6188,7 @@ static void P_SpectatorMovement(player_t *player) if (cmd->buttons & BT_JUMP) player->mo->z += FRACUNIT*16; - else if (cmd->buttons & BT_USE) + else if (cmd->buttons & BT_SPIN) player->mo->z -= FRACUNIT*16; if (player->mo->z > player->mo->ceilingz - player->mo->height) @@ -7410,7 +7410,7 @@ static void P_NiGHTSMovement(player_t *player) // No more bumper braking if (!player->bumpertime && ((cmd->buttons & (BT_CAMLEFT|BT_CAMRIGHT)) == (BT_CAMLEFT|BT_CAMRIGHT) - || (cmd->buttons & BT_USE))) + || (cmd->buttons & BT_SPIN))) { if (!(player->pflags & PF_STARTDASH)) S_StartSound(player->mo, sfx_ngskid); @@ -8464,7 +8464,7 @@ static void P_MovePlayer(player_t *player) S_StartSound(player->mo, sfx_putput); // Descend - if (cmd->buttons & BT_USE && !(player->pflags & PF_STASIS) && !player->exiting && !(player->mo->eflags & MFE_GOOWATER)) + if (cmd->buttons & BT_SPIN && !(player->pflags & PF_STASIS) && !player->exiting && !(player->mo->eflags & MFE_GOOWATER)) if (P_MobjFlip(player->mo)*player->mo->momz > -FixedMul(5*actionspd, player->mo->scale)) { if (player->fly1 > 2) @@ -8828,7 +8828,7 @@ static void P_DoRopeHang(player_t *player) player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed)); player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - playerz, dist), (speed)); - if (player->cmd.buttons & BT_USE && !(player->pflags & PF_STASIS)) // Drop off of the rope + if (player->cmd.buttons & BT_SPIN && !(player->pflags & PF_STASIS)) // Drop off of the rope { player->pflags |= (P_GetJumpFlags(player)|PF_USEDOWN); P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); @@ -9483,7 +9483,7 @@ static void P_DeathThink(player_t *player) // continue logic if (!(netgame || multiplayer) && player->lives <= 0) { - if (player->deadtimer > (3*TICRATE) && (cmd->buttons & BT_USE || cmd->buttons & BT_JUMP) && (!continuesInSession || player->continues > 0)) + if (player->deadtimer > (3*TICRATE) && (cmd->buttons & BT_SPIN || cmd->buttons & BT_JUMP) && (!continuesInSession || player->continues > 0)) G_UseContinue(); else if (player->deadtimer >= gameovertics) G_UseContinue(); // Even if we don't have one this handles ending the game @@ -11017,7 +11017,7 @@ static void P_MinecartThink(player_t *player) else if (detright && player->cmd.sidemove > 0) sidelock = detright; - //if (player->cmd.buttons & BT_USE && currentSpeed > 4*FRACUNIT) + //if (player->cmd.buttons & BT_SPIN && currentSpeed > 4*FRACUNIT) // currentSpeed -= FRACUNIT/8; // Jumping @@ -11694,7 +11694,7 @@ void P_PlayerThink(player_t *player) if ((gametyperules & GTR_RACE) && leveltime < 4*TICRATE) { - cmd->buttons &= BT_USE; // Remove all buttons except BT_USE + cmd->buttons &= BT_SPIN; // Remove all buttons except BT_SPIN cmd->forwardmove = 0; cmd->sidemove = 0; } @@ -12064,7 +12064,7 @@ void P_PlayerThink(player_t *player) // check for use if (player->powers[pw_carry] != CR_NIGHTSMODE) { - if (cmd->buttons & BT_USE) + if (cmd->buttons & BT_SPIN) player->pflags |= PF_USEDOWN; else player->pflags &= ~PF_USEDOWN; diff --git a/src/st_stuff.c b/src/st_stuff.c index 53d988913..947d2311f 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1126,7 +1126,7 @@ static void ST_drawInput(void) V_DrawCharacter(x+16+1+(xoffs), y+1+(yoffs)-offs, hudinfo[HUD_LIVES].f|symb, false) drawbutt( 4,-3, BT_JUMP, 'J'); - drawbutt(15,-3, BT_USE, 'S'); + drawbutt(15,-3, BT_SPIN, 'S'); V_DrawFill(x+16+4, y+8, 21, 10, hudinfo[HUD_LIVES].f|20); // sundial backing if (stplyr->mo) diff --git a/src/y_inter.c b/src/y_inter.c index 58e0c4a88..914dc7691 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1017,7 +1017,7 @@ void Y_Ticker(void) return; for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && (players[i].cmd.buttons & BT_USE)) + if (playeringame[i] && (players[i].cmd.buttons & BT_SPIN)) skip = true; // bonuses count down by 222 each tic @@ -1094,7 +1094,7 @@ void Y_Ticker(void) for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) { - if (players[i].cmd.buttons & BT_USE) + if (players[i].cmd.buttons & BT_SPIN) skip = true; if (players[i].charflags & SF_SUPER) super = true; From 14b9028b9c258d0c2f6f355b7b76a9b4e4ede207 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 6 Jun 2020 04:46:22 -0500 Subject: [PATCH 026/136] Remove 2 characters of whitespace This is what I get for using find+replace I guess lol --- src/d_ticcmd.h | 2 +- src/st_stuff.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 74a92a823..c0e520a82 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -31,7 +31,7 @@ typedef enum BT_WEAPONPREV = 1<<5, BT_ATTACK = 1<<6, // shoot rings - BT_SPIN = 1<<7, // spin + BT_SPIN = 1<<7, // spin BT_CAMLEFT = 1<<8, // turn camera left BT_CAMRIGHT = 1<<9, // turn camera right BT_TOSSFLAG = 1<<10, diff --git a/src/st_stuff.c b/src/st_stuff.c index 947d2311f..13a89bd26 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1126,7 +1126,7 @@ static void ST_drawInput(void) V_DrawCharacter(x+16+1+(xoffs), y+1+(yoffs)-offs, hudinfo[HUD_LIVES].f|symb, false) drawbutt( 4,-3, BT_JUMP, 'J'); - drawbutt(15,-3, BT_SPIN, 'S'); + drawbutt(15,-3, BT_SPIN, 'S'); V_DrawFill(x+16+4, y+8, 21, 10, hudinfo[HUD_LIVES].f|20); // sundial backing if (stplyr->mo) From 549b76423f616ed756cc513e82ad9d175868d678 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 6 Jun 2020 05:32:21 -0500 Subject: [PATCH 027/136] Remove unneeded comment --- src/d_ticcmd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index c0e520a82..2a5ef0981 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -31,7 +31,7 @@ typedef enum BT_WEAPONPREV = 1<<5, BT_ATTACK = 1<<6, // shoot rings - BT_SPIN = 1<<7, // spin + BT_SPIN = 1<<7, BT_CAMLEFT = 1<<8, // turn camera left BT_CAMRIGHT = 1<<9, // turn camera right BT_TOSSFLAG = 1<<10, From acbf9830830e9c5cfc7adfa55cc36682426f946e Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 6 Jun 2020 05:34:39 -0500 Subject: [PATCH 028/136] Remove another unneeded comment --- src/dehacked.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dehacked.c b/src/dehacked.c index f1e977f35..a174418c0 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -9938,7 +9938,7 @@ struct { {"BT_WEAPONNEXT",BT_WEAPONNEXT}, {"BT_WEAPONPREV",BT_WEAPONPREV}, {"BT_ATTACK",BT_ATTACK}, // shoot rings - {"BT_SPIN",BT_SPIN}, // spin + {"BT_SPIN",BT_SPIN}, {"BT_CAMLEFT",BT_CAMLEFT}, // turn camera left {"BT_CAMRIGHT",BT_CAMRIGHT}, // turn camera right {"BT_TOSSFLAG",BT_TOSSFLAG}, From 3c4200901a545e76783f6eb3ce7338dbb53d539a Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 6 Jun 2020 05:52:29 -0500 Subject: [PATCH 029/136] Replace 'use' with 'spin' in game control constants --- src/f_finale.c | 4 ++-- src/g_input.c | 20 ++++++++++---------- src/g_input.h | 10 +++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 11f4ac475..21f355e99 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -4437,11 +4437,11 @@ static boolean F_GetTextPromptTutorialTag(char *tag, INT32 length) else if (!strncmp(tag, "TAJ", 3)) // Jump gcs = G_GetControlScheme(gamecontrol, gcl_jump, num_gcl_jump); else if (!strncmp(tag, "TAS", 3)) // Spin - gcs = G_GetControlScheme(gamecontrol, gcl_use, num_gcl_use); + gcs = G_GetControlScheme(gamecontrol, gcl_spin, num_gcl_spin); else if (!strncmp(tag, "TAA", 3)) // Char ability gcs = G_GetControlScheme(gamecontrol, gcl_jump, num_gcl_jump); else if (!strncmp(tag, "TAW", 3)) // Shield ability - gcs = G_GetControlScheme(gamecontrol, gcl_jump_use, num_gcl_jump_use); + gcs = G_GetControlScheme(gamecontrol, gcl_jump_spin, num_gcl_jump_spin); else gcs = G_GetControlScheme(gamecontrol, gcl_tutorial_used, num_gcl_tutorial_used); diff --git a/src/g_input.c b/src/g_input.c index ecce4d83c..441aa8dda 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -57,13 +57,13 @@ const INT32 gcl_tutorial_check[num_gcl_tutorial_check] = { const INT32 gcl_tutorial_used[num_gcl_tutorial_used] = { gc_forward, gc_backward, gc_strafeleft, gc_straferight, gc_turnleft, gc_turnright, - gc_jump, gc_use + gc_jump, gc_spin }; const INT32 gcl_tutorial_full[num_gcl_tutorial_full] = { gc_forward, gc_backward, gc_strafeleft, gc_straferight, gc_lookup, gc_lookdown, gc_turnleft, gc_turnright, gc_centerview, - gc_jump, gc_use, + gc_jump, gc_spin, gc_fire, gc_firenormal }; @@ -82,10 +82,10 @@ const INT32 gcl_movement_camera[num_gcl_movement_camera] = { const INT32 gcl_jump[num_gcl_jump] = { gc_jump }; -const INT32 gcl_use[num_gcl_use] = { gc_use }; +const INT32 gcl_spin[num_gcl_spin] = { gc_spin }; -const INT32 gcl_jump_use[num_gcl_jump_use] = { - gc_jump, gc_use +const INT32 gcl_jump_spin[num_gcl_jump_spin] = { + gc_jump, gc_spin }; typedef struct @@ -692,7 +692,7 @@ void G_DefineDefaultControls(void) gamecontroldefault[gcs_fps][gc_turnright ][0] = KEY_RIGHTARROW; gamecontroldefault[gcs_fps][gc_centerview ][0] = KEY_END; gamecontroldefault[gcs_fps][gc_jump ][0] = KEY_SPACE; - gamecontroldefault[gcs_fps][gc_use ][0] = KEY_LSHIFT; + gamecontroldefault[gcs_fps][gc_spin ][0] = KEY_LSHIFT; gamecontroldefault[gcs_fps][gc_fire ][0] = KEY_RCTRL; gamecontroldefault[gcs_fps][gc_fire ][1] = KEY_MOUSE1+0; gamecontroldefault[gcs_fps][gc_firenormal ][0] = 'c'; @@ -708,7 +708,7 @@ void G_DefineDefaultControls(void) gamecontroldefault[gcs_platform][gc_turnright ][0] = KEY_RIGHTARROW; gamecontroldefault[gcs_platform][gc_centerview ][0] = KEY_END; gamecontroldefault[gcs_platform][gc_jump ][0] = KEY_SPACE; - gamecontroldefault[gcs_platform][gc_use ][0] = KEY_LSHIFT; + gamecontroldefault[gcs_platform][gc_spin ][0] = KEY_LSHIFT; gamecontroldefault[gcs_platform][gc_fire ][0] = 's'; gamecontroldefault[gcs_platform][gc_fire ][1] = KEY_MOUSE1+0; gamecontroldefault[gcs_platform][gc_firenormal ][0] = 'w'; @@ -743,7 +743,7 @@ void G_DefineDefaultControls(void) gamecontroldefault[i][gc_weaponnext ][1] = KEY_JOY1+1; // B gamecontroldefault[i][gc_weaponprev ][1] = KEY_JOY1+2; // X gamecontroldefault[i][gc_tossflag ][1] = KEY_JOY1+0; // A - gamecontroldefault[i][gc_use ][1] = KEY_JOY1+4; // LB + gamecontroldefault[i][gc_spin ][1] = KEY_JOY1+4; // LB gamecontroldefault[i][gc_camtoggle ][1] = KEY_HAT1+0; // D-Pad Up gamecontroldefault[i][gc_camreset ][1] = KEY_JOY1+3; // Y gamecontroldefault[i][gc_centerview ][1] = KEY_JOY1+9; // Right Stick @@ -758,7 +758,7 @@ void G_DefineDefaultControls(void) gamecontrolbisdefault[i][gc_weaponnext][0] = KEY_2JOY1+1; // B gamecontrolbisdefault[i][gc_weaponprev][0] = KEY_2JOY1+2; // X gamecontrolbisdefault[i][gc_tossflag ][0] = KEY_2JOY1+0; // A - gamecontrolbisdefault[i][gc_use ][0] = KEY_2JOY1+4; // LB + gamecontrolbisdefault[i][gc_spin ][0] = KEY_2JOY1+4; // LB gamecontrolbisdefault[i][gc_camreset ][0] = KEY_2JOY1+3; // Y gamecontrolbisdefault[i][gc_centerview][0] = KEY_2JOY1+9; // Right Stick gamecontrolbisdefault[i][gc_jump ][0] = KEY_2JOY1+5; // RB @@ -890,7 +890,7 @@ static INT32 G_FilterKeyByVersion(INT32 numctrl, INT32 keyidx, INT32 player, INT if (GETMAJOREXECVERSION(cv_execversion.value) < 27 && ( // v2.1.22 numctrl == gc_weaponnext || numctrl == gc_weaponprev || numctrl == gc_tossflag || - numctrl == gc_use || numctrl == gc_camreset || numctrl == gc_jump || + numctrl == gc_spin || numctrl == gc_camreset || numctrl == gc_jump || numctrl == gc_pause || numctrl == gc_systemmenu || numctrl == gc_camtoggle || numctrl == gc_screenshot || numctrl == gc_talkkey || numctrl == gc_scores || numctrl == gc_centerview diff --git a/src/g_input.h b/src/g_input.h index a7484c7ad..ce38f6ba9 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -80,7 +80,7 @@ typedef enum gc_fire, gc_firenormal, gc_tossflag, - gc_use, + gc_spin, gc_camtoggle, gc_camreset, gc_lookup, @@ -141,8 +141,8 @@ extern INT32 gamecontrolbisdefault[num_gamecontrolschemes][num_gamecontrols][2]; #define num_gcl_camera 2 #define num_gcl_movement_camera 6 #define num_gcl_jump 1 -#define num_gcl_use 1 -#define num_gcl_jump_use 2 +#define num_gcl_spin 1 +#define num_gcl_jump_spin 2 extern const INT32 gcl_tutorial_check[num_gcl_tutorial_check]; extern const INT32 gcl_tutorial_used[num_gcl_tutorial_used]; @@ -151,8 +151,8 @@ extern const INT32 gcl_movement[num_gcl_movement]; extern const INT32 gcl_camera[num_gcl_camera]; extern const INT32 gcl_movement_camera[num_gcl_movement_camera]; extern const INT32 gcl_jump[num_gcl_jump]; -extern const INT32 gcl_use[num_gcl_use]; -extern const INT32 gcl_jump_use[num_gcl_jump_use]; +extern const INT32 gcl_spin[num_gcl_spin]; +extern const INT32 gcl_jump_spin[num_gcl_jump_spin]; // peace to my little coder fingers! // check a gamecontrol being active or not From 50fd9de9d8ec86360d9c417c3561e19eb47ed534 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 6 Jun 2020 05:57:32 -0500 Subject: [PATCH 030/136] Oops! Missed a couple... --- src/g_game.c | 2 +- src/m_menu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index ab0bdd265..c2649d60f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1340,7 +1340,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // use with any button/key axis = PlayerJoyAxis(ssplayer, AXISSPIN); - if (PLAYERINPUTDOWN(ssplayer, gc_use) || (usejoystick && axis > 0)) + if (PLAYERINPUTDOWN(ssplayer, gc_spin) || (usejoystick && axis > 0)) cmd->buttons |= BT_SPIN; // Centerview can be a toggle in simple mode! diff --git a/src/m_menu.c b/src/m_menu.c index 131f72c76..70de842b2 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1116,7 +1116,7 @@ static menuitem_t OP_ChangeControlsMenu[] = {IT_CALL | IT_STRING2, NULL, "Move Left", M_ChangeControl, gc_strafeleft }, {IT_CALL | IT_STRING2, NULL, "Move Right", M_ChangeControl, gc_straferight }, {IT_CALL | IT_STRING2, NULL, "Jump", M_ChangeControl, gc_jump }, - {IT_CALL | IT_STRING2, NULL, "Spin", M_ChangeControl, gc_use }, + {IT_CALL | IT_STRING2, NULL, "Spin", M_ChangeControl, gc_spin }, {IT_HEADER, NULL, "Camera", NULL, 0}, {IT_SPACE, NULL, NULL, NULL, 0}, // padding {IT_CALL | IT_STRING2, NULL, "Look Up", M_ChangeControl, gc_lookup }, From b3198ed063dec7a31bfd542b1628d4134084474b Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 6 Jun 2020 06:16:28 -0500 Subject: [PATCH 031/136] Replace PF_USEDOWN with PF_SPINDOWN --- src/d_player.h | 2 +- src/dehacked.c | 2 +- src/g_game.c | 2 +- src/p_user.c | 36 ++++++++++++++++++------------------ 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 26086a331..bd33ebae5 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -115,7 +115,7 @@ typedef enum // True if button down last tic. PF_ATTACKDOWN = 1<<7, - PF_USEDOWN = 1<<8, + PF_SPINDOWN = 1<<8, PF_JUMPDOWN = 1<<9, PF_WPNDOWN = 1<<10, diff --git a/src/dehacked.c b/src/dehacked.c index a174418c0..31ba91bd5 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -9049,7 +9049,7 @@ static const char *const PLAYERFLAG_LIST[] = { // True if button down last tic. "ATTACKDOWN", - "USEDOWN", + "SPINDOWN", "JUMPDOWN", "WPNDOWN", diff --git a/src/g_game.c b/src/g_game.c index c2649d60f..cd2b2fad0 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2570,7 +2570,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->spheres = spheres; // Don't do anything immediately - p->pflags |= PF_USEDOWN; + p->pflags |= PF_SPINDOWN; p->pflags |= PF_ATTACKDOWN; p->pflags |= PF_JUMPDOWN; diff --git a/src/p_user.c b/src/p_user.c index a4aff1499..18313f283 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -665,7 +665,7 @@ static void P_DeNightserizePlayer(player_t *player) player->powers[pw_carry] = CR_NIGHTSFALL; player->powers[pw_underwater] = 0; - player->pflags &= ~(PF_USEDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SPINNING|PF_DRILLING|PF_TRANSFERTOCLOSEST); + player->pflags &= ~(PF_SPINDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SPINNING|PF_DRILLING|PF_TRANSFERTOCLOSEST); player->secondjump = 0; player->homing = 0; player->climbing = 0; @@ -795,7 +795,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) } } - player->pflags &= ~(PF_USEDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SHIELDABILITY|PF_SPINNING|PF_DRILLING); + player->pflags &= ~(PF_SPINDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SHIELDABILITY|PF_SPINNING|PF_DRILLING); player->homing = 0; player->mo->fuse = 0; player->speed = 0; @@ -4592,12 +4592,12 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) case CA2_SPINDASH: // Spinning and Spindashing // Start revving if ((cmd->buttons & BT_SPIN) && (player->speed < FixedMul(5<mo->scale) || player->mo->state - states == S_PLAY_GLIDE_LANDING) - && !player->mo->momz && onground && !(player->pflags & (PF_USEDOWN|PF_SPINNING)) + && !player->mo->momz && onground && !(player->pflags & (PF_SPINDOWN|PF_SPINNING)) && canstand) { player->mo->momx = player->cmomx; player->mo->momy = player->cmomy; - player->pflags |= (PF_USEDOWN|PF_STARTDASH|PF_SPINNING); + player->pflags |= (PF_SPINDOWN|PF_STARTDASH|PF_SPINNING); player->dashspeed = player->mindash; P_SetPlayerMobjState(player->mo, S_PLAY_SPINDASH); if (!player->spectator) @@ -4639,16 +4639,16 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) // AKA Just go into a spin on the ground, you idiot. ;) else if ((cmd->buttons & BT_SPIN || ((twodlevel || (player->mo->flags2 & MF2_TWOD)) && cmd->forwardmove < -20)) && !player->climbing && !player->mo->momz && onground && (player->speed > FixedMul(5<mo->scale) - || !canstand) && !(player->pflags & (PF_USEDOWN|PF_SPINNING))) + || !canstand) && !(player->pflags & (PF_SPINDOWN|PF_SPINNING))) { - player->pflags |= (PF_USEDOWN|PF_SPINNING); + player->pflags |= (PF_SPINDOWN|PF_SPINNING); P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); if (!player->spectator) S_StartSound(player->mo, sfx_spin); } else // Catapult the player from a spindash rev! - if (onground && !(player->pflags & PF_USEDOWN) && (player->pflags & PF_STARTDASH) && (player->pflags & PF_SPINNING)) + if (onground && !(player->pflags & PF_SPINDOWN) && (player->pflags & PF_STARTDASH) && (player->pflags & PF_SPINNING)) { player->pflags &= ~PF_STARTDASH; if (player->powers[pw_carry] == CR_BRAKGOOP) @@ -4690,7 +4690,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) P_SetTarget(&visual->target, lockon); } } - if ((cmd->buttons & BT_SPIN) && !(player->pflags & PF_USEDOWN)) + if ((cmd->buttons & BT_SPIN) && !(player->pflags & PF_SPINDOWN)) { mobj_t *bullet; @@ -4719,7 +4719,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) player->mo->momx >>= 1; player->mo->momy >>= 1; - player->pflags |= PF_USEDOWN; + player->pflags |= PF_SPINDOWN; P_SetWeaponDelay(player, TICRATE/2); } } @@ -4727,7 +4727,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) break; case CA2_MELEE: // Melee attack if (player->panim != PA_ABILITY2 && (cmd->buttons & BT_SPIN) - && !player->mo->momz && onground && !(player->pflags & PF_USEDOWN) + && !player->mo->momz && onground && !(player->pflags & PF_SPINDOWN) && canstand) { P_ResetPlayer(player); @@ -4767,7 +4767,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) P_SetPlayerMobjState(player->mo, S_PLAY_MELEE); S_StartSound(player->mo, sfx_s3k42); } - player->pflags |= PF_USEDOWN; + player->pflags |= PF_SPINDOWN; } break; } @@ -5040,7 +5040,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) ; else if (player->pflags & (PF_GLIDING|PF_SLIDING|PF_SHIELDABILITY)) // If the player has used an ability previously ; - else if ((player->powers[pw_shield] & SH_NOSTACK) && !player->powers[pw_super] && !(player->pflags & PF_USEDOWN) + else if ((player->powers[pw_shield] & SH_NOSTACK) && !player->powers[pw_super] && !(player->pflags & PF_SPINDOWN) && ((!(player->pflags & PF_THOKKED) || ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP && player->secondjump == UINT8_MAX)))) // thokked is optional if you're bubblewrapped { if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) @@ -5142,7 +5142,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) } else if ((cmd->buttons & BT_SPIN)) { - if (!(player->pflags & PF_USEDOWN) && P_SuperReady(player)) + if (!(player->pflags & PF_SPINDOWN) && P_SuperReady(player)) { // If you can turn super and aren't already, // and you don't have a shield, do it! @@ -5172,7 +5172,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) } break; case CA_TELEKINESIS: - if (!(player->pflags & (PF_THOKKED|PF_USEDOWN)) || (player->charflags & SF_MULTIABILITY)) + if (!(player->pflags & (PF_THOKKED|PF_SPINDOWN)) || (player->charflags & SF_MULTIABILITY)) { P_Telekinesis(player, -FixedMul(player->actionspd, player->mo->scale), // -ve thrust (pulling towards player) @@ -5180,7 +5180,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) } break; case CA_TWINSPIN: - if ((player->charability2 == CA2_MELEE) && (!(player->pflags & (PF_THOKKED|PF_USEDOWN)) || player->charflags & SF_MULTIABILITY)) + if ((player->charability2 == CA2_MELEE) && (!(player->pflags & (PF_THOKKED|PF_SPINDOWN)) || player->charflags & SF_MULTIABILITY)) P_DoTwinSpin(player); break; default: @@ -8830,7 +8830,7 @@ static void P_DoRopeHang(player_t *player) if (player->cmd.buttons & BT_SPIN && !(player->pflags & PF_STASIS)) // Drop off of the rope { - player->pflags |= (P_GetJumpFlags(player)|PF_USEDOWN); + player->pflags |= (P_GetJumpFlags(player)|PF_SPINDOWN); P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); P_SetTarget(&player->mo->tracer, NULL); @@ -12065,9 +12065,9 @@ void P_PlayerThink(player_t *player) if (player->powers[pw_carry] != CR_NIGHTSMODE) { if (cmd->buttons & BT_SPIN) - player->pflags |= PF_USEDOWN; + player->pflags |= PF_SPINDOWN; else - player->pflags &= ~PF_USEDOWN; + player->pflags &= ~PF_SPINDOWN; } // IF PLAYER NOT HERE THEN FLASH END IF From 1dd754d13941e49083969862025aa9e8b45ce2b3 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 6 Jun 2020 07:30:32 -0500 Subject: [PATCH 032/136] The only "use" that I had to change to "spin" after a manual half hour search --- src/g_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_input.c b/src/g_input.c index 441aa8dda..55d91a8f5 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -583,7 +583,7 @@ static const char *gamecontrolname[num_gamecontrols] = "fire", "firenormal", "tossflag", - "use", + "spin", "camtoggle", "camreset", "lookup", From a59c03fefea76cfb93a19d57798e566bf1401f62 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 6 Jun 2020 07:50:58 -0500 Subject: [PATCH 033/136] Ok fiiineee, I'll support 2.2 --- src/dehacked.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/dehacked.c b/src/dehacked.c index 31ba91bd5..200be071c 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -10810,6 +10810,12 @@ static inline int lib_getenum(lua_State *L) lua_pushinteger(L, (lua_Integer)PF_FULLSTASIS); return 1; } + else if (fastcmp(p, "USEDOWN")) // Remove case when 2.3 nears release... + { + LUA_Deprecated(L, "PF_USEDOWN", "PF_SPINDOWN"); + lua_pushinteger(L, (lua_Integer)PF_SPINDOWN); + return 1; + } if (mathlib) return luaL_error(L, "playerflag '%s' could not be found.\n", word); return 0; } @@ -11076,6 +11082,13 @@ static inline int lib_getenum(lua_State *L) return 0; } + if (fastcmp(word, "BT_USE")) // Remove case when 2.3 nears release... + { + LUA_Deprecated(L, "BT_USE", "BT_SPIN"); + lua_pushinteger(L, (lua_Integer)BT_SPIN); + return 1; + } + for (i = 0; INT_CONST[i].n; i++) if (fastcmp(word,INT_CONST[i].n)) { lua_pushinteger(L, INT_CONST[i].v); From 7c3a565709fdf1f633cb05285b95a068f3522dc7 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 6 Jun 2020 12:21:52 -0500 Subject: [PATCH 034/136] Use kinda counter-intuitive ternary operator to hack in a check for "use" for 2.2 compatibility --- src/g_input.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/g_input.c b/src/g_input.c index 55d91a8f5..1cf6990c9 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -996,7 +996,9 @@ static void setcontrol(INT32 (*gc)[2]) INT32 player = ((void*)gc == (void*)&gamecontrolbis ? 1 : 0); boolean nestedoverride = false; - namectrl = COM_Argv(1); + // Update me for 2.3 + namectrl = (stricmp(COM_Argv(1), "use")) ? COM_Argv(1) : "spin"; + for (numctrl = 0; numctrl < num_gamecontrols && stricmp(namectrl, gamecontrolname[numctrl]); numctrl++) ; From 1f89e1cf4269c98f664aa2392e22c094628652d0 Mon Sep 17 00:00:00 2001 From: Hannu Hanhi Date: Sun, 12 Jul 2020 18:04:56 +0300 Subject: [PATCH 035/136] Adjust model uvs when used sprite texture changes --- src/hardware/hw_md2.c | 42 +++++++++++++++++++++++++++++++++-------- src/hardware/hw_model.c | 8 ++++++++ src/hardware/hw_model.h | 10 ++++++++++ 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 80c01f98c..05a70f6e8 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1177,6 +1177,7 @@ static UINT8 HWR_GetModelSprite2(md2_t *md2, skin_t *skin, UINT8 spr2, player_t return spr2; } +// Adjust texture coords of model to fit into a patch's max_s and max_t static void adjustTextureCoords(model_t *model, GLPatch_t *gpatch) { int i; @@ -1185,24 +1186,38 @@ static void adjustTextureCoords(model_t *model, GLPatch_t *gpatch) int j; mesh_t *mesh = &model->meshes[i]; int numVertices; - float *uvPtr = mesh->uvs; + float *uvReadPtr = mesh->originaluvs; + float *uvWritePtr; // i dont know if this is actually possible, just logical conclusion of structure in CreateModelVBOs - if (!mesh->frames && !mesh->tinyframes) return; + if (!mesh->frames && !mesh->tinyframes) continue; if (mesh->frames) // again CreateModelVBO and CreateModelVBOTiny iterate like this so I'm gonna do that too numVertices = mesh->numTriangles * 3; else numVertices = mesh->numVertices; + // if originaluvs points to uvs, we need to allocate new memory for adjusted uvs + // the old uvs are kept around for use in possible readjustments + if (mesh->uvs == mesh->originaluvs) + { + CONS_Printf("Debug: allocating memory for adjusted uvs\n"); + mesh->uvs = Z_Malloc(numVertices * 2 * sizeof(float), PU_STATIC, NULL); + } + + uvWritePtr = mesh->uvs; + // fix uvs (texture coordinates) to take into account that the actual texture // has empty space added until the next power of two for (j = 0; j < numVertices; j++) { - *uvPtr++ *= gpatch->max_s; - *uvPtr++ *= gpatch->max_t; + *uvWritePtr++ = *uvReadPtr++ * gpatch->max_s; + *uvWritePtr++ = *uvReadPtr++ * gpatch->max_t; } } + // Save the values we adjusted the uvs for + model->max_s = gpatch->max_s; + model->max_t = gpatch->max_t; } // @@ -1226,6 +1241,10 @@ boolean HWR_DrawModel(gr_vissprite_t *spr) if (spr->precip) return false; + // Lactozilla: Disallow certain models from rendering + if (!HWR_AllowModel(spr->mobj)) + return false; + memset(&p, 0x00, sizeof(FTransform)); // MD2 colormap fix @@ -1344,10 +1363,6 @@ boolean HWR_DrawModel(gr_vissprite_t *spr) } } - // Lactozilla: Disallow certain models from rendering - if (!HWR_AllowModel(spr->mobj)) - return false; - //HWD.pfnSetBlend(blend); // This seems to actually break translucency? finalscale = md2->scale; //Hurdler: arf, I don't like that implementation at all... too much crappy @@ -1391,6 +1406,17 @@ boolean HWR_DrawModel(gr_vissprite_t *spr) { // Sprite gpatch = spr->gpatch; //W_CachePatchNum(spr->patchlumpnum, PU_CACHE); + // Check if sprite dimensions are different from previously used sprite. + // If so, uvs need to be readjusted. + if (gpatch->max_s != md2->model->max_s || gpatch->max_t != md2->model->max_t) + { + CONS_Printf("Debug: Readjusting uvs!\n"); + adjustTextureCoords(md2->model, gpatch); + // The vbo(s) are now wrong, so recreate them. + // If this turns out to be slow, then could try updating the vbos instead of + // deleting and creating new ones. + HWD.pfnCreateModelVBOs(md2->model); + } HWR_GetMappedPatch(gpatch, spr->colormap); } diff --git a/src/hardware/hw_model.c b/src/hardware/hw_model.c index ac73f8aca..cb22a2ec4 100644 --- a/src/hardware/hw_model.c +++ b/src/hardware/hw_model.c @@ -221,6 +221,14 @@ model_t *LoadModel(const char *filename, int ztag) material->shininess = 25.0f; } + // Set originaluvs to point to uvs + for (i = 0; i < model->numMeshes; i++) + model->meshes[i].originaluvs = model->meshes[i].uvs; + + // Set initial values to max_s and max_t + model->max_s = 1.0; + model->max_t = 1.0; + return model; } diff --git a/src/hardware/hw_model.h b/src/hardware/hw_model.h index 2a5240bde..0b1834b52 100644 --- a/src/hardware/hw_model.h +++ b/src/hardware/hw_model.h @@ -59,6 +59,11 @@ typedef struct mesh_s int numTriangles; float *uvs; + // if uv adjustment is needed, uvs is changed to point to adjusted ones and + // this one retains the originals + // note: this member has been added with the assumption that models are never freed. + // (UnloadModel is called by nobody at the time of writing.) + float *originaluvs; float *lightuvs; int numFrames; @@ -99,6 +104,11 @@ typedef struct model_s char *framenames; boolean interpolate[256]; modelspr2frames_t *spr2frames; + + // the max_s and max_t values that the uvs are currently adjusted to + // (if a sprite is used as a texture) + float max_s; + float max_t; } model_t; extern int numModels; From edc479fd1396374700790e236d2ca8517caed1df Mon Sep 17 00:00:00 2001 From: Hannu Hanhi Date: Thu, 16 Jul 2020 22:11:36 +0300 Subject: [PATCH 036/136] More work on sprite-texture model uv adjustment --- src/hardware/hw_md2.c | 17 +++------ src/hardware/hw_model.h | 4 ++ src/hardware/r_opengl/r_opengl.c | 65 ++++++++++++++++++++++---------- 3 files changed, 55 insertions(+), 31 deletions(-) diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index abb5267a9..05afff37f 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1200,10 +1200,7 @@ static void adjustTextureCoords(model_t *model, GLPatch_t *gpatch) // if originaluvs points to uvs, we need to allocate new memory for adjusted uvs // the old uvs are kept around for use in possible readjustments if (mesh->uvs == mesh->originaluvs) - { - CONS_Printf("Debug: allocating memory for adjusted uvs\n"); mesh->uvs = Z_Malloc(numVertices * 2 * sizeof(float), PU_STATIC, NULL); - } uvWritePtr = mesh->uvs; @@ -1349,10 +1346,13 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) if (md2->model) { md2_printModelInfo(md2->model); - // if model uses sprite patch as texture, then + // If model uses sprite patch as texture, then // adjust texture coordinates to take power of two textures into account if (!gpatch || !gpatch->mipmap->format) adjustTextureCoords(md2->model, spr->gpatch); + // note down the max_s and max_t that end up in the VBO + md2->model->vbo_max_s = md2->model->max_s; + md2->model->vbo_max_t = md2->model->max_t; HWD.pfnCreateModelVBOs(md2->model); } else @@ -1408,15 +1408,10 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) gpatch = spr->gpatch; //W_CachePatchNum(spr->patchlumpnum, PU_CACHE); // Check if sprite dimensions are different from previously used sprite. // If so, uvs need to be readjusted. + // Comparing floats with the != operator here should be okay because they + // are just copies of glpatches' max_s and max_t values. if (gpatch->max_s != md2->model->max_s || gpatch->max_t != md2->model->max_t) - { - CONS_Printf("Debug: Readjusting uvs!\n"); adjustTextureCoords(md2->model, gpatch); - // The vbo(s) are now wrong, so recreate them. - // If this turns out to be slow, then could try updating the vbos instead of - // deleting and creating new ones. - HWD.pfnCreateModelVBOs(md2->model); - } HWR_GetMappedPatch(gpatch, spr->colormap); } diff --git a/src/hardware/hw_model.h b/src/hardware/hw_model.h index 0b1834b52..6b39eb24d 100644 --- a/src/hardware/hw_model.h +++ b/src/hardware/hw_model.h @@ -109,6 +109,10 @@ typedef struct model_s // (if a sprite is used as a texture) float max_s; float max_t; + // These are the values that the uvs in the VBO have been adjusted to. + // If they are not same as max_s and max_t, then the VBO won't be used. + float vbo_max_s; + float vbo_max_t; } model_t; extern int numModels; diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 08d688e1d..a837b5e03 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -2626,6 +2626,8 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 boolean useTinyFrames; + boolean useVBO = true; + int i; // Because otherwise, scaling the screen negatively vertically breaks the lighting @@ -2766,6 +2768,13 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 if (useTinyFrames) pglScalef(1 / 64.0f, 1 / 64.0f, 1 / 64.0f); + // Don't use the VBO if it does not have the correct texture coordinates. + // (Can happen when model uses a sprite as a texture and the sprite changes) + // Comparing floats with the != operator here should be okay because they + // are just copies of glpatches' max_s and max_t values. + if (model->vbo_max_s != model->max_s || model->vbo_max_t != model->max_t) + useVBO = false; + pglEnableClientState(GL_NORMAL_ARRAY); for (i = 0; i < model->numMeshes; i++) @@ -2782,13 +2791,24 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 if (!nextframe || fpclassify(pol) == FP_ZERO) { - pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); - pglVertexPointer(3, GL_SHORT, sizeof(vbotiny_t), BUFFER_OFFSET(0)); - pglNormalPointer(GL_BYTE, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short)*3)); - pglTexCoordPointer(2, GL_FLOAT, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short) * 3 + sizeof(char) * 6)); + if (useVBO) + { + pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); + pglVertexPointer(3, GL_SHORT, sizeof(vbotiny_t), BUFFER_OFFSET(0)); + pglNormalPointer(GL_BYTE, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short)*3)); + pglTexCoordPointer(2, GL_FLOAT, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short) * 3 + sizeof(char) * 6)); - pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); - pglBindBuffer(GL_ARRAY_BUFFER, 0); + pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + pglBindBuffer(GL_ARRAY_BUFFER, 0); + } + else + { + //CONS_Printf("non-vbo tinyframe\n"); + pglVertexPointer(3, GL_SHORT, 0, frame->vertices); + pglNormalPointer(GL_BYTE, 0, frame->normals); + pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); + pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + } } else { @@ -2824,21 +2844,26 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 if (!nextframe || fpclassify(pol) == FP_ZERO) { - // Zoom! Take advantage of just shoving the entire arrays to the GPU. -/* pglVertexPointer(3, GL_FLOAT, 0, frame->vertices); - pglNormalPointer(GL_FLOAT, 0, frame->normals); - pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); - pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3);*/ + if (useVBO) + { + pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); + pglVertexPointer(3, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(0)); + pglNormalPointer(GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 3)); + pglTexCoordPointer(2, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 6)); - pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); - pglVertexPointer(3, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(0)); - pglNormalPointer(GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 3)); - pglTexCoordPointer(2, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 6)); - - pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3); - // No tinyframes, no mesh indices - //pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); - pglBindBuffer(GL_ARRAY_BUFFER, 0); + pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3); + // No tinyframes, no mesh indices + //pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + pglBindBuffer(GL_ARRAY_BUFFER, 0); + } + else + { + //CONS_Printf("non-vbo frame\n"); + pglVertexPointer(3, GL_FLOAT, 0, frame->vertices); + pglNormalPointer(GL_FLOAT, 0, frame->normals); + pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); + pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3); + } } else { From 85b2ed594f6aef3435c63913dff2206184edce40 Mon Sep 17 00:00:00 2001 From: Hannu Hanhi Date: Thu, 16 Jul 2020 22:39:44 +0300 Subject: [PATCH 037/136] More work on sprite-texture model uv adjustment 2 --- src/hardware/hw_model.c | 3 ++- src/hardware/r_opengl/r_opengl.c | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hardware/hw_model.c b/src/hardware/hw_model.c index cb22a2ec4..4ed03744b 100644 --- a/src/hardware/hw_model.c +++ b/src/hardware/hw_model.c @@ -225,9 +225,10 @@ model_t *LoadModel(const char *filename, int ztag) for (i = 0; i < model->numMeshes; i++) model->meshes[i].originaluvs = model->meshes[i].uvs; - // Set initial values to max_s and max_t model->max_s = 1.0; model->max_t = 1.0; + model->vbo_max_s = 1.0; + model->vbo_max_t = 1.0; return model; } diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index a837b5e03..195f3d6f7 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -2803,7 +2803,6 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 } else { - //CONS_Printf("non-vbo tinyframe\n"); pglVertexPointer(3, GL_SHORT, 0, frame->vertices); pglNormalPointer(GL_BYTE, 0, frame->normals); pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); @@ -2858,7 +2857,6 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 } else { - //CONS_Printf("non-vbo frame\n"); pglVertexPointer(3, GL_FLOAT, 0, frame->vertices); pglNormalPointer(GL_FLOAT, 0, frame->normals); pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); From ed068a76641607f6f26f191be21adacad7033a91 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 8 Aug 2020 15:36:01 -0300 Subject: [PATCH 038/136] Fix buffer overrun in I_ClipboardPaste --- src/sdl/i_system.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index b24ae2814..516c8f2af 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -2726,10 +2726,10 @@ const char *I_ClipboardPaste(void) if (!SDL_HasClipboardText()) return NULL; + clipboard_contents = SDL_GetClipboardText(); - memcpy(clipboard_modified, clipboard_contents, 255); + strlcpy(clipboard_modified, clipboard_contents, 256); SDL_free(clipboard_contents); - clipboard_modified[255] = 0; while (*i) { From 7382602e3c098382b32b5e99586cb5909a76d4a7 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 26 Apr 2020 20:46:33 -0700 Subject: [PATCH 039/136] Match MAXSERVERLIST to MAXNETNODES, increase MAXNETNODES Each server in the server list requires a node. It's also MAXNETNODES-1 because the first node is yourself. (cherry picked from commit bad06eba17b804f152a174938656ecc73c2f072e) --- src/d_clisrv.h | 3 ++- src/d_net.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.h b/src/d_clisrv.h index d537984df..60967f6cf 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -14,6 +14,7 @@ #define __D_CLISRV__ #include "d_ticcmd.h" +#include "d_net.h" #include "d_netcmd.h" #include "d_net.h" #include "tables.h" @@ -483,7 +484,7 @@ typedef struct #pragma pack() #endif -#define MAXSERVERLIST 64 // Depends only on the display +#define MAXSERVERLIST (MAXNETNODES-1) typedef struct { SINT8 node; diff --git a/src/d_net.h b/src/d_net.h index ed4f66284..4ada32344 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -19,7 +19,7 @@ #define __D_NET__ // Max computers in a game -#define MAXNETNODES (MAXPLAYERS+4) +#define MAXNETNODES 64 #define BROADCASTADDR MAXNETNODES #define MAXSPLITSCREENPLAYERS 2 // Max number of players on a single computer //#define NETSPLITSCREEN // Kart's splitscreen netgame feature From ab2bc630026f626772a583a495a056a99393ad19 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sun, 9 Aug 2020 11:03:02 -0500 Subject: [PATCH 040/136] Fix string drawing functions so they account for V_NOSCALEPATCH effectively multiplying the width of the screen --- src/v_video.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/v_video.c b/src/v_video.c index b88c4838b..888fea39e 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -2120,6 +2120,9 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + switch (option & V_SPACINGMASK) { case V_MONOSPACE: @@ -2233,6 +2236,9 @@ void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string) scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + charflags = (option & V_CHARCOLORMASK); switch (option & V_SPACINGMASK) @@ -2348,6 +2354,9 @@ void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string) scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + charflags = (option & V_CHARCOLORMASK); switch (option & V_SPACINGMASK) @@ -2483,6 +2492,9 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + charflags = (option & V_CHARCOLORMASK); switch (option & V_SPACINGMASK) @@ -2596,6 +2608,9 @@ void V_DrawSmallStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *st scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + charflags = (option & V_CHARCOLORMASK); switch (option & V_SPACINGMASK) @@ -2710,6 +2725,9 @@ void V_DrawThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *str scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + charflags = (option & V_CHARCOLORMASK); switch (option & V_SPACINGMASK) @@ -2824,6 +2842,9 @@ void V_DrawSmallThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + charflags = (option & V_CHARCOLORMASK); switch (option & V_SPACINGMASK) @@ -2917,7 +2938,7 @@ void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num) INT32 w = SHORT(tallnum[0]->width); boolean neg; - if (flags & V_NOSCALESTART) + if (flags & (V_NOSCALESTART|V_NOSCALEPATCH)) w *= vid.dupx; if ((neg = num < 0)) @@ -2942,7 +2963,7 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits) { INT32 w = SHORT(tallnum[0]->width); - if (flags & V_NOSCALESTART) + if (flags & (V_NOSCALESTART|V_NOSCALEPATCH)) w *= vid.dupx; if (num < 0) @@ -2995,6 +3016,9 @@ void V_DrawCreditString(fixed_t x, fixed_t y, INT32 option, const char *string) else dupx = dupy = 1; + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + for (;;) { c = *ch++; @@ -3052,6 +3076,9 @@ static void V_DrawNameTagLine(INT32 x, INT32 y, INT32 option, fixed_t scale, UIN scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + for (;;ch++) { if (!*ch) @@ -3267,6 +3294,9 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string) scrwidth -= left; } + if (option & V_NOSCALEPATCH) + scrwidth *= vid.dupx; + for (;;ch++) { if (!*ch) @@ -3402,8 +3432,8 @@ INT32 V_StringWidth(const char *string, INT32 option) w += (charwidth ? charwidth : SHORT(hu_font[c]->width)); } - if (option & V_NOSCALESTART) - w *= vid.dupx; + if (option & (V_NOSCALESTART|V_NOSCALEPATCH)) + w *= vid.dupx; return w; } From 1e1a57ba81e6b792cb3b1d952c08e15ddc87d56b Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 9 Aug 2020 18:26:13 -0700 Subject: [PATCH 041/136] Bump MAXNETNODES to 127 (cherry picked from commit 3cb1a53b3f7d992dd4ceb0890af65b6b21bf11e7) --- src/d_net.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/d_net.h b/src/d_net.h index 4ada32344..ea6b5d4d9 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -19,7 +19,9 @@ #define __D_NET__ // Max computers in a game -#define MAXNETNODES 64 +// 127 is probably as high as this can go, because +// SINT8 is used for nodes sometimes >:( +#define MAXNETNODES 127 #define BROADCASTADDR MAXNETNODES #define MAXSPLITSCREENPLAYERS 2 // Max number of players on a single computer //#define NETSPLITSCREEN // Kart's splitscreen netgame feature From 79469d401c6f91cd7a5d2df012086fd3bde0a31d Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sun, 12 Apr 2020 17:56:12 -0400 Subject: [PATCH 042/136] Fix cmake using wrong libs if internal libs is turned on Use '\0' instead, just in case (cherry picked from commit 47d70e1ae67649f788d6fe7201d683c45e574436) --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d3fde6c2e..dc7e026b9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -452,9 +452,9 @@ if(${SRB2_CONFIG_HAVE_CURL}) set(CURL_FOUND ON) set(CURL_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/curl) if(${SRB2_SYSTEM_BITS} EQUAL 64) - set(CURL_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/curl/lib32 -lcurl") - else() # 32-bit set(CURL_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/curl/lib64 -lcurl") + else() # 32-bit + set(CURL_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/curl/lib32 -lcurl") endif() else() find_package(CURL) From b771ff2426212f11b5acdd01c4970d8b091f4a5c Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 9 Aug 2020 23:32:43 -0700 Subject: [PATCH 043/136] Add http-mserv to fuck (cherry picked from commit 8e493bddc3ce4c788b19a745b1c27d086981d874) --- src/CMakeLists.txt | 2 ++ src/sdl/Srb2SDL-vc10.vcxproj | 2 ++ src/sdl/Srb2SDL-vc10.vcxproj.filters | 6 ++++ src/sdl/Srb2SDL-vc9.vcproj | 44 ++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dc7e026b9..e43cafcca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,6 +36,7 @@ set(SRB2_CORE_SOURCES m_random.c md5.c mserv.c + http-mserv.c s_sound.c screen.c sounds.c @@ -101,6 +102,7 @@ set(SRB2_CORE_HEADERS m_swap.h md5.h mserv.h + http-mserv.h p5prof.h s_sound.h screen.h diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index 5592de86b..c6cef56de 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -251,6 +251,7 @@ + @@ -405,6 +406,7 @@ + diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index db1aa123f..04a1b5fa5 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -297,6 +297,9 @@ I_Interface + + I_Interface + LUA @@ -684,6 +687,9 @@ I_Interface + + I_Interface + LUA diff --git a/src/sdl/Srb2SDL-vc9.vcproj b/src/sdl/Srb2SDL-vc9.vcproj index cfa49ea50..3c430b2b4 100644 --- a/src/sdl/Srb2SDL-vc9.vcproj +++ b/src/sdl/Srb2SDL-vc9.vcproj @@ -2742,6 +2742,50 @@ RelativePath="..\mserv.h" > + + + + + + + + + + + + + + + + Date: Thu, 13 Aug 2020 21:10:53 -0700 Subject: [PATCH 044/136] Stop treating apostrophes, parenthesis and curly braces as separate arguments --- src/command.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/command.c b/src/command.c index 0a46839f3..39ffbee44 100644 --- a/src/command.c +++ b/src/command.c @@ -2370,15 +2370,6 @@ skipwhite: } } - // parse single characters - if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'') - { - com_token[len] = c; - len++; - com_token[len] = 0; - return data + 1; - } - // parse a regular word do { @@ -2398,8 +2389,6 @@ skipwhite: len++; c = *data; } - if (c == '{' || c == '}' || c == ')'|| c == '(' || c == '\'') - break; } while (c > 32); com_token[len] = 0; From 7be58ca3eef817b5282ce3b8a4359750d9d47758 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 13 Aug 2020 21:57:36 -0700 Subject: [PATCH 045/136] Use AsciiChar to get the input from windows console window --- src/sdl/i_system.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 88f45c302..728587e16 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -560,14 +560,12 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co) case VK_TAB: event.data1 = KEY_NULL; break; - case VK_SHIFT: - event.data1 = KEY_LSHIFT; - break; case VK_RETURN: entering_con_command = false; /* FALLTHRU */ default: - event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char + //event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char + event.data1 = evt.uChar.AsciiChar; } if (co != INVALID_HANDLE_VALUE && GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &t)) { @@ -586,18 +584,6 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co) } } } - else - { - event.type = ev_keyup; - switch (evt.wVirtualKeyCode) - { - case VK_SHIFT: - event.data1 = KEY_LSHIFT; - break; - default: - break; - } - } if (event.data1) D_PostEvent(&event); } From 39da55bce3f2a8df200462a1edceca62b0375182 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 14 Aug 2020 08:25:12 -0700 Subject: [PATCH 046/136] dumbass doesn't test changes before pushing directly to next --- src/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e43cafcca..6e3da126b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -102,7 +102,6 @@ set(SRB2_CORE_HEADERS m_swap.h md5.h mserv.h - http-mserv.h p5prof.h s_sound.h screen.h From 1e99f94de597d40fb1b6d5d7304dc00dcbefe242 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Fri, 14 Aug 2020 20:31:30 -0300 Subject: [PATCH 047/136] Fix cv_glshearing 2 in first person --- src/r_main.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/r_main.c b/src/r_main.c index 4f79dd8db..c99e6c329 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1074,15 +1074,22 @@ subsector_t *R_PointInSubsectorOrNull(fixed_t x, fixed_t y) // recalc necessary stuff for mouseaiming // slopes are already calculated for the full possible view (which is 4*viewheight). // 18/08/18: (No it's actually 16*viewheight, thanks Jimita for finding this out) -static void R_SetupFreelook(void) +static void R_SetupFreelook(player_t *player, boolean skybox) { INT32 dy = 0; +#ifndef HWRENDER + (void)player; + (void)skybox; +#endif + // clip it in the case we are looking a hardware 90 degrees full aiming // (lmps, network and use F12...) if (rendermode == render_soft #ifdef HWRENDER - || cv_glshearing.value + || (rendermode == render_opengl + && (cv_glshearing.value == 1 + || (cv_glshearing.value == 2 && R_IsViewpointThirdPerson(player, skybox)))) #endif ) { @@ -1203,7 +1210,7 @@ void R_SetupFrame(player_t *player) viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); - R_SetupFreelook(); + R_SetupFreelook(player, false); } void R_SkyboxFrame(player_t *player) @@ -1340,7 +1347,7 @@ void R_SkyboxFrame(player_t *player) viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); - R_SetupFreelook(); + R_SetupFreelook(player, true); } boolean R_ViewpointHasChasecam(player_t *player) From e1a745db1f2ab5dd920fc0a5d03a369c871f946d Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 14 Aug 2020 21:01:41 -0700 Subject: [PATCH 048/136] REMOVE THE LAST OF CV_ALLCAPS --- src/hu_stuff.c | 16 ++++++++-------- src/v_video.c | 2 -- src/v_video.h | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index b61192533..0dee3558c 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -1466,7 +1466,7 @@ static void HU_drawMiniChat(void) if (cv_chatbacktint.value) // on request of wolfy V_DrawFillConsoleMap(x + dx + 2, y+dy, charwidth, charheight, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT); - V_DrawChatCharacter(x + dx + 2, y+dy, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|transflag, !cv_allcaps.value, colormap); + V_DrawChatCharacter(x + dx + 2, y+dy, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|transflag, true, colormap); } dx += charwidth; @@ -1559,7 +1559,7 @@ static void HU_drawChatLog(INT32 offset) else { if ((y+dy+2 >= chat_topy) && (y+dy < (chat_bottomy))) - V_DrawChatCharacter(x + dx + 2, y+dy+2, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT, !cv_allcaps.value, colormap); + V_DrawChatCharacter(x + dx + 2, y+dy+2, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT, true, colormap); else j++; // don't forget to increment this or we'll get stuck in the limbo. } @@ -1659,7 +1659,7 @@ static void HU_DrawChat(void) ++i; else { - V_DrawChatCharacter(chatx + c + 2, y, talk[i] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|cflag, !cv_allcaps.value, V_GetStringColormap(talk[i]|cflag)); + V_DrawChatCharacter(chatx + c + 2, y, talk[i] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|cflag, true, V_GetStringColormap(talk[i]|cflag)); i++; } @@ -1677,7 +1677,7 @@ static void HU_DrawChat(void) typelines = 1; if ((strlen(w_chat) == 0 || c_input == 0) && hu_tick < 4) - V_DrawChatCharacter(chatx + 2 + c, y+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL); + V_DrawChatCharacter(chatx + 2 + c, y+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, true, NULL); while (w_chat[i]) { @@ -1687,7 +1687,7 @@ static void HU_DrawChat(void) INT32 cursorx = (c+charwidth < boxw-charwidth) ? (chatx + 2 + c+charwidth) : (chatx+1); // we may have to go down. INT32 cursory = (cursorx != chatx+1) ? (y) : (y+charheight); if (hu_tick < 4) - V_DrawChatCharacter(cursorx, cursory+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL); + V_DrawChatCharacter(cursorx, cursory+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, true, NULL); if (cursorx == chatx+1 && saylen == i) // a weirdo hack { @@ -1700,7 +1700,7 @@ static void HU_DrawChat(void) if (w_chat[i] < HU_FONTSTART) ++i; else - V_DrawChatCharacter(chatx + c + 2, y, w_chat[i++] | V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, !cv_allcaps.value, NULL); + V_DrawChatCharacter(chatx + c + 2, y, w_chat[i++] | V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, true, NULL); c += charwidth; if (c > boxw-(charwidth*2) && !skippedline) @@ -1825,7 +1825,7 @@ static void HU_DrawChat_Old(void) } if ((strlen(w_chat) == 0 || c_input == 0) && hu_tick < 4) - V_DrawCharacter(HU_INPUTX+c, y+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, !cv_allcaps.value); + V_DrawCharacter(HU_INPUTX+c, y+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, true); i = 0; while (w_chat[i]) @@ -1835,7 +1835,7 @@ static void HU_DrawChat_Old(void) { INT32 cursorx = (HU_INPUTX+c+charwidth < vid.width) ? (HU_INPUTX + c + charwidth) : (HU_INPUTX); // we may have to go down. INT32 cursory = (cursorx != HU_INPUTX) ? (y) : (y+charheight); - V_DrawCharacter(cursorx, cursory+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, !cv_allcaps.value); + V_DrawCharacter(cursorx, cursory+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, true); } //Hurdler: isn't it better like that? diff --git a/src/v_video.c b/src/v_video.c index b88c4838b..81c1d4d66 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -77,8 +77,6 @@ consvar_t cv_csaturation = {"csaturation", "10", CV_SAVE|CV_CALL, saturation_con consvar_t cv_bsaturation = {"bsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_msaturation = {"msaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_allcaps = {"allcaps", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; - static CV_PossibleValue_t constextsize_cons_t[] = { {V_NOSCALEPATCH, "Small"}, {V_SMALLSCALEPATCH, "Medium"}, {V_MEDSCALEPATCH, "Large"}, {0, "Huge"}, {0, NULL}}; diff --git a/src/v_video.h b/src/v_video.h index 9f7a9a9e9..636d002ea 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -32,7 +32,7 @@ cv_globalgamma, cv_globalsaturation, \ cv_rhue, cv_yhue, cv_ghue, cv_chue, cv_bhue, cv_mhue,\ cv_rgamma, cv_ygamma, cv_ggamma, cv_cgamma, cv_bgamma, cv_mgamma, \ cv_rsaturation, cv_ysaturation, cv_gsaturation, cv_csaturation, cv_bsaturation, cv_msaturation,\ -cv_allcaps; +; // Allocates buffer screens, call before R_Init. void V_Init(void); From 065471392c3c196ad60c0b7c55eca13bc8ae5501 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 14 Aug 2020 21:05:23 -0700 Subject: [PATCH 049/136] Disable showjoinaddress by default --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7ccb4ef92..8c8dad4cc 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -157,7 +157,7 @@ ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL}; -consvar_t cv_showjoinaddress = {"showjoinaddress", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_showjoinaddress = {"showjoinaddress", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; consvar_t cv_playbackspeed = {"playbackspeed", "1", 0, playbackspeed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; From c660a86b8d0f582ba4c4ad805ecef16c66ccd6e6 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 14 Aug 2020 21:13:15 -0700 Subject: [PATCH 050/136] Add a menu option for showjoinaddress I also had to make the "alphaKey" UINT16. Hopefully nothing breaks! --- src/m_menu.c | 3 +++ src/m_menu.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index f0f4e85ae..92dfe8ff4 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1663,8 +1663,11 @@ static menuitem_t OP_ServerOptionsMenu[] = #ifndef NONET {IT_HEADER, NULL, "Advanced", NULL, 225}, {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Master server", &cv_masterserver, 231}, + {IT_STRING | IT_CVAR, NULL, "Join delay", &cv_joindelay, 246}, {IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", &cv_resynchattempts, 251}, + + {IT_STRING | IT_CVAR, NULL, "Show IP Address of Joiners", &cv_showjoinaddress, 256}, #endif }; diff --git a/src/m_menu.h b/src/m_menu.h index 88c06ae6f..0465128ef 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -327,7 +327,7 @@ typedef struct menuitem_s void *itemaction; // hotkey in menu or y of the item - UINT8 alphaKey; + UINT16 alphaKey; } menuitem_t; extern menuitem_t MP_RoomMenu[]; From 9c914095c001e06f1b93856e13766cb018ee2ea3 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 14 Aug 2020 22:13:06 -0700 Subject: [PATCH 051/136] Is this SRB2Kart server? --- src/mserv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mserv.c b/src/mserv.c index 44fa8e3ae..fcce1ab4d 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -57,7 +57,7 @@ static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { }; consvar_t cv_masterserver = {"masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_servername = {"servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_servername = {"servername", "SRB2 server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; From 4acc3e6d0c52695e559f5a5fbfc6b06bc93f71ae Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 14 Aug 2020 22:18:51 -0700 Subject: [PATCH 052/136] Strip a few more instances of SONIC ROBO BLAST 2 KART --- src/i_threads.h | 2 +- src/sdl/i_threads.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i_threads.h b/src/i_threads.h index 45a3dcc3e..ecb9fce67 100644 --- a/src/i_threads.h +++ b/src/i_threads.h @@ -1,4 +1,4 @@ -// SONIC ROBO BLAST 2 KART +// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2020 by James R. // diff --git a/src/sdl/i_threads.c b/src/sdl/i_threads.c index 078f4e0f4..3b1c20b9a 100644 --- a/src/sdl/i_threads.c +++ b/src/sdl/i_threads.c @@ -1,4 +1,4 @@ -// SONIC ROBO BLAST 2 KART +// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2020 by James R. // From fe4f311da5830b8ec9328da0723c485323921973 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 14 Aug 2020 22:38:15 -0700 Subject: [PATCH 053/136] . --- src/v_video.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/v_video.h b/src/v_video.h index 636d002ea..59d05dcd0 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -27,12 +27,11 @@ extern UINT8 *screens[5]; -extern consvar_t cv_ticrate, cv_constextsize,\ -cv_globalgamma, cv_globalsaturation, \ -cv_rhue, cv_yhue, cv_ghue, cv_chue, cv_bhue, cv_mhue,\ -cv_rgamma, cv_ygamma, cv_ggamma, cv_cgamma, cv_bgamma, cv_mgamma, \ -cv_rsaturation, cv_ysaturation, cv_gsaturation, cv_csaturation, cv_bsaturation, cv_msaturation,\ -; +extern consvar_t cv_ticrate, cv_constextsize, +cv_globalgamma, cv_globalsaturation, +cv_rhue, cv_yhue, cv_ghue, cv_chue, cv_bhue, cv_mhue, +cv_rgamma, cv_ygamma, cv_ggamma, cv_cgamma, cv_bgamma, cv_mgamma, +cv_rsaturation, cv_ysaturation, cv_gsaturation, cv_csaturation, cv_bsaturation, cv_msaturation; // Allocates buffer screens, call before R_Init. void V_Init(void); From 12b4b7f7086b5c074e738e465dfe84f790ca40d3 Mon Sep 17 00:00:00 2001 From: Zwip-Zwap Zapony Date: Tue, 18 Aug 2020 11:42:35 +0200 Subject: [PATCH 054/136] Fix Ringslinger weapon ring penalty missnapment --- src/st_stuff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index 2fd1bda77..86e0b3754 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -2195,7 +2195,7 @@ static void ST_drawMatchHUD(void) { sprintf(penaltystr, "-%d", stplyr->ammoremoval); V_DrawString(offset + 8 + stplyr->ammoremovalweapon * 20, y, - V_REDMAP, penaltystr); + V_REDMAP|V_SNAPTOBOTTOM, penaltystr); } } From d082e42d48c4d304896fa5327906fcf92ba96d67 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 18 Aug 2020 11:18:42 -0700 Subject: [PATCH 055/136] Cast UINT16 for unlockable_t.height -> alphaKey --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index 92dfe8ff4..b80ace3cb 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8020,7 +8020,7 @@ static void M_SecretsMenu(INT32 choice) skyRoomMenuTranslations[i-1] = (UINT8)ul; SR_MainMenu[i].text = unlockables[ul].name; - SR_MainMenu[i].alphaKey = (UINT8)unlockables[ul].height; + SR_MainMenu[i].alphaKey = (UINT16)unlockables[ul].height; if (unlockables[ul].type == SECRET_HEADER) { From 9473de18c58f3b3c204505c687e53c7aeb5291ed Mon Sep 17 00:00:00 2001 From: Hannu Hanhi Date: Tue, 18 Aug 2020 23:21:26 +0300 Subject: [PATCH 056/136] Convince the compiler that I know what I'm doing, I think --- src/hardware/hw_md2.c | 4 +++- src/hardware/r_opengl/r_opengl.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 05afff37f..5f5130896 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1410,7 +1410,9 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) // If so, uvs need to be readjusted. // Comparing floats with the != operator here should be okay because they // are just copies of glpatches' max_s and max_t values. - if (gpatch->max_s != md2->model->max_s || gpatch->max_t != md2->model->max_t) + // Instead of the != operator, memcmp is used to avoid a compiler warning. + if (memcmp(&(gpatch->max_s), &(md2->model->max_s), sizeof(md2->model->max_s)) != 0 || + memcmp(&(gpatch->max_t), &(md2->model->max_t), sizeof(md2->model->max_t)) != 0) adjustTextureCoords(md2->model, gpatch); HWR_GetMappedPatch(gpatch, spr->colormap); } diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 195f3d6f7..da86dc0fd 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -2772,7 +2772,9 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 // (Can happen when model uses a sprite as a texture and the sprite changes) // Comparing floats with the != operator here should be okay because they // are just copies of glpatches' max_s and max_t values. - if (model->vbo_max_s != model->max_s || model->vbo_max_t != model->max_t) + // Instead of the != operator, memcmp is used to avoid a compiler warning. + if (memcmp(&(model->vbo_max_s), &(model->max_s), sizeof(model->max_s)) != 0 || + memcmp(&(model->vbo_max_t), &(model->max_t), sizeof(model->max_t)) != 0) useVBO = false; pglEnableClientState(GL_NORMAL_ARRAY); From 2a27628232d12bcec5c70f68699257fd4b8e0963 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Tue, 18 Aug 2020 17:34:32 -0500 Subject: [PATCH 057/136] Fix bug where SRB2 would check size of current directory instead of srb2home Hopefully that fixes that one bug with weird filesize issues too --- src/sdl/i_system.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index b24ae2814..8ad2826b7 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -2601,7 +2601,7 @@ void I_GetDiskFreeSpace(INT64 *freespace) return; #else // Both Linux and BSD have this, apparently. struct statfs stfs; - if (statfs(".", &stfs) == -1) + if (statfs(srb2home, &stfs) == -1) { *freespace = INT32_MAX; return; @@ -2620,7 +2620,7 @@ void I_GetDiskFreeSpace(INT64 *freespace) } if (pfnGetDiskFreeSpaceEx) { - if (pfnGetDiskFreeSpaceEx(NULL, &lfreespace, &usedbytes, NULL)) + if (pfnGetDiskFreeSpaceEx(srb2home, &lfreespace, &usedbytes, NULL)) *freespace = lfreespace.QuadPart; else *freespace = INT32_MAX; From 63ba605f527e1c4781c076b59562eac56e60ee93 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 15 Aug 2020 21:29:02 -0700 Subject: [PATCH 058/136] Is it GCC is it gcc, is it???? (cherry picked from commit 246e71a463c88c3bbc5334f9d07caca2e1577f79) --- src/Makefile.cfg | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index 5c56978e7..81cbc1aee 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -48,7 +48,9 @@ endif # Automatically set version flag, but not if one was manually set ifeq (,$(filter GCC%,$(.VARIABLES))) - ifneq (,$(findstring gcc,$(shell $(CC) --version))) # if it's GCC + version:=$(shell $(CC) --version) + # check if this is in fact GCC + ifneq (,$(or $(findstring gcc,$(version)),$(findstring GCC,$(version)))) version:=$(shell $(CC) -dumpversion) # Turn version into words of major, minor From ce98fc16bd898c49d187579a12517136ec036634 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 18 Aug 2020 16:45:00 -0700 Subject: [PATCH 059/136] Makefile: Make WARNINGMODE the default, optionally disable with RELAXWARNINGS --- src/Makefile | 3 ++- src/Makefile.cfg | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index 606e65930..ffe4b4a16 100644 --- a/src/Makefile +++ b/src/Makefile @@ -58,7 +58,8 @@ # Compile with GCC 4.6x version, add 'GCC46=1' # Compile a profile version, add 'PROFILEMODE=1' # Compile a debug version, add 'DEBUGMODE=1' -# Compile with extra warnings, add 'WARNINGMODE=1' +# Compile with less warnings, add 'RELAXWARNINGS=1' +# Generate compiler errors for most compiler warnings, add 'ERRORMODE=1' # Compile without NASM's tmap.nas, add 'NOASM=1' # Compile without 3D hardware support, add 'NOHW=1' # Compile without 3D sound support, add 'NOHS=1' diff --git a/src/Makefile.cfg b/src/Makefile.cfg index 81cbc1aee..86d602438 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -210,10 +210,7 @@ WFLAGS=-Wall ifndef GCC295 #WFLAGS+=-Wno-packed endif -ifdef ERRORMODE -WARNINGMODE=1 -endif -ifdef WARNINGMODE +ifndef RELAXWARNINGS WFLAGS+=-W #WFLAGS+=-Wno-sign-compare ifndef GCC295 From fd4ab28a8408da01c5745cd2cdd7787e833028ba Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 18 Aug 2020 18:01:59 -0700 Subject: [PATCH 060/136] Makefile: automatically detect system to compile for, if no system was specified This should work for mingw and linux so far. --- src/Makefile | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/Makefile b/src/Makefile index 606e65930..6d8d6a89c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -82,6 +82,57 @@ # ############################################################################# +ALL_SYSTEMS=\ + PANDORA\ + LINUX64\ + MINGW64\ + HAIKU\ + DUMMY\ + DJGPPDOS\ + MINGW\ + UNIX\ + LINUX\ + SOLARIS\ + FREEBSD\ + MACOSX\ + SDL\ + +# check for user specified system +ifeq (,$(filter $(ALL_SYSTEMS),$(.VARIABLES))) +ifeq ($(OS),Windows_NT) # all windows are Windows_NT... + + $(info Detected a Windows system, compiling for 32-bit MinGW SDL2...) + + # go for a 32-bit sdl mingw exe by default + MINGW=1 + SDL=1 + +else # if you on the *nix + + system:=$(shell uname -s) + + ifeq ($(system),Linux) + new_system=LINUX + else + + $(error \ + Could not automatically detect your system,\ + try specifying a system manually) + + endif + + ifeq ($(shell getconf LONG_BIT),64) + system+=64-bit + new_system:=$(new_system)64 + endif + + $(info Detected $(system) ($(new_system))...) + $(new_system)=1 + +endif +endif + + # SRB2 data files D_DIR?=../bin/Resources D_FILES=$(D_DIR)/srb2.pk3 \ From ec3dffbb94ec8c86214f632511e68fe5dbb2dd02 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Aug 2020 20:19:50 -0700 Subject: [PATCH 061/136] Makefile: add WINDOWSHELL=1 for the Windows detect --- src/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Makefile b/src/Makefile index 6d8d6a89c..23312d27e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -106,6 +106,7 @@ ifeq ($(OS),Windows_NT) # all windows are Windows_NT... # go for a 32-bit sdl mingw exe by default MINGW=1 SDL=1 + WINDOWSHELL=1 else # if you on the *nix From 972c8da4bba51a046b249fff0744d8867bd8ca83 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Fri, 21 Aug 2020 03:30:22 -0500 Subject: [PATCH 062/136] Fix a bug where adding the same mod multiple times counted to the mod limit A one line fix. Seriously. --- src/w_wad.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/w_wad.c b/src/w_wad.c index 548d1bc00..701820b2f 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -776,6 +776,7 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) if (!memcmp(wadfiles[i]->md5sum, md5sum, 16)) { CONS_Alert(CONS_ERROR, M_GetText("%s is already loaded\n"), filename); + packetsizetally -= nameonlylength(filename) + 22; if (handle) fclose(handle); return W_InitFileError(filename, false); From 8b6450fa4dde0217b72af0815352571e8e5e90a1 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Fri, 21 Aug 2020 04:00:54 -0500 Subject: [PATCH 063/136] Forgot to check if the file was important, whoops! --- src/w_wad.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/w_wad.c b/src/w_wad.c index 701820b2f..3a1b17db8 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -776,7 +776,8 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) if (!memcmp(wadfiles[i]->md5sum, md5sum, 16)) { CONS_Alert(CONS_ERROR, M_GetText("%s is already loaded\n"), filename); - packetsizetally -= nameonlylength(filename) + 22; + if (important) + packetsizetally -= nameonlylength(filename) + 22; if (handle) fclose(handle); return W_InitFileError(filename, false); From baaf19249ba73428a9fd28967a77a348a030d8d2 Mon Sep 17 00:00:00 2001 From: Hannu Hanhi Date: Sun, 23 Aug 2020 20:09:55 +0300 Subject: [PATCH 064/136] Add tic and ui times to render stats --- src/d_clisrv.c | 4 ++++ src/d_main.c | 20 ++++++++++++++++++-- src/r_main.c | 2 ++ src/r_main.h | 2 ++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 8c8dad4cc..51c3156f8 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5427,11 +5427,15 @@ void TryRunTics(tic_t realtics) { DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic)); + rs_tictime = I_GetTimeMicros(); + G_Ticker((gametic % NEWTICRATERATIO) == 0); ExtraDataTicker(); gametic++; consistancy[gametic%BACKUPTICS] = Consistancy(); + rs_tictime = I_GetTimeMicros() - rs_tictime; + // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame. if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value) break; diff --git a/src/d_main.c b/src/d_main.c index 25209de68..fcc22b497 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -495,6 +495,8 @@ static void D_Display(void) lastdraw = false; } + rs_uitime = I_GetTimeMicros(); + if (gamestate == GS_LEVEL) { ST_Drawer(); @@ -504,6 +506,10 @@ static void D_Display(void) else F_TitleScreenDrawer(); } + else + { + rs_uitime = I_GetTimeMicros(); + } } // change gamma if needed @@ -544,6 +550,8 @@ static void D_Display(void) CON_Drawer(); + rs_uitime = I_GetTimeMicros() - rs_uitime; + // // wipe update // @@ -656,8 +664,12 @@ static void D_Display(void) V_DrawThinString(30, 60, V_MONOSPACE | V_YELLOWMAP, s); snprintf(s, sizeof s - 1, "sdrw %d", rs_hw_spritedrawtime / divisor); V_DrawThinString(30, 70, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "fin %d", rs_swaptime / divisor); + snprintf(s, sizeof s - 1, "ui %d", rs_uitime / divisor); V_DrawThinString(30, 80, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "fin %d", rs_swaptime / divisor); + V_DrawThinString(30, 90, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "tic %d", rs_tictime / divisor); + V_DrawThinString(30, 105, V_MONOSPACE | V_GRAYMAP, s); if (cv_glbatching.value) { snprintf(s, sizeof s - 1, "bsrt %d", rs_hw_batchsorttime / divisor); @@ -689,8 +701,12 @@ static void D_Display(void) V_DrawThinString(30, 50, V_MONOSPACE | V_YELLOWMAP, s); snprintf(s, sizeof s - 1, "mskd %d", rs_sw_maskedtime / divisor); V_DrawThinString(30, 60, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "fin %d", rs_swaptime / divisor); + snprintf(s, sizeof s - 1, "ui %d", rs_uitime / divisor); V_DrawThinString(30, 70, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "fin %d", rs_swaptime / divisor); + V_DrawThinString(30, 80, V_MONOSPACE | V_YELLOWMAP, s); + snprintf(s, sizeof s - 1, "tic %d", rs_tictime / divisor); + V_DrawThinString(30, 95, V_MONOSPACE | V_GRAYMAP, s); } } diff --git a/src/r_main.c b/src/r_main.c index 4f79dd8db..46ba0ecad 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -102,7 +102,9 @@ extracolormap_t *extra_colormaps = NULL; // Render stats int rs_prevframetime = 0; int rs_rendercalltime = 0; +int rs_uitime = 0; int rs_swaptime = 0; +int rs_tictime = 0; int rs_bsptime = 0; diff --git a/src/r_main.h b/src/r_main.h index 729ec6973..f95e2538d 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -81,7 +81,9 @@ extern consvar_t cv_renderstats; extern int rs_prevframetime;// time when previous frame was rendered extern int rs_rendercalltime; +extern int rs_uitime; extern int rs_swaptime; +extern int rs_tictime; extern int rs_bsptime; From 0846079c0621e816dbf19d7bafff29aea854b00f Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Mon, 24 Aug 2020 00:34:44 +0200 Subject: [PATCH 065/136] Fix bonus items sometimes overriding apples in Snake minigame --- src/d_clisrv.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 8c8dad4cc..b6542f78b 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1269,19 +1269,25 @@ static UINT8 Snake_GetOppositeDir(UINT8 dir) return 12 + 5 - dir; } -static void Snake_FindFreeSlot(UINT8 *x, UINT8 *y, UINT8 headx, UINT8 heady) +static void Snake_FindFreeSlot(UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady) { + UINT8 x, y; UINT16 i; do { - *x = M_RandomKey(SNAKE_NUM_BLOCKS_X); - *y = M_RandomKey(SNAKE_NUM_BLOCKS_Y); + x = M_RandomKey(SNAKE_NUM_BLOCKS_X); + y = M_RandomKey(SNAKE_NUM_BLOCKS_Y); for (i = 0; i < snake->snakelength; i++) - if (*x == snake->snakex[i] && *y == snake->snakey[i]) + if (x == snake->snakex[i] && y == snake->snakey[i]) break; - } while (i < snake->snakelength || (*x == headx && *y == heady)); + } while (i < snake->snakelength || (x == headx && y == heady) + || (x == snake->applex && y == snake->appley) + || (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy)); + + *freex = x; + *freey = y; } static void Snake_Handle(void) @@ -1412,7 +1418,7 @@ static void Snake_Handle(void) // Check collision with apple if (x == snake->applex && y == snake->appley) { - if (snake->snakelength + 1 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y) + if (snake->snakelength + 3 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y) { snake->snakelength++; snake->snakex [snake->snakelength - 1] = snake->snakex [snake->snakelength - 2]; From 57b1becfe00211d0e3ae3ebd4e1af0b45b154c70 Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Mon, 24 Aug 2020 23:53:21 -0300 Subject: [PATCH 066/136] Decolorize players as intended --- src/p_enemy.c | 21 +++++++++++---------- src/p_spec.c | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index fd30f8e38..aa126d244 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8839,25 +8839,26 @@ void A_Dye(mobj_t *actor) INT32 locvar2 = var2; mobj_t *target = ((locvar1 && actor->target) ? actor->target : actor); - UINT8 color = (UINT8)locvar2; + UINT16 color = (UINT16)locvar2; if (LUA_CallAction("A_Dye", actor)) return; if (color >= numskincolors) return; - if (!color) - target->colorized = false; - else - target->colorized = true; - // What if it's a player? if (target->player) - { target->player->powers[pw_dye] = color; - return; - } - target->color = color; + if (!color) + { + target->colorized = false; + target->color = target->player ? target->player->skincolor : SKINCOLOR_NONE; + } + else if (!(target->player)) + { + target->colorized = true; + target->color = color; + } } // Function: A_MoveRelative diff --git a/src/p_spec.c b/src/p_spec.c index 1df212e1b..102c971a9 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1933,7 +1933,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller case 336: // object dye - once { INT32 triggercolor = (INT32)sides[triggerline->sidenum[0]].toptexture; - UINT8 color = (actor->player ? actor->player->powers[pw_dye] : actor->color); + UINT16 color = (actor->player ? actor->player->powers[pw_dye] : actor->color); boolean invert = (triggerline->flags & ML_NOCLIMB ? true : false); if (invert ^ (triggercolor != color)) From ba4310939a5c73fd53916b72c28eea77f889d527 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Wed, 26 Aug 2020 23:22:53 -0500 Subject: [PATCH 067/136] Make Special Stage map transitions respect nextmapoverride. In other words: lets Luas set the next map of Special Stages via G_SetCustomExitVars. It's otherwise impossible to do so via Lua. --- src/g_game.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index b969eb4a4..df298e20b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3808,7 +3808,7 @@ static void G_DoCompleted(void) // a map of the proper gametype -- skip levels that don't support // the current gametype. (Helps avoid playing boss levels in Race, // for instance). - if (!spec) + if (!spec || nextmapoverride) { if (nextmap >= 0 && nextmap < NUMMAPS) { @@ -3860,7 +3860,8 @@ static void G_DoCompleted(void) if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1103-1) I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1); - lastmap = nextmap; // Remember last map for when you come out of the special stage. + if (!spec) + lastmap = nextmap; // Remember last map for when you come out of the special stage. } if ((gottoken = ((gametyperules & GTR_SPECIALSTAGES) && token))) @@ -3881,7 +3882,7 @@ static void G_DoCompleted(void) } } - if (spec && !gottoken) + if (spec && !gottoken && !nextmapoverride) nextmap = lastmap; // Exiting from a special stage? Go back to the game. Tails 08-11-2001 automapactive = false; From 6a37f39ca850eb387ee237f49da2c6154c5986dc Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Fri, 28 Aug 2020 17:10:54 -0400 Subject: [PATCH 068/136] New STJR Intro, replaces the old PURE FAT intro --- src/f_finale.c | 129 ++++++++++++++----------------------------------- 1 file changed, 35 insertions(+), 94 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index d83eeb5cf..d56869897 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -225,6 +225,9 @@ static INT32 cutscene_textspeed = 0; static UINT8 cutscene_boostspeed = 0; static tic_t cutscene_lasttextwrite = 0; +// STJR Intro +char stjrintro[9] = "STJRI000"; + // // This alters the text string cutscene_disptext. // Use the typical string drawing functions to display it. @@ -312,7 +315,7 @@ const char *introtext[NUMINTROSCENES]; static tic_t introscenetime[NUMINTROSCENES] = { - 7*TICRATE + (TICRATE/2), // STJr Presents + 7*TICRATE, // STJr Presents 11*TICRATE + (TICRATE/2), // Two months had passed since... 15*TICRATE + (TICRATE/2), // As it was about to drain the rings... 14*TICRATE, // What Sonic, Tails, and Knuckles... @@ -527,7 +530,8 @@ static void F_IntroDrawScene(void) switch (intro_scenenum) { case 0: - break; + bgxoffs = 28; + break; case 1: background = W_CachePatchName("INTRO1", PU_PATCH); break; @@ -616,98 +620,35 @@ static void F_IntroDrawScene(void) V_DrawScaledPatch(bgxoffs, 0, 0, background); } else if (intro_scenenum == 0) // STJr presents - { - // "Waaaaaaah" intro - if (finalecount-TICRATE/2 < 4*TICRATE+23) { - // aspect is FRACUNIT/2 for 4:3 (source) resolutions, smaller for 16:10 (SRB2) resolutions - fixed_t aspect = (FRACUNIT + (FRACUNIT*4/3 - FRACUNIT*vid.width/vid.height)/2)>>1; - fixed_t x,y; - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 2); - if (finalecount < 30) { // Cry! - if (finalecount < 4) - S_StopMusic(); - if (finalecount == 4) - S_ChangeMusicInternal("_stjr", false); - x = (BASEVIDWIDTH< 6) { - V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH2", PU_PATCH)), aspect); - W_UnlockCachedPatch(patch); - } - if (finalecount > 10) { - V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH3", PU_PATCH)), aspect); - W_UnlockCachedPatch(patch); - } - if (finalecount > 14) { - V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH4", PU_PATCH)), aspect); - W_UnlockCachedPatch(patch); - } - } - else if (finalecount-30 < 20) { // Big eggy - background = W_CachePatchName("FEEDIN", PU_PATCH); - x = (BASEVIDWIDTH< 1 && intro_curtime < 278) + { + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + if (intro_curtime < 30) // Make the text shine! + sprintf(stjrintro, "STJRI%03u", intro_curtime-1); + else if (intro_curtime >= 29 && intro_curtime < 50) // Pause on black screen for just a second + return; + else if (intro_curtime == 51) { - { - // Draw tiny eggy - fixed_t scale = FixedMul(FRACUNIT/3, aspect); - background = W_CachePatchName("FEEDIN", PU_PATCH); - x = (BASEVIDWIDTH< 4*TICRATE) { // Door is being raised! - int ftime = (finalecount-TICRATE/2-4*TICRATE); - y -= FixedDiv((ftime*ftime)< Date: Fri, 28 Aug 2020 17:11:38 -0400 Subject: [PATCH 069/136] Add new `STARTUP` lump, for the splash screen --- src/console.c | 8 ++++++-- src/d_main.c | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/console.c b/src/console.c index 0235b9bd4..21f660a56 100644 --- a/src/console.c +++ b/src/console.c @@ -1677,8 +1677,12 @@ static void CON_DrawBackpic(void) lumpnum_t piclump; int x, w, h; - // Get the lumpnum for CONSBACK, or fallback into MISSING. - piclump = W_CheckNumForName("CONSBACK"); + // Get the lumpnum for CONSBACK, STARTUP (Only during game startup) or fallback into MISSING. + if (con_startup) + piclump = W_CheckNumForName("STARTUP"); + else + piclump = W_CheckNumForName("CONSBACK"); + if (piclump == LUMPERROR) piclump = W_GetNumForName("MISSING"); diff --git a/src/d_main.c b/src/d_main.c index 25209de68..77c33e7ba 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -768,7 +768,7 @@ void D_SRB2Loop(void) */ /* Smells like a hack... Don't fade Sonic's ass into the title screen. */ if (gamestate != GS_TITLESCREEN) - V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(W_GetNumForName("CONSBACK"), PU_PATCH)); + V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(W_GetNumForName("STARTUP"), PU_PATCH)); for (;;) { From f0ec718aae6b9fa32c63c949ac34344cb511d54f Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Fri, 28 Aug 2020 20:08:46 -0400 Subject: [PATCH 070/136] Make the intro shorter as I felt it was a bit too long Change the code a bit so less magic numbers. --- src/f_finale.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index d56869897..71c7c3784 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -315,7 +315,7 @@ const char *introtext[NUMINTROSCENES]; static tic_t introscenetime[NUMINTROSCENES] = { - 7*TICRATE, // STJr Presents + 6*TICRATE, // STJr Presents 11*TICRATE + (TICRATE/2), // Two months had passed since... 15*TICRATE + (TICRATE/2), // As it was about to drain the rings... 14*TICRATE, // What Sonic, Tails, and Knuckles... @@ -621,14 +621,14 @@ static void F_IntroDrawScene(void) } else if (intro_scenenum == 0) // STJr presents { - if (intro_curtime > 1 && intro_curtime < 278) + if (intro_curtime > 1 && intro_curtime < introscenetime[intro_scenenum]) { V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - if (intro_curtime < 30) // Make the text shine! + if (intro_curtime < TICRATE-5) // Make the text shine! sprintf(stjrintro, "STJRI%03u", intro_curtime-1); - else if (intro_curtime >= 29 && intro_curtime < 50) // Pause on black screen for just a second + else if (intro_curtime >= TICRATE-6 && intro_curtime < 2*TICRATE-20) // Pause on black screen for just a second return; - else if (intro_curtime == 51) + else if (intro_curtime == 2*TICRATE-19) { // Fade in the text // The text fade out is automatically handled when switching to a new intro scene From 13033c521bda9caa56607c019db8a3f92ff41c86 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sat, 29 Aug 2020 19:28:16 -0400 Subject: [PATCH 071/136] Attempts to make the intro a bit more shorter Make the scene time be 5 seconds long max Use the standard fade which is much shorter in length. --- src/f_finale.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 71c7c3784..2ff19b377 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -315,7 +315,7 @@ const char *introtext[NUMINTROSCENES]; static tic_t introscenetime[NUMINTROSCENES] = { - 6*TICRATE, // STJr Presents + 5*TICRATE, // STJr Presents 11*TICRATE + (TICRATE/2), // Two months had passed since... 15*TICRATE + (TICRATE/2), // As it was about to drain the rings... 14*TICRATE, // What Sonic, Tails, and Knuckles... @@ -641,7 +641,7 @@ static void F_IntroDrawScene(void) F_TryColormapFade(31); V_DrawSmallScaledPatch(bgxoffs, 84, 0, background); F_WipeEndScreen(); - F_RunWipe(99,true); + F_RunWipe(0,true); } if (!WipeInAction) // Draw the patch if not in a wipe From 08aa1a5ad7150a1f14b96557100b918698d6b358 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sat, 29 Aug 2020 20:04:11 -0400 Subject: [PATCH 072/136] Fix compile error --- src/f_finale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/f_finale.c b/src/f_finale.c index 2ff19b377..e6765b020 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -621,7 +621,7 @@ static void F_IntroDrawScene(void) } else if (intro_scenenum == 0) // STJr presents { - if (intro_curtime > 1 && intro_curtime < introscenetime[intro_scenenum]) + if (intro_curtime > 1 && intro_curtime < (INT32)introscenetime[intro_scenenum]) { V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); if (intro_curtime < TICRATE-5) // Make the text shine! From c188d6eb5d976de3fbe732fecd2dcceaa14f4eb3 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 29 Aug 2020 17:21:54 -0700 Subject: [PATCH 073/136] C90? --- src/mserv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mserv.c b/src/mserv.c index fcce1ab4d..5f80216f9 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -53,7 +53,7 @@ static void MasterServer_OnChange(void); static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { {2, "MIN"}, {60, "MAX"}, - {0} + {0,NULL} }; consvar_t cv_masterserver = {"masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL}; From d31fb748b2e4c4e19afdb178c31e1c692eae0c1e Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 16 Aug 2020 21:42:58 -0700 Subject: [PATCH 074/136] Do not do master server things when NOCURL (cherry picked from commit b681b6e9e619c965ce7dff00ff95338247e989e3) --- src/d_clisrv.c | 19 ++++++++++---- src/doomdef.h | 6 +++++ src/http-mserv.c | 8 ++++++ src/m_menu.c | 27 +++++++++++++------- src/mserv.c | 65 ++++++++++++++++++++++++++++++++---------------- 5 files changed, 89 insertions(+), 36 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 8c8dad4cc..330dfa27c 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2306,7 +2306,7 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node) M_SortServerList(); } -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) struct Fetch_servers_ctx { int room; @@ -2351,7 +2351,7 @@ Fetch_servers_thread (struct Fetch_servers_ctx *ctx) free(ctx); } -#endif/*HAVE_THREADS*/ +#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ void CL_QueryServerList (msg_server_t *server_list) { @@ -2388,9 +2388,8 @@ void CL_QueryServerList (msg_server_t *server_list) void CL_UpdateServerList(boolean internetsearch, INT32 room) { -#ifdef HAVE_THREADS - struct Fetch_servers_ctx *ctx; -#endif + (void)internetsearch; + (void)room; SL_ClearServerList(0); @@ -2407,9 +2406,12 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) if (netgame) SendAskInfo(BROADCASTADDR); +#ifdef MASTERSERVER if (internetsearch) { #ifdef HAVE_THREADS + struct Fetch_servers_ctx *ctx; + ctx = malloc(sizeof *ctx); /* This called from M_Refresh so I don't use a mutex */ @@ -2436,6 +2438,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) } #endif } +#endif/*MASTERSERVER*/ } #endif // ifndef NONET @@ -3840,8 +3843,10 @@ void D_QuitNetGame(void) for (i = 0; i < MAXNETNODES; i++) if (nodeingame[i]) HSendPacket(i, true, 0, 0); +#ifdef MASTERSERVER if (serverrunning && ms_RoomId > 0) UnregisterServer(); +#endif } else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode]) { @@ -4105,8 +4110,10 @@ boolean SV_SpawnServer(void) if (netgame && I_NetOpenSocket) { I_NetOpenSocket(); +#ifdef MASTERSERVER if (ms_RoomId > 0) RegisterServer(); +#endif } // non dedicated server just connect to itself @@ -5568,7 +5575,9 @@ void NetUpdate(void) // client send the command after a receive of the server // the server send before because in single player is beter +#ifdef MASTERSERVER MasterClient_Ticker(); // Acking the Master Server +#endif if (client) { diff --git a/src/doomdef.h b/src/doomdef.h index 31dd2bcda..a51b66300 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -644,4 +644,10 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Maintain compatibility with older 2.2 demos #define OLD22DEMOCOMPAT +#ifdef HAVE_CURL +#define MASTERSERVER +#else +#undef UPDATE_ALERT +#endif + #endif // __DOOMDEF__ diff --git a/src/http-mserv.c b/src/http-mserv.c index 47c2b5b0f..1e8d35648 100644 --- a/src/http-mserv.c +++ b/src/http-mserv.c @@ -14,7 +14,9 @@ Documentation available here. */ +#ifdef HAVE_CURL #include +#endif #include "doomdef.h" #include "d_clisrv.h" @@ -49,6 +51,8 @@ consvar_t cv_masterserver_token = { NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ }; +#ifdef MASTERSERVER + static int hms_started; static char *hms_api; @@ -664,10 +668,14 @@ HMS_set_api (char *api) #endif } +#endif/*MASTERSERVER*/ + static void MasterServer_Debug_OnChange (void) { +#ifdef MASTERSERVER /* TODO: change to 'latest-log.txt' for log files revision. */ if (cv_masterserver_debug.value) CONS_Printf("Master server debug messages will appear in log.txt\n"); +#endif } diff --git a/src/m_menu.c b/src/m_menu.c index b80ace3cb..d09ce6087 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -3842,7 +3842,7 @@ void M_SetupNextMenu(menu_t *menudef) { INT16 i; -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) if (currentMenu == &MP_RoomDef || currentMenu == &MP_ConnectDef) { I_lock_mutex(&ms_QueryId_mutex); @@ -3930,7 +3930,7 @@ void M_Ticker(void) if (currentMenu == &OP_ScreenshotOptionsDef) M_SetupScreenshotMenu(); -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) I_lock_mutex(&ms_ServerList_mutex); { if (ms_ServerList) @@ -11194,8 +11194,9 @@ static boolean M_CheckMODVersion(int id) } else return true; } +#endif/*UPDATE_ALERT*/ -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) static void Check_new_version_thread (int *id) { @@ -11204,7 +11205,9 @@ Check_new_version_thread (int *id) okay = 0; +#ifdef UPDATE_ALERT if (M_CheckMODVersion(*id)) +#endif { I_lock_mutex(&ms_QueryId_mutex); { @@ -11248,8 +11251,7 @@ Check_new_version_thread (int *id) free(id); } -#endif/*HAVE_THREADS*/ -#endif/*UPDATE_ALERT*/ +#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ static void M_ConnectMenu(INT32 choice) { @@ -11290,7 +11292,7 @@ UINT32 roomIds[NUM_LIST_ROOMS]; static void M_RoomMenu(INT32 choice) { INT32 i; -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) int *id; #endif @@ -11312,9 +11314,14 @@ static void M_RoomMenu(INT32 choice) MP_RoomDef.prevMenu = currentMenu; M_SetupNextMenu(&MP_RoomDef); -#ifdef UPDATE_ALERT +#ifdef MASTERSERVER #ifdef HAVE_THREADS +#ifdef UPDATE_ALERT m_waiting_mode = M_WAITING_VERSION; +#else/*UPDATE_ALERT*/ + m_waiting_mode = M_WAITING_ROOMS; +#endif/*UPDATE_ALERT*/ + MP_RoomMenu[0].text = ""; id = malloc(sizeof *id); @@ -11328,17 +11335,19 @@ static void M_RoomMenu(INT32 choice) I_spawn_thread("check-new-version", (I_thread_fn)Check_new_version_thread, id); #else/*HAVE_THREADS*/ +#ifdef UPDATE_ALERT if (M_CheckMODVersion(0)) +#endif/*UPDATE_ALERT*/ { GetRoomsList(currentMenu->prevMenu == &MP_ServerDef, 0); } #endif/*HAVE_THREADS*/ -#endif/*UPDATE_ALERT*/ +#endif/*MASTERSERVER*/ } static void M_ChooseRoom(INT32 choice) { -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) I_lock_mutex(&ms_QueryId_mutex); { ms_QueryId++; diff --git a/src/mserv.c b/src/mserv.c index 5f80216f9..27d479797 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -23,6 +23,8 @@ #include "m_menu.h" #include "z_zone.h" +#ifdef MASTERSERVER + static int MSId; static int MSRegisteredId = -1; @@ -43,11 +45,14 @@ static I_cond MSCond; # define Unlock_state() #endif/*HAVE_THREADS*/ -static void Update_parameters (void); - #ifndef NONET static void Command_Listserv_f(void); #endif + +#endif/*MASTERSERVER*/ + +static void Update_parameters (void); + static void MasterServer_OnChange(void); static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { @@ -63,7 +68,7 @@ consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SA INT16 ms_RoomId = -1; -#ifdef HAVE_THREADS +#if defined (MASTERSERVER) && defined (HAVE_THREADS) int ms_QueryId; I_mutex ms_QueryId_mutex; @@ -91,10 +96,14 @@ void AddMServCommands(void) CV_RegisterVar(&cv_masterserver_debug); CV_RegisterVar(&cv_masterserver_token); CV_RegisterVar(&cv_servername); +#ifdef MASTERSERVER COM_AddCommand("listserv", Command_Listserv_f); #endif +#endif } +#ifdef MASTERSERVER + static void WarnGUI (void) { #ifdef HAVE_THREADS @@ -395,6 +404,7 @@ Change_masterserver_thread (char *api) void RegisterServer(void) { +#ifdef MASTERSERVER #ifdef HAVE_THREADS I_spawn_thread( "register-server", @@ -404,6 +414,7 @@ void RegisterServer(void) #else Finish_registration(); #endif +#endif/*MASTERSERVER*/ } static void UpdateServer(void) @@ -421,6 +432,7 @@ static void UpdateServer(void) void UnregisterServer(void) { +#ifdef MASTERSERVER #ifdef HAVE_THREADS I_spawn_thread( "unlist-server", @@ -430,6 +442,7 @@ void UnregisterServer(void) #else Finish_unlist(); #endif +#endif/*MASTERSERVER*/ } static boolean @@ -465,9 +478,33 @@ static inline void SendPingToMasterServer(void) } } +void MasterClient_Ticker(void) +{ +#ifdef MASTERSERVER + SendPingToMasterServer(); +#endif +} + +static void +Set_api (const char *api) +{ +#ifdef HAVE_THREADS + I_spawn_thread( + "change-masterserver", + (I_thread_fn)Change_masterserver_thread, + strdup(api) + ); +#else + HMS_set_api(strdup(api)); +#endif +} + +#endif/*MASTERSERVER*/ + static void Update_parameters (void) { +#ifdef MASTERSERVER int registered; int delayed; @@ -487,29 +524,12 @@ Update_parameters (void) if (! delayed && registered) UpdateServer(); } -} - -void MasterClient_Ticker(void) -{ - SendPingToMasterServer(); -} - -static void -Set_api (const char *api) -{ -#ifdef HAVE_THREADS - I_spawn_thread( - "change-masterserver", - (I_thread_fn)Change_masterserver_thread, - strdup(api) - ); -#else - HMS_set_api(strdup(api)); -#endif +#endif/*MASTERSERVER*/ } static void MasterServer_OnChange(void) { +#ifdef MASTERSERVER UnregisterServer(); /* @@ -527,4 +547,5 @@ static void MasterServer_OnChange(void) if (Online()) RegisterServer(); +#endif/*MASTERSERVER*/ } From e6c914c7b2391e5aa530d346e14d8606290557bc Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 29 Aug 2020 17:51:59 -0700 Subject: [PATCH 075/136] Fix stupid NONET --- src/d_clisrv.c | 1 - src/doomdef.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 330dfa27c..9a636dd45 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2566,7 +2566,6 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) *asksent = I_GetTime(); } #else - (void)viams; (void)asksent; // No netgames, so we skip this state. cl_mode = CL_ASKJOIN; diff --git a/src/doomdef.h b/src/doomdef.h index a51b66300..6645fb834 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -644,7 +644,7 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Maintain compatibility with older 2.2 demos #define OLD22DEMOCOMPAT -#ifdef HAVE_CURL +#if defined (HAVE_CURL) && ! defined (NONET) #define MASTERSERVER #else #undef UPDATE_ALERT From ce666acddaf354a1f0ec0c6bc86f570c94ba83a5 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Tue, 7 Apr 2020 19:07:39 -0400 Subject: [PATCH 076/136] Fix some build errors (cherry picked from commit f25235d1eb8a2affb0b14cf2afb49863b5fee477) --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8ecee2b19..67a3b66b7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -43,7 +43,7 @@ jobs: - v1-SRB2-APT - run: name: Install SDK - command: apt-get -qq -y --no-install-recommends install git build-essential nasm libpng-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 libopenmpt-dev:i386 gettext ccache wget gcc-multilib upx openssh-client + command: apt-get -qq -y --no-install-recommends install git build-essential nasm libpng-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 libcurl4-openssl-dev:i386 libopenmpt-dev:i386 gettext ccache wget gcc-multilib upx openssh-client - save_cache: key: v1-SRB2-APT @@ -71,4 +71,4 @@ jobs: - save_cache: key: v1-SRB2-{{ .Branch }}-{{ checksum "objs/Linux/SDL/Release/depend.dep" }} paths: - - /root/.ccache \ No newline at end of file + - /root/.ccache From 1818693d71007588f265e36a0820c91cfc8ebb8f Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 24 Aug 2020 17:51:01 -0700 Subject: [PATCH 077/136] Fix NOGME compiling (cherry picked from commit d52a35e5b6c1e65be94820fa42a8b8e8f0b8d091) --- src/sdl/mixer_sound.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index ef34b266d..25e7fde89 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -220,6 +220,7 @@ static void var_cleanup(void) internal_volume = 100; } +#if defined (HAVE_LIBGME) && defined (HAVE_ZLIB) static const char* get_zlib_error(int zErr) { switch (zErr) @@ -240,6 +241,7 @@ static const char* get_zlib_error(int zErr) return "unknown error"; } } +#endif /// ------------------------ /// Audio System From 7cbc9add07546fb89abb00e30beb240e8eb4ff4f Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 24 Aug 2020 17:58:39 -0700 Subject: [PATCH 078/136] Kill NOHS (cherry picked from commit a79316b3759f10de051edc5b6a632e1fce3632de) --- src/Makefile | 23 ----------------------- src/doomdef.h | 6 ------ src/sdl/Makefile.cfg | 22 ---------------------- 3 files changed, 51 deletions(-) diff --git a/src/Makefile b/src/Makefile index 3ce2dad13..91ce596a4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -62,7 +62,6 @@ # Generate compiler errors for most compiler warnings, add 'ERRORMODE=1' # Compile without NASM's tmap.nas, add 'NOASM=1' # Compile without 3D hardware support, add 'NOHW=1' -# Compile without 3D sound support, add 'NOHS=1' # Compile with GDBstubs, add 'RDB=1' # Compile without PNG, add 'NOPNG=1' # Compile without zlib, add 'NOZLIB=1' @@ -174,7 +173,6 @@ NOPNG=1 NOZLIB=1 NONET=1 NOHW=1 -NOHS=1 NOASM=1 NOIPX=1 EXENAME?=srb2dummy @@ -196,7 +194,6 @@ endif ifdef PANDORA NONX86=1 NOHW=1 -NOHS=1 endif ifdef DJGPPDOS @@ -281,13 +278,6 @@ else $(OBJDIR)/hw_md2load.o $(OBJDIR)/hw_md3load.o $(OBJDIR)/hw_model.o $(OBJDIR)/u_list.o $(OBJDIR)/hw_batching.o endif -ifdef NOHS - OPTS+=-DNOHS -else - OPTS+=-DHW3SOUND - OBJS+=$(OBJDIR)/hw3sound.o -endif - OPTS += -DCOMPVERSION ifndef NONX86 @@ -802,19 +792,6 @@ $(OBJDIR)/ogl_win.o: hardware/r_opengl/ogl_win.c hardware/r_opengl/r_opengl.h \ $(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@ endif -ifndef NOHS -$(OBJDIR)/s_ds3d.o: hardware/s_ds3d/s_ds3d.c hardware/hw3dsdrv.h \ - hardware/hw_dll.h - $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_ds3d.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_ds3d/s_ds3d.c - -$(OBJDIR)/s_fmod.o: hardware/s_openal/s_openal.c hardware/hw3dsdrv.h \ - hardware/hw_dll.h - $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_fmod.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_fmod/s_fmod.c - -$(OBJDIR)/s_openal.o: hardware/s_openal/s_openal.c hardware/hw3dsdrv.h \ - hardware/hw_dll.h - $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_openal.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_openal/s_openal.c -endif endif endif diff --git a/src/doomdef.h b/src/doomdef.h index 249bd5128..6cfaba8a1 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -29,7 +29,6 @@ // Use Mixer interface? #ifdef HAVE_MIXER #define SOUND SOUND_MIXER - #define NOHS // No HW3SOUND #ifdef HW3SOUND #undef HW3SOUND #endif @@ -45,7 +44,6 @@ // Use FMOD? #ifdef HAVE_FMOD #define SOUND SOUND_FMOD - #define NOHS // No HW3SOUND #ifdef HW3SOUND #undef HW3SOUND #endif @@ -62,10 +60,6 @@ #if !defined (HWRENDER) && !defined (NOHW) #define HWRENDER #endif -// judgecutor: 3D sound support -#if !defined(HW3SOUND) && !defined (NOHS) -#define HW3SOUND -#endif #endif #ifdef _WIN32 diff --git a/src/sdl/Makefile.cfg b/src/sdl/Makefile.cfg index 05b60f7a3..f1383a04f 100644 --- a/src/sdl/Makefile.cfg +++ b/src/sdl/Makefile.cfg @@ -53,28 +53,6 @@ ifndef NOHW OBJS+=$(OBJDIR)/r_opengl.o $(OBJDIR)/ogl_sdl.o endif -ifndef NOHS -ifdef OPENAL - OBJS+=$(OBJDIR)/s_openal.o - OPTS+=-DSTATIC3DS - STATICHS=1 -else -ifdef FMOD - OBJS+=$(OBJDIR)/s_fmod.o - OPTS+=-DSTATIC3DS - STATICHS=1 -else -ifdef MINGW -ifdef DS3D - OBJS+=$(OBJDIR)/s_ds3d.o - OPTS+=-DSTATIC3DS - STATICHS=1 -endif -endif -endif -endif -endif - ifdef NOMIXER i_sound_o=$(OBJDIR)/sdl_sound.o else From e4b7257ec939438e34d53e7fb41b82f3c7b60e6b Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 31 Aug 2020 16:09:41 -0700 Subject: [PATCH 079/136] Fix NOHW compiling --- src/d_main.c | 2 ++ src/sdl/i_video.c | 1 + 2 files changed, 3 insertions(+) diff --git a/src/d_main.c b/src/d_main.c index 96167ac0b..21b10de0a 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -629,6 +629,7 @@ static void D_Display(void) V_DrawThinString(80, 40, V_MONOSPACE | V_BLUEMAP, s); if (rendermode == render_opengl) // OpenGL specific stats { +#ifdef HWRENDER snprintf(s, sizeof s - 1, "nsrt %d", rs_hw_nodesorttime / divisor); V_DrawThinString(30, 40, V_MONOSPACE | V_YELLOWMAP, s); snprintf(s, sizeof s - 1, "ndrw %d", rs_hw_nodedrawtime / divisor); @@ -661,6 +662,7 @@ static void D_Display(void) snprintf(s, sizeof s - 1, "ncol %d", rs_hw_numcolors); V_DrawThinString(185, 30, V_MONOSPACE | V_PURPLEMAP, s); } +#endif } else // software specific stats { diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 01194a02f..5c5b6119c 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1478,6 +1478,7 @@ static SDL_bool Impl_CreateContext(void) void VID_CheckGLLoaded(rendermode_t oldrender) { + (void)oldrender; #ifdef HWRENDER if (vid_opengl_state == -1) // Well, it didn't work the first time anyway. { From 861118ac53eb99834f0adca9180a2f9f8b7e1906 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Tue, 1 Sep 2020 14:05:45 -0400 Subject: [PATCH 080/136] Allow the game to continue even if the `STARTUP` lump is somehow missing --- src/d_main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/d_main.c b/src/d_main.c index 4a93c852d..ca2f71a58 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -731,6 +731,7 @@ tic_t rendergametic; void D_SRB2Loop(void) { tic_t oldentertics = 0, entertic = 0, realtics = 0, rendertimeout = INFTICS; + static lumpnum_t gstartuplumpnum; if (dedicated) server = true; @@ -770,7 +771,12 @@ void D_SRB2Loop(void) */ /* Smells like a hack... Don't fade Sonic's ass into the title screen. */ if (gamestate != GS_TITLESCREEN) - V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(W_GetNumForName("STARTUP"), PU_PATCH)); + { + gstartuplumpnum = W_CheckNumForName("STARTUP"); + if (gstartuplumpnum == LUMPERROR) + gstartuplumpnum = W_GetNumForName("MISSING"); + V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(gstartuplumpnum, PU_PATCH)); + } for (;;) { From aa8f07af4122c091ad8498902ddfb39b0a401d48 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 1 Sep 2020 15:26:13 -0700 Subject: [PATCH 081/136] Update patch.pk3 asset hash --- src/config.h.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/config.h.in b/src/config.h.in index 595bea7b3..a6de7823d 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -32,12 +32,13 @@ * Last updated 2020 / 05 / 11 - v2.2.4 - patch.pk3 * Last updated 2020 / 07 / 07 - v2.2.5 - player.dta & patch.pk3 * Last updated 2020 / 07 / 10 - v2.2.6 - player.dta & patch.pk3 + * Last updated 2020 / 09 / 01 - v2.2.7 - patch.pk3 */ #define ASSET_HASH_SRB2_PK3 "0277c9416756627004e83cbb5b2e3e28" #define ASSET_HASH_ZONES_PK3 "f7e88afb6af7996a834c7d663144bead" #define ASSET_HASH_PLAYER_DTA "49dad7b24634c89728cc3e0b689e12bb" #ifdef USE_PATCH_DTA -#define ASSET_HASH_PATCH_PK3 "ecf00060f03c76b3e49c6ae3925b627f" +#define ASSET_HASH_PATCH_PK3 "97b440b48139c53fa666b585a4e208f2" #endif #endif From 1922a78a3b4ea57c51cd893e73e20ba7d5b59612 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 1 Sep 2020 15:37:32 -0700 Subject: [PATCH 082/136] Define BETAVERSION as a suffix to the version string --- src/doomdef.h | 4 ++++ src/version.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/doomdef.h b/src/doomdef.h index 24898bdf6..b1ce0b79c 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -130,7 +130,11 @@ extern char logfilename[1024]; // most interface strings are ignored in development mode. // we use comprevision and compbranch instead. #else +#ifdef BETAVERSION +#define VERSIONSTRING "v"SRB2VERSION" "BETAVERSION +#else #define VERSIONSTRING "v"SRB2VERSION +#endif // Hey! If you change this, add 1 to the MODVERSION below! // Otherwise we can't force updates! #endif diff --git a/src/version.h b/src/version.h index 31cf85bdc..dce681669 100644 --- a/src/version.h +++ b/src/version.h @@ -10,3 +10,6 @@ // Only set it higher, not lower, obviously. // Note that we use this to help keep internal testing in check; this is why v2.2.0 is not version "1". #define MODVERSION 47 + +// Define this as a prerelease version suffix +//#define BETAVERSION "RC1" From 605ea2c6680322e9c8537453fa01b7fcc3113045 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 1 Sep 2020 15:38:51 -0700 Subject: [PATCH 083/136] Update version to 2.2.7 RC1 --- src/version.h | 6 +++--- src/win32/Srb2win.rc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/version.h b/src/version.h index dce681669..2ef7116bb 100644 --- a/src/version.h +++ b/src/version.h @@ -1,4 +1,4 @@ -#define SRB2VERSION "2.2.6"/* this must be the first line, for cmake !! */ +#define SRB2VERSION "2.2.7"/* this must be the first line, for cmake !! */ // The Modification ID; must be obtained from a Master Server Admin ( https://mb.srb2.org/showgroups.php ). // DO NOT try to set this otherwise, or your modification will be unplayable through the Master Server. @@ -9,7 +9,7 @@ // it's only for detection of the version the player is using so the MS can alert them of an update. // Only set it higher, not lower, obviously. // Note that we use this to help keep internal testing in check; this is why v2.2.0 is not version "1". -#define MODVERSION 47 +#define MODVERSION 48 // Define this as a prerelease version suffix -//#define BETAVERSION "RC1" +#define BETAVERSION "RC1" diff --git a/src/win32/Srb2win.rc b/src/win32/Srb2win.rc index b90947a9e..293dd29bd 100644 --- a/src/win32/Srb2win.rc +++ b/src/win32/Srb2win.rc @@ -66,8 +66,8 @@ END #include "../doomdef.h" // Needed for version string VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,2,6,0 - PRODUCTVERSION 2,2,6,0 + FILEVERSION 2,2,7,0 + PRODUCTVERSION 2,2,7,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L From a7d7933c5a5b5fb4a952d1f18e6d19ef12c4355b Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 1 Sep 2020 16:22:29 -0700 Subject: [PATCH 084/136] Disable update alert if this is a prerelease --- src/doomdef.h | 2 ++ src/m_menu.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/doomdef.h b/src/doomdef.h index b1ce0b79c..22d98d792 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -157,7 +157,9 @@ extern char logfilename[1024]; // the other options the same. // Comment out this line to completely disable update alerts (recommended for testing, but not for release) +#ifndef BETAVERSION #define UPDATE_ALERT +#endif // The string used in the alert that pops up in the event of an update being available. // Please change to apply to your modification (we don't want everyone asking where your mod is on SRB2.org!). diff --git a/src/m_menu.c b/src/m_menu.c index d09ce6087..9b538c66a 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -11227,6 +11227,7 @@ Check_new_version_thread (int *id) GetRoomsList(hosting, *id); } } +#ifdef UPDATE_ALERT else { I_lock_mutex(&ms_QueryId_mutex); @@ -11235,6 +11236,7 @@ Check_new_version_thread (int *id) } I_unlock_mutex(ms_QueryId_mutex); } +#endif if (okay) { From e438d557208262469ebd6ba1c88d53d548eaed66 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 1 Sep 2020 18:51:47 -0700 Subject: [PATCH 085/136] Add SuperPhanto to the art credits --- src/f_finale.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/f_finale.c b/src/f_finale.c index e6765b020..a50f9fbe3 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1144,6 +1144,7 @@ static const char *credits[] = { "Wessel \"sphere\" Smit", "David \"Instant Sonic\" Spencer Jr.", "\"SSNTails\"", + "\"SuperPhanto\"", // for the new brak render "Daniel \"Inazuma\" Trinh", "\"VelocitOni\"", "Jarrett \"JEV3\" Voight", From c33f3e0cb222629dfd4da1ecb691d2a3ac121c7e Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 2 Sep 2020 17:59:53 -0700 Subject: [PATCH 086/136] Update SuperPhanto's name with real name per request --- src/f_finale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/f_finale.c b/src/f_finale.c index a50f9fbe3..b51e9c9a0 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1122,6 +1122,7 @@ static const char *credits[] = { "\1Art", "Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo -- sorry for our limited font! D: "Ryan \"Blaze Hedgehog\" Bloom", + "Graeme P. \"SuperPhanto\" Caldwell", // for the new brak render "\"ChrispyPixels\"", "Paul \"Boinciel\" Clempson", "Sally \"TehRealSalt\" Cochenour", @@ -1144,7 +1145,6 @@ static const char *credits[] = { "Wessel \"sphere\" Smit", "David \"Instant Sonic\" Spencer Jr.", "\"SSNTails\"", - "\"SuperPhanto\"", // for the new brak render "Daniel \"Inazuma\" Trinh", "\"VelocitOni\"", "Jarrett \"JEV3\" Voight", From 0b1f7792e9ce2f8cb62e7b0e3e3e64509ef0cf8d Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 5 Sep 2020 22:03:14 -0700 Subject: [PATCH 087/136] Start netid at 1 to avoid CV_FindNetVar returning a regular cvar for netid 0 --- src/command.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command.c b/src/command.c index 0a46839f3..d3e0e4969 100644 --- a/src/command.c +++ b/src/command.c @@ -1262,7 +1262,7 @@ void CV_RegisterVar(consvar_t *variable) // check net variables if (variable->flags & CV_NETVAR) { - variable->netid = consvar_number_of_netids++; + variable->netid = ++consvar_number_of_netids; /* in case of overflow... */ if (variable->netid > consvar_number_of_netids) From 2c303b935d69ee412cdbb1725e04c5c8ee2340b7 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sun, 6 Sep 2020 00:30:05 -0500 Subject: [PATCH 088/136] Remove BT_USE and PF_USEDOWN warning someone entertained the idea of maybe removing it, so might as well put this merge request up to see if people want it --- src/dehacked.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 4c7ffaa96..ae7ed5a22 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -10844,7 +10844,6 @@ static inline int lib_getenum(lua_State *L) } else if (fastcmp(p, "USEDOWN")) // Remove case when 2.3 nears release... { - LUA_Deprecated(L, "PF_USEDOWN", "PF_SPINDOWN"); lua_pushinteger(L, (lua_Integer)PF_SPINDOWN); return 1; } @@ -11116,7 +11115,6 @@ static inline int lib_getenum(lua_State *L) if (fastcmp(word, "BT_USE")) // Remove case when 2.3 nears release... { - LUA_Deprecated(L, "BT_USE", "BT_SPIN"); lua_pushinteger(L, (lua_Integer)BT_SPIN); return 1; } From 6c94b9d07e92f975b2bf2ad9311805990d362a74 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 5 Sep 2020 23:23:54 -0700 Subject: [PATCH 089/136] Fix netid overflow check --- src/command.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/command.c b/src/command.c index d3e0e4969..ded854e49 100644 --- a/src/command.c +++ b/src/command.c @@ -1262,12 +1262,12 @@ void CV_RegisterVar(consvar_t *variable) // check net variables if (variable->flags & CV_NETVAR) { - variable->netid = ++consvar_number_of_netids; - /* in case of overflow... */ - if (variable->netid > consvar_number_of_netids) + if (consvar_number_of_netids + 1 < consvar_number_of_netids) I_Error("Way too many netvars"); + variable->netid = ++consvar_number_of_netids; + #ifdef OLD22DEMOCOMPAT CV_RegisterOldDemoVar(variable); #endif From e77ebf1234fa718c304009f634278b506b65a561 Mon Sep 17 00:00:00 2001 From: lachwright Date: Sun, 6 Sep 2020 17:28:34 +0930 Subject: [PATCH 090/136] Objectplace improvements: - movement speeds are scaled with player scale - spawned objects are scaled with player scale - command accepts argument for thing num to set --- src/m_cheat.c | 130 ++++++++++++++++++++++++++++---------------------- src/p_local.h | 2 + src/p_mobj.c | 2 +- 3 files changed, 75 insertions(+), 59 deletions(-) diff --git a/src/m_cheat.c b/src/m_cheat.c index ab1454503..3a2bf79a8 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -1023,8 +1023,8 @@ static void OP_CycleThings(INT32 amt) states[S_OBJPLACE_DUMMY].frame = states[mobjinfo[op_currentthing].spawnstate].frame; } if (players[0].mo->eflags & MFE_VERTICALFLIP) // correct z when flipped - players[0].mo->z += players[0].mo->height - mobjinfo[op_currentthing].height; - players[0].mo->height = mobjinfo[op_currentthing].height; + players[0].mo->z += players[0].mo->height - FixedMul(mobjinfo[op_currentthing].height, players[0].mo->scale); + players[0].mo->height = FixedMul(mobjinfo[op_currentthing].height, players[0].mo->scale); P_SetPlayerMobjState(players[0].mo, S_OBJPLACE_DUMMY); op_currentdoomednum = mobjinfo[op_currentthing].doomednum; @@ -1107,6 +1107,7 @@ static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean c mt->angle = (INT16)(FixedInt(AngleFixed(player->mo->angle))); mt->options = (mt->z << ZSHIFT) | (UINT16)cv_opflags.value; + mt->scale = player->mo->scale; return mt; } @@ -1305,19 +1306,19 @@ void OP_ObjectplaceMovement(player_t *player) ticmiss++; if (cmd->buttons & BT_JUMP) - player->mo->z += FRACUNIT*cv_speed.value; + player->mo->z += player->mo->scale*cv_speed.value; else if (cmd->buttons & BT_SPIN) - player->mo->z -= FRACUNIT*cv_speed.value; + player->mo->z -= player->mo->scale*cv_speed.value; if (cmd->forwardmove != 0) { - P_Thrust(player->mo, player->mo->angle, (cmd->forwardmove*FRACUNIT/MAXPLMOVE)*cv_speed.value); + P_Thrust(player->mo, player->mo->angle, (cmd->forwardmove*player->mo->scale/MAXPLMOVE)*cv_speed.value); P_TeleportMove(player->mo, player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, player->mo->z); player->mo->momx = player->mo->momy = 0; } if (cmd->sidemove != 0) { - P_Thrust(player->mo, player->mo->angle-ANGLE_90, (cmd->sidemove*FRACUNIT/MAXPLMOVE)*cv_speed.value); + P_Thrust(player->mo, player->mo->angle-ANGLE_90, (cmd->sidemove*player->mo->scale/MAXPLMOVE)*cv_speed.value); P_TeleportMove(player->mo, player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, player->mo->z); player->mo->momx = player->mo->momy = 0; } @@ -1443,62 +1444,75 @@ void Command_ObjectPlace_f(void) G_SetGameModified(multiplayer); // Entering objectplace? - if (!objectplacing) + if (!objectplacing || COM_Argc() > 1) { - objectplacing = true; - - if (players[0].powers[pw_carry] == CR_NIGHTSMODE) - return; - - if (!COM_CheckParm("-silent")) + if (!objectplacing) { - HU_SetCEchoFlags(V_RETURN8|V_MONOSPACE|V_AUTOFADEOUT); - HU_SetCEchoDuration(10); - HU_DoCEcho(va(M_GetText( - "\\\\\\\\\\\\\\\\\\\\\\\\\x82" - " Objectplace Controls: \x80\\\\" - "Weapon Next/Prev: Cycle mapthings\\" - " Jump: Float up \\" - " Spin: Float down \\" - " Fire Ring: Place object \\"))); + objectplacing = true; + + if (players[0].powers[pw_carry] == CR_NIGHTSMODE) + return; + + if (!COM_CheckParm("-silent")) + { + HU_SetCEchoFlags(V_RETURN8|V_MONOSPACE|V_AUTOFADEOUT); + HU_SetCEchoDuration(10); + HU_DoCEcho(va(M_GetText( + "\\\\\\\\\\\\\\\\\\\\\\\\\x82" + " Objectplace Controls: \x80\\\\" + "Weapon Next/Prev: Cycle mapthings\\" + " Jump: Float up \\" + " Spin: Float down \\" + " Fire Ring: Place object \\"))); + } + + // Save all the player's data. + op_oldflags1 = players[0].mo->flags; + op_oldflags2 = players[0].mo->flags2; + op_oldeflags = players[0].mo->eflags; + op_oldpflags = players[0].pflags; + op_oldmomx = players[0].mo->momx; + op_oldmomy = players[0].mo->momy; + op_oldmomz = players[0].mo->momz; + op_oldheight = players[0].mo->height; + op_oldstate = S_PLAY_STND; + op_oldcolor = players[0].mo->color; // save color too in case of super/fireflower + + // Remove ALL flags and motion. + P_UnsetThingPosition(players[0].mo); + players[0].pflags = 0; + players[0].mo->flags2 = 0; + players[0].mo->eflags = 0; + players[0].mo->flags = (MF_NOCLIP|MF_NOGRAVITY|MF_NOBLOCKMAP); + players[0].mo->momx = players[0].mo->momy = players[0].mo->momz = 0; + P_SetThingPosition(players[0].mo); + + // Take away color so things display properly + players[0].mo->color = 0; + + // Like the classics, recover from death by entering objectplace + if (players[0].mo->health <= 0) + { + players[0].mo->health = 1; + players[0].deadtimer = 0; + op_oldflags1 = mobjinfo[MT_PLAYER].flags; + ++players[0].lives; + players[0].playerstate = PST_LIVE; + P_RestoreMusic(&players[0]); + } + else + op_oldstate = (statenum_t)(players[0].mo->state-states); } - // Save all the player's data. - op_oldflags1 = players[0].mo->flags; - op_oldflags2 = players[0].mo->flags2; - op_oldeflags = players[0].mo->eflags; - op_oldpflags = players[0].pflags; - op_oldmomx = players[0].mo->momx; - op_oldmomy = players[0].mo->momy; - op_oldmomz = players[0].mo->momz; - op_oldheight = players[0].mo->height; - op_oldstate = S_PLAY_STND; - op_oldcolor = players[0].mo->color; // save color too in case of super/fireflower - - // Remove ALL flags and motion. - P_UnsetThingPosition(players[0].mo); - players[0].pflags = 0; - players[0].mo->flags2 = 0; - players[0].mo->eflags = 0; - players[0].mo->flags = (MF_NOCLIP|MF_NOGRAVITY|MF_NOBLOCKMAP); - players[0].mo->momx = players[0].mo->momy = players[0].mo->momz = 0; - P_SetThingPosition(players[0].mo); - - // Take away color so things display properly - players[0].mo->color = 0; - - // Like the classics, recover from death by entering objectplace - if (players[0].mo->health <= 0) + if (COM_Argc() > 1) { - players[0].mo->health = 1; - players[0].deadtimer = 0; - op_oldflags1 = mobjinfo[MT_PLAYER].flags; - ++players[0].lives; - players[0].playerstate = PST_LIVE; - P_RestoreMusic(&players[0]); + UINT16 mapthingnum = atoi(COM_Argv(1)); + mobjtype_t type = P_GetMobjtype(mapthingnum); + if (type == MT_UNKNOWN) + CONS_Printf(M_GetText("No mobj type delegated to thing type %d.\n"), mapthingnum); + else + op_currentthing = type; } - else - op_oldstate = (statenum_t)(players[0].mo->state-states); // If no thing set, then cycle a little if (!op_currentthing) @@ -1506,8 +1520,8 @@ void Command_ObjectPlace_f(void) op_currentthing = 1; OP_CycleThings(1); } - else // Cycle things sets this for the former. - players[0].mo->height = mobjinfo[op_currentthing].height; + else + OP_CycleThings(0); // sets all necessary height values without cycling op_currentthing P_SetPlayerMobjState(players[0].mo, S_OBJPLACE_DUMMY); } diff --git a/src/p_local.h b/src/p_local.h index 4077fecf6..cf3a66e9d 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -274,6 +274,8 @@ extern tic_t itemrespawntime[ITEMQUESIZE]; extern size_t iquehead, iquetail; extern consvar_t cv_gravity, cv_movebob; +mobjtype_t P_GetMobjtype(UINT16 mthingtype); + void P_RespawnSpecials(void); mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); diff --git a/src/p_mobj.c b/src/p_mobj.c index fcba1f690..1e8fe02c1 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11249,7 +11249,7 @@ void P_PrecipitationEffects(void) * \param mthingtype Mapthing number in question. * \return Mobj type; MT_UNKNOWN if nothing found. */ -static mobjtype_t P_GetMobjtype(UINT16 mthingtype) +mobjtype_t P_GetMobjtype(UINT16 mthingtype) { mobjtype_t i; for (i = 0; i < NUMMOBJTYPES; i++) From c4a4608236c41b2d3c7ac01efd5b17aa86d7b045 Mon Sep 17 00:00:00 2001 From: lachwright Date: Sun, 6 Sep 2020 19:03:17 +0930 Subject: [PATCH 091/136] Let analog control schemes control objectplace like standard control schemes --- src/g_game.h | 3 ++- src/m_cheat.c | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/g_game.h b/src/g_game.h index c8abe560c..26f60e125 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -18,6 +18,7 @@ #include "doomstat.h" #include "d_event.h" #include "g_demo.h" +#include "m_cheat.h" // objectplacing extern char gamedatafilename[64]; extern char timeattackfolder[64]; @@ -64,7 +65,7 @@ typedef enum { CS_STANDARD, CS_SIMPLE = CS_LMAOGALOG|CS_STANDARD, } controlstyle_e; -#define G_ControlStyle(ssplayer) (cv_directionchar[(ssplayer)-1].value == 3 ? CS_LMAOGALOG : ((cv_analog[(ssplayer)-1].value ? CS_LMAOGALOG : 0) | (cv_directionchar[(ssplayer)-1].value ? CS_STANDARD : 0))) +#define G_ControlStyle(ssplayer) (cv_directionchar[(ssplayer)-1].value == 3 ? CS_LMAOGALOG : ((!objectplacing && cv_analog[(ssplayer)-1].value ? CS_LMAOGALOG : 0) | (cv_directionchar[(ssplayer)-1].value ? CS_STANDARD : 0))) #define P_ControlStyle(player) ((((player)->pflags & PF_ANALOGMODE) ? CS_LMAOGALOG : 0) | (((player)->pflags & PF_DIRECTIONCHAR) ? CS_STANDARD : 0)) extern consvar_t cv_autobrake, cv_autobrake2; diff --git a/src/m_cheat.c b/src/m_cheat.c index 3a2bf79a8..2fe4552ff 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -1298,8 +1298,7 @@ void OP_ObjectplaceMovement(player_t *player) { ticcmd_t *cmd = &player->cmd; - if (!player->climbing && (netgame || !cv_analog[0].value || (player->pflags & PF_SPINNING))) - player->drawangle = player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */); + player->drawangle = player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */); ticruned++; if (!(cmd->angleturn & TICCMD_RECEIVED)) From 89eccd7e874b41e2effbce27f8a7f07f72cc1db8 Mon Sep 17 00:00:00 2001 From: lachwright Date: Mon, 7 Sep 2020 13:08:22 +0930 Subject: [PATCH 092/136] Set additional UDMF parameters on objectplaced mapthings to defaults --- src/m_cheat.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/m_cheat.c b/src/m_cheat.c index 2fe4552ff..a3f6d3daf 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -1108,6 +1108,10 @@ static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean c mt->options = (mt->z << ZSHIFT) | (UINT16)cv_opflags.value; mt->scale = player->mo->scale; + mt->tag = 0; + memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args)); + memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs)); + mt->pitch = mt->roll = 0; return mt; } From 92f8fb360e0e2ac6a98eac2f18e8ef5941b30510 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Wed, 9 Sep 2020 22:19:14 -0500 Subject: [PATCH 093/136] Can we just do this please? Thanks. --- src/blua/lstrlib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/blua/lstrlib.c b/src/blua/lstrlib.c index 297504e95..af933d25a 100644 --- a/src/blua/lstrlib.c +++ b/src/blua/lstrlib.c @@ -19,6 +19,7 @@ #include "lauxlib.h" #include "lualib.h" +#include "../m_fixed.h" /* macro to `unsign' a character */ #define uchar(c) ((unsigned char)(c)) @@ -790,7 +791,7 @@ static int str_format (lua_State *L) { case 'e': case 'E': case 'f': case 'g': case 'G': { lua_Number n = luaL_checknumber(L, arg); - sprintf(buff, form, (double)n); + sprintf(buff, form, (double)n / FRACUNIT); break; } case 'q': { From bf521254949c9bb28406aa34f4c9dac182c75c1a Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Thu, 10 Sep 2020 01:43:46 -0300 Subject: [PATCH 094/136] Change method of color look-up table generation --- src/m_anigif.c | 4 ++-- src/v_video.c | 57 ++++++++++++++++++++++++++++++++++++-------------- src/v_video.h | 15 ++++++++----- 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/src/m_anigif.c b/src/m_anigif.c index 83bc3dddc..8113e20d8 100644 --- a/src/m_anigif.c +++ b/src/m_anigif.c @@ -505,14 +505,14 @@ static void GIF_rgbconvert(UINT8 *linear, UINT8 *scr) size_t src = 0, dest = 0; size_t size = (vid.width * vid.height * 3); - InitColorLUT(gif_framepalette); + InitColorLUT(gif_framepalette, true); while (src < size) { r = (UINT8)linear[src]; g = (UINT8)linear[src + 1]; b = (UINT8)linear[src + 2]; - scr[dest] = colorlookup[r >> SHIFTCOLORBITS][g >> SHIFTCOLORBITS][b >> SHIFTCOLORBITS]; + scr[dest] = GetColorLUTDirect(r, g, b); src += (3 * scrbuf_downscaleamt); dest += scrbuf_downscaleamt; } diff --git a/src/v_video.c b/src/v_video.c index b88c4838b..3ac694dd4 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -3666,28 +3666,53 @@ Unoptimized version #endif } -// Generates a color look-up table -// which has up to 64 colors at each channel -// (see the defines in v_video.h) +// Generates a RGB565 color look-up table +static colorlookup_t colorlookup; -UINT8 colorlookup[CLUTSIZE][CLUTSIZE][CLUTSIZE]; - -void InitColorLUT(RGBA_t *palette) +void InitColorLUT(RGBA_t *palette, boolean makecolors) { - UINT8 r, g, b; - static boolean clutinit = false; - static RGBA_t *lastpalette = NULL; - if ((!clutinit) || (lastpalette != palette)) + size_t palsize = (sizeof(RGBA_t) * 256); + + if (!colorlookup.init || memcmp(colorlookup.palette, palette, palsize)) { - for (r = 0; r < CLUTSIZE; r++) - for (g = 0; g < CLUTSIZE; g++) - for (b = 0; b < CLUTSIZE; b++) - colorlookup[r][g][b] = NearestPaletteColor(r << SHIFTCOLORBITS, g << SHIFTCOLORBITS, b << SHIFTCOLORBITS, palette); - clutinit = true; - lastpalette = palette; + INT32 i; + + colorlookup.init = true; + memcpy(colorlookup.palette, palette, palsize); + + for (i = 0; i < 0xFFFF; i++) + colorlookup.table[i] = 0xFFFF; + + if (makecolors) + { + UINT8 r, g, b; + + for (r = 0; r < 0xFF; r++) + for (g = 0; g < 0xFF; g++) + for (b = 0; b < 0xFF; b++) + { + i = CLUTINDEX(r, g, b); + if (colorlookup.table[i] == 0xFFFF) + colorlookup.table[i] = NearestPaletteColor(r, g, b, palette); + } + } } } +UINT8 GetColorLUT(UINT8 r, UINT8 g, UINT8 b) +{ + INT32 i = CLUTINDEX(r, g, b); + if (colorlookup.table[i] == 0xFFFF) + colorlookup.table[i] = NearestPaletteColor(r << 3, g << 2, b << 3, colorlookup.palette); + return colorlookup.table[i]; +} + +UINT8 GetColorLUTDirect(UINT8 r, UINT8 g, UINT8 b) +{ + INT32 i = CLUTINDEX(r, g, b); + return colorlookup.table[i]; +} + // V_Init // old software stuff, buffers are allocated at video mode setup // here we set the screens[x] pointers accordingly diff --git a/src/v_video.h b/src/v_video.h index 9f7a9a9e9..6cd10f606 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -38,13 +38,18 @@ cv_allcaps; void V_Init(void); // Color look-up table -#define COLORBITS 6 -#define SHIFTCOLORBITS (8-COLORBITS) -#define CLUTSIZE (1<> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3) -extern UINT8 colorlookup[CLUTSIZE][CLUTSIZE][CLUTSIZE]; +void InitColorLUT(RGBA_t *palette, boolean makecolors); +UINT8 GetColorLUT(UINT8 r, UINT8 g, UINT8 b); +UINT8 GetColorLUTDirect(UINT8 r, UINT8 g, UINT8 b); -void InitColorLUT(RGBA_t *palette); +typedef struct +{ + boolean init; + RGBA_t palette[256]; + UINT16 table[0xFFFF]; +} colorlookup_t; // Set the current RGB palette lookup to use for palettized graphics void V_SetPalette(INT32 palettenum); From 15eb91be69bcf069fd95f206bf10ad62561c8ab5 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Thu, 10 Sep 2020 02:10:31 -0300 Subject: [PATCH 095/136] Use color look-up table for PNG conversion --- src/m_anigif.c | 6 ++++-- src/r_picformats.c | 13 +++++++++++++ src/r_picformats.h | 2 ++ src/v_video.c | 28 +++++++++++++--------------- src/v_video.h | 8 ++++---- 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/m_anigif.c b/src/m_anigif.c index 8113e20d8..7c2bb359e 100644 --- a/src/m_anigif.c +++ b/src/m_anigif.c @@ -499,20 +499,22 @@ static size_t gifframe_size = 8192; // converts an RGB frame to a frame with a palette. // #ifdef HWRENDER +static colorlookup_t gif_colorlookup; + static void GIF_rgbconvert(UINT8 *linear, UINT8 *scr) { UINT8 r, g, b; size_t src = 0, dest = 0; size_t size = (vid.width * vid.height * 3); - InitColorLUT(gif_framepalette, true); + InitColorLUT(&gif_colorlookup, gif_framepalette, true); while (src < size) { r = (UINT8)linear[src]; g = (UINT8)linear[src + 1]; b = (UINT8)linear[src + 2]; - scr[dest] = GetColorLUTDirect(r, g, b); + scr[dest] = GetColorLUTDirect(&gif_colorlookup, r, g, b); src += (3 * scrbuf_downscaleamt); dest += scrbuf_downscaleamt; } diff --git a/src/r_picformats.c b/src/r_picformats.c index 39aaeef64..dbd090fc7 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -52,6 +52,10 @@ static unsigned char imgbuf[1<<26]; +#ifdef PICTURE_PNG_USELOOKUP +static colorlookup_t png_colorlookup; +#endif + /** Converts a picture between two formats. * * \param informat Input picture format. @@ -964,6 +968,11 @@ void *Picture_PNGConvert( if (outbpp == PICDEPTH_8BPP) memset(flat, TRANSPARENTPIXEL, (width * height)); +#ifdef PICTURE_PNG_USELOOKUP + if (outbpp != PICDEPTH_32BPP) + InitColorLUT(&png_colorlookup, pMasterPalette, false); +#endif + for (y = 0; y < height; y++) { png_bytep row = row_pointers[y]; @@ -988,7 +997,11 @@ void *Picture_PNGConvert( } else { +#ifdef PICTURE_PNG_USELOOKUP + UINT8 palidx = GetColorLUT(&png_colorlookup, red, green, blue); +#else UINT8 palidx = NearestColor(red, green, blue); +#endif if (outbpp == PICDEPTH_16BPP) { UINT16 *outflat = (UINT16 *)flat; diff --git a/src/r_picformats.h b/src/r_picformats.h index 58c84b2c8..32754d64e 100644 --- a/src/r_picformats.h +++ b/src/r_picformats.h @@ -113,6 +113,8 @@ void *Picture_PNGConvert( boolean Picture_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size); #endif +#define PICTURE_PNG_USELOOKUP + // SpriteInfo extern spriteinfo_t spriteinfo[NUMSPRITES]; void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps); diff --git a/src/v_video.c b/src/v_video.c index 3ac694dd4..26403b520 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -3667,21 +3667,19 @@ Unoptimized version } // Generates a RGB565 color look-up table -static colorlookup_t colorlookup; - -void InitColorLUT(RGBA_t *palette, boolean makecolors) +void InitColorLUT(colorlookup_t *lut, RGBA_t *palette, boolean makecolors) { size_t palsize = (sizeof(RGBA_t) * 256); - if (!colorlookup.init || memcmp(colorlookup.palette, palette, palsize)) + if (!lut->init || memcmp(lut->palette, palette, palsize)) { INT32 i; - colorlookup.init = true; - memcpy(colorlookup.palette, palette, palsize); + lut->init = true; + memcpy(lut->palette, palette, palsize); for (i = 0; i < 0xFFFF; i++) - colorlookup.table[i] = 0xFFFF; + lut->table[i] = 0xFFFF; if (makecolors) { @@ -3692,25 +3690,25 @@ void InitColorLUT(RGBA_t *palette, boolean makecolors) for (b = 0; b < 0xFF; b++) { i = CLUTINDEX(r, g, b); - if (colorlookup.table[i] == 0xFFFF) - colorlookup.table[i] = NearestPaletteColor(r, g, b, palette); + if (lut->table[i] == 0xFFFF) + lut->table[i] = NearestPaletteColor(r, g, b, palette); } } } } -UINT8 GetColorLUT(UINT8 r, UINT8 g, UINT8 b) +UINT8 GetColorLUT(colorlookup_t *lut, UINT8 r, UINT8 g, UINT8 b) { INT32 i = CLUTINDEX(r, g, b); - if (colorlookup.table[i] == 0xFFFF) - colorlookup.table[i] = NearestPaletteColor(r << 3, g << 2, b << 3, colorlookup.palette); - return colorlookup.table[i]; + if (lut->table[i] == 0xFFFF) + lut->table[i] = NearestPaletteColor(r, g, b, lut->palette); + return lut->table[i]; } -UINT8 GetColorLUTDirect(UINT8 r, UINT8 g, UINT8 b) +UINT8 GetColorLUTDirect(colorlookup_t *lut, UINT8 r, UINT8 g, UINT8 b) { INT32 i = CLUTINDEX(r, g, b); - return colorlookup.table[i]; + return lut->table[i]; } // V_Init diff --git a/src/v_video.h b/src/v_video.h index 6cd10f606..a6ca197bb 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -40,10 +40,6 @@ void V_Init(void); // Color look-up table #define CLUTINDEX(r, g, b) (((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3) -void InitColorLUT(RGBA_t *palette, boolean makecolors); -UINT8 GetColorLUT(UINT8 r, UINT8 g, UINT8 b); -UINT8 GetColorLUTDirect(UINT8 r, UINT8 g, UINT8 b); - typedef struct { boolean init; @@ -51,6 +47,10 @@ typedef struct UINT16 table[0xFFFF]; } colorlookup_t; +void InitColorLUT(colorlookup_t *lut, RGBA_t *palette, boolean makecolors); +UINT8 GetColorLUT(colorlookup_t *lut, UINT8 r, UINT8 g, UINT8 b); +UINT8 GetColorLUTDirect(colorlookup_t *lut, UINT8 r, UINT8 g, UINT8 b); + // Set the current RGB palette lookup to use for palettized graphics void V_SetPalette(INT32 palettenum); From a8d351095819b23813600fc5646feef8de8bad6d Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Thu, 10 Sep 2020 03:16:21 -0300 Subject: [PATCH 096/136] Attempt to use the PNG image's palette, if it is present --- src/r_picformats.c | 193 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 163 insertions(+), 30 deletions(-) diff --git a/src/r_picformats.c b/src/r_picformats.c index dbd090fc7..d48bbaaf2 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -798,13 +798,24 @@ static void PNG_warn(png_structp PNG, png_const_charp pngtext) CONS_Debug(DBG_RENDER, "libpng warning at %p: %s", PNG, pngtext); } -static png_bytep *PNG_Read(const UINT8 *png, INT32 *w, INT32 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) +static png_bytep *PNG_Read( + const UINT8 *png, + INT32 *w, INT32 *h, INT16 *topoffset, INT16 *leftoffset, + boolean *use_palette, size_t size) { png_structp png_ptr; png_infop png_info_ptr; png_uint_32 width, height; int bit_depth, color_type; png_uint_32 y; + + png_colorp palette; + int palette_size; + + png_bytep trans; + int trans_num; + png_color_16p trans_values; + #ifdef PNG_SETJMP_SUPPORTED #ifdef USE_FAR_KEYWORD jmp_buf jmpbuf; @@ -864,10 +875,48 @@ static png_bytep *PNG_Read(const UINT8 *png, INT32 *w, INT32 *h, INT16 *topoffse if (bit_depth == 16) png_set_strip_16(png_ptr); + palette = NULL; + *use_palette = false; + if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); else if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(png_ptr); + { + boolean usepal = false; + + // Lactozilla: Check if the PNG has a palette, and if its color count + // matches the color count of SRB2's palette: 256 colors. + if (png_get_PLTE(png_ptr, png_info_ptr, &palette, &palette_size)) + { + if (palette_size == 256) + usepal = true; + } + + // If any of the tRNS colors have an alpha lower than 0xFF, and that + // color is present on the image, the palette flag is disabled. + png_get_tRNS(png_ptr, png_info_ptr, &trans, &trans_num, &trans_values); + + if (trans_num == 256) + { + int i; + for (i = 0; i < trans_num; i++) + { + // libpng will transform this image into RGB even if + // the transparent index does not exist in the image, + // and there is no way around that. + if (trans[i] < 0xFF) + { + usepal = false; + break; + } + } + } + + if (usepal) + *use_palette = true; + else + png_set_palette_to_rgb(png_ptr); + } if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); @@ -908,6 +957,7 @@ static png_bytep *PNG_Read(const UINT8 *png, INT32 *w, INT32 *h, INT16 *topoffse *w = (INT32)width; *h = (INT32)height; + return row_pointers; } @@ -935,12 +985,17 @@ void *Picture_PNGConvert( INT32 outbpp; size_t flatsize; png_uint_32 x, y; - png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, insize); + png_bytep row; + boolean palette = false; + png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, &palette, insize); png_uint_32 width = *w, height = *h; if (png == NULL) I_Error("Picture_PNGConvert: picture was NULL!"); + if (row_pointers == NULL) + I_Error("Picture_PNGConvert: row_pointers was NULL!"); + // Find the output format's bits per pixel amount outbpp = Picture_FormatBPP(outformat); @@ -973,43 +1028,119 @@ void *Picture_PNGConvert( InitColorLUT(&png_colorlookup, pMasterPalette, false); #endif - for (y = 0; y < height; y++) + if (outbpp == PICDEPTH_32BPP) { - png_bytep row = row_pointers[y]; - for (x = 0; x < width; x++) + RGBA_t out; + UINT32 *outflat = (UINT32 *)flat; + + if (palette) { - png_bytep px = &(row[x * 4]); - if ((UINT8)px[3]) + for (y = 0; y < height; y++) { - UINT8 red = (UINT8)px[0]; - UINT8 green = (UINT8)px[1]; - UINT8 blue = (UINT8)px[2]; - UINT8 alpha = (UINT8)px[3]; - if (outbpp == PICDEPTH_32BPP) + row = row_pointers[y]; + for (x = 0; x < width; x++) { - UINT32 *outflat = (UINT32 *)flat; - RGBA_t out; - out.s.red = red; - out.s.green = green; - out.s.blue = blue; - out.s.alpha = alpha; + out = V_GetColor(row[x]); outflat[((y * width) + x)] = out.rgba; } - else + } + } + else + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) { -#ifdef PICTURE_PNG_USELOOKUP - UINT8 palidx = GetColorLUT(&png_colorlookup, red, green, blue); -#else - UINT8 palidx = NearestColor(red, green, blue); -#endif - if (outbpp == PICDEPTH_16BPP) + png_bytep px = &(row[x * 4]); + if ((UINT8)px[3]) { - UINT16 *outflat = (UINT16 *)flat; - outflat[((y * width) + x)] = (alpha << 8) | palidx; + out.s.red = (UINT8)px[0]; + out.s.green = (UINT8)px[1]; + out.s.blue = (UINT8)px[2]; + out.s.alpha = (UINT8)px[3]; + outflat[((y * width) + x)] = out.rgba; } - else // 8bpp + else + outflat[((y * width) + x)] = 0x00000000; + } + } + } + } + else if (outbpp == PICDEPTH_16BPP) + { + UINT16 *outflat = (UINT16 *)flat; + + if (palette) + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + outflat[((y * width) + x)] = (0xFF << 8) | row[x]; + } + } + else + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + { + png_bytep px = &(row[x * 4]); + UINT8 red = (UINT8)px[0]; + UINT8 green = (UINT8)px[1]; + UINT8 blue = (UINT8)px[2]; + UINT8 alpha = (UINT8)px[3]; + + if (alpha) { - UINT8 *outflat = (UINT8 *)flat; +#ifdef PICTURE_PNG_USELOOKUP + UINT8 palidx = GetColorLUT(&png_colorlookup, red, green, blue); +#else + UINT8 palidx = NearestColor(red, green, blue); +#endif + outflat[((y * width) + x)] = (0xFF << 8) | palidx; + } + else + outflat[((y * width) + x)] = 0x0000; + } + } + } + } + else // 8bpp + { + UINT8 *outflat = (UINT8 *)flat; + + if (palette) + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + outflat[((y * width) + x)] = row[x]; + } + } + else + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + { + png_bytep px = &(row[x * 4]); + UINT8 red = (UINT8)px[0]; + UINT8 green = (UINT8)px[1]; + UINT8 blue = (UINT8)px[2]; + UINT8 alpha = (UINT8)px[3]; + + if (alpha) + { +#ifdef PICTURE_PNG_USELOOKUP + UINT8 palidx = GetColorLUT(&png_colorlookup, red, green, blue); +#else + UINT8 palidx = NearestColor(red, green, blue); +#endif outflat[((y * width) + x)] = palidx; } } @@ -1018,6 +1149,8 @@ void *Picture_PNGConvert( } // Free the row pointers that we allocated for libpng. + for (y = 0; y < height; y++) + free(row_pointers[y]); free(row_pointers); // But wait, there's more! From 58d4045b93f832121a0956fcc4d393309ad2d4c6 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 14 Sep 2020 22:54:07 -0400 Subject: [PATCH 097/136] Make "divide by zero" or "modulo by zero" errors show the file and line of where it happened Shouldn't break scripts as it stops execution as soon it happens, like it already does. --- src/blua/lvm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/blua/lvm.c b/src/blua/lvm.c index b654613f4..46a015c1e 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -322,8 +322,8 @@ static void Arith (lua_State *L, StkId ra, TValue *rb, case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break; case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break; case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break; - case TM_DIV: if (nc == 0) { lua_pushliteral(L, "divide by zero error"); lua_error(L); } else setnvalue(ra, luai_numdiv(nb, nc)); break; - case TM_MOD: if (nc == 0) { lua_pushliteral(L, "modulo by zero error"); lua_error(L); } else setnvalue(ra, luai_nummod(nb, nc)); break; + case TM_DIV: if (nc == 0) { luaG_runerror(L, "divide by zero error"); } else setnvalue(ra, luai_numdiv(nb, nc)); break; + case TM_MOD: if (nc == 0) { luaG_runerror(L, "modulo by zero error"); } else setnvalue(ra, luai_nummod(nb, nc)); break; case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break; case TM_UNM: setnvalue(ra, luai_numunm(nb)); break; case TM_AND: setnvalue(ra, luai_numand(nb, nc)); break; @@ -492,8 +492,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { if (ttisnumber(rb) && ttisnumber(rc)) { lua_Number nb = nvalue(rb), nc = nvalue(rc); if (nc == 0) { - lua_pushliteral(L, "divide by zero error"); - lua_error(L); + luaG_runerror(L, "divide by zero error"); } else setnvalue(ra, luai_numdiv(nb, nc)); @@ -508,8 +507,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { if (ttisnumber(rb) && ttisnumber(rc)) { lua_Number nb = nvalue(rb), nc = nvalue(rc); if (nc == 0) { - lua_pushliteral(L, "modulo by zero error"); - lua_error(L); + luaG_runerror(L, "modulo by zero error"); } else setnvalue(ra, luai_nummod(nb, nc)); From 2d3eae9ee1b46620f65bb2f331cd0935ee54d2c2 Mon Sep 17 00:00:00 2001 From: lachwright Date: Thu, 17 Sep 2020 19:00:49 +0930 Subject: [PATCH 098/136] Save changes made to the bot's skin, and display it properly in save select --- src/m_menu.c | 2 +- src/r_skins.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index 49e8211d0..932d368da 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8483,7 +8483,7 @@ static void M_DrawLoadGameData(void) sprdef = &charbotskin->sprites[SPR2_SIGN]; if (!sprdef->numframes) goto skipbot; - colormap = R_GetTranslationColormap(savegameinfo[savetodraw].botskin, charbotskin->prefcolor, 0); + colormap = R_GetTranslationColormap(savegameinfo[savetodraw].botskin-1, charbotskin->prefcolor, 0); sprframe = &sprdef->spriteframes[0]; patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH); diff --git a/src/r_skins.c b/src/r_skins.c index a4fe1982f..25904e95e 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -286,6 +286,11 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) else if (playernum == secondarydisplayplayer) CV_StealthSetValue(&cv_playercolor2, skin->prefcolor); player->skincolor = newcolor = skin->prefcolor; + if (player->bot && botingame) + { + botskin = (UINT8)(skinnum + 1); + botcolor = skin->prefcolor; + } } if (player->followmobj) From 87d71bf8117f3dd24a97712c917103652d686549 Mon Sep 17 00:00:00 2001 From: kaysrishaq <62462173+kaysrishaq@users.noreply.github.com> Date: Fri, 18 Sep 2020 17:47:38 -0400 Subject: [PATCH 099/136] Update lua_infolib.c --- src/lua_infolib.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 8143bda89..9a6153bb3 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -1542,8 +1542,10 @@ static int lib_setSkinColor(lua_State *L) strlcpy(info->name, n, MAXCOLORNAME+1); if (strlen(n) > MAXCOLORNAME) CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') longer than %d chars; clipped to %s.\n", n, MAXCOLORNAME, info->name); +#if 0 if (strchr(info->name, ' ') != NULL) CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') contains spaces.\n", info->name); +#endif if (info->name[0] != '\0') // don't check empty string for dupe { @@ -1633,8 +1635,10 @@ static int skincolor_set(lua_State *L) strlcpy(info->name, n, MAXCOLORNAME+1); if (strlen(n) > MAXCOLORNAME) CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') longer than %d chars; clipped to %s.\n", n, MAXCOLORNAME, info->name); +#if 0 if (strchr(info->name, ' ') != NULL) CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') contains spaces.\n", info->name); +#endif if (info->name[0] != '\0') // don't check empty string for dupe { From d99757278b9f63b1426ea418a4847624e63b7c2a Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 19 Sep 2020 18:34:52 -0700 Subject: [PATCH 100/136] Don't ifdef CV_LoadDemoVars out if OLD22DEMOCOMPAT defined --- src/g_demo.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 57a955cb1..a2a9cb907 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -1933,9 +1933,8 @@ void G_DoPlayDemo(char *defdemoname) if (use_old_demo_vars) CV_LoadOldDemoVars(&demo_p); else -#else - CV_LoadDemoVars(&demo_p); #endif + CV_LoadDemoVars(&demo_p); // Sigh ... it's an empty demo. if (*demo_p == DEMOMARKER) From ec8eb478529cbf783b2f0baeb0a614965919a24d Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 19 Sep 2020 18:54:04 -0700 Subject: [PATCH 101/136] Shitty decode listserv --- src/http-mserv.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/http-mserv.c b/src/http-mserv.c index 1e8d35648..a76827184 100644 --- a/src/http-mserv.c +++ b/src/http-mserv.c @@ -472,6 +472,7 @@ HMS_list_servers (void) { struct HMS_buffer *hms; + char *list; char *p; hms = HMS_connect("servers"); @@ -481,11 +482,17 @@ HMS_list_servers (void) if (HMS_do(hms)) { - p = &hms->buffer[strlen(hms->buffer)]; - while (*--p == '\n') - ; + list = curl_easy_unescape(hms->curl, hms->buffer, 0, NULL); - CONS_Printf("%s\n", hms->buffer); + p = strtok(list, "\n"); + + while (p != NULL) + { + CONS_Printf("\x80%s\n", p); + p = strtok(NULL, "\n"); + } + + curl_free(list); } HMS_end(hms); From 5301255032f75683ea3fd7a625325fffd93cdbf1 Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Sat, 19 Sep 2020 23:10:03 -0300 Subject: [PATCH 102/136] Ignore spectators --- src/p_mobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 1e8fe02c1..badf19372 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4194,7 +4194,7 @@ boolean P_SupermanLook4Players(mobj_t *actor) for (c = 0; c < MAXPLAYERS; c++) { - if (playeringame[c]) + if (playeringame[c] && !players[c].spectator) { if (players[c].pflags & PF_INVIS) continue; // ignore notarget From 511518f0e48505657af7223d96ee24b54bd0d3ae Mon Sep 17 00:00:00 2001 From: lachwright Date: Sun, 20 Sep 2020 11:49:53 +0930 Subject: [PATCH 103/136] Only allow explicitly living players to trigger sector specials --- src/p_spec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_spec.c b/src/p_spec.c index 2b2a5884a..189622c35 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4395,7 +4395,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers // Ignore dead players. // If this strange phenomenon could be potentially used in levels, // TODO: modify this to accommodate for it. - if (player->playerstate == PST_DEAD) + if (player->playerstate != PST_LIVE) return; // Conveyor stuff From bf857e1c59f55ab6ba97bc46ef28ff0ab1df847c Mon Sep 17 00:00:00 2001 From: Zippy_Zolton Date: Sun, 20 Sep 2020 13:07:28 -0500 Subject: [PATCH 104/136] Add-on clarity from Kart --- src/d_clisrv.c | 10 +++++----- src/m_menu.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 9a636dd45..a467a7a02 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2513,11 +2513,11 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) CL_Reset(); D_StartTitle(); M_StartMessage(M_GetText( - "You have WAD files loaded or have\n" - "modified the game in some way, and\n" - "your file list does not match\n" - "the server's file list.\n" - "Please restart SRB2 before connecting.\n\n" + "You have the wrong addons loaded.\n\n" + "To play on this server, restart\n" + "the game and don't load any addons.\n" + "SRB2 will automatically add\n" + "everything you need when you join.\n\n" "Press ESC\n" ), NULL, MM_NOTHING); return false; diff --git a/src/m_menu.c b/src/m_menu.c index 49e8211d0..889328e6e 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -11279,7 +11279,7 @@ static void M_ConnectMenuModChecks(INT32 choice) if (modifiedgame) { - M_StartMessage(M_GetText("Add-ons are currently loaded.\n\nYou will only be able to join a server if\nit has the same ones loaded in the same order, which may be unlikely.\n\nIf you wish to play on other servers,\nrestart the game to clear existing add-ons.\n\n(Press a key)\n"),M_ConnectMenu,MM_EVENTHANDLER); + M_StartMessage(M_GetText("You have add-ons loaded.\nYou won't be able to join netgames!\n\nTo play online, restart the game\nand don't load any addons.\nSRB2 will automatically add\neverything you need when you join.\n\n(Press a key)\n"),M_ConnectMenu,MM_EVENTHANDLER); return; } From 4c5ea92516aa0254b317684bfaa7eaa5c5c85761 Mon Sep 17 00:00:00 2001 From: lachwright Date: Mon, 21 Sep 2020 17:23:53 +0930 Subject: [PATCH 105/136] Prevent Canarivore gas from teleporting up walls --- src/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/info.c b/src/info.c index fccd1b269..e86b4678f 100644 --- a/src/info.c +++ b/src/info.c @@ -5139,7 +5139,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOCLIP|MF_SPECIAL, // flags + MF_NOCLIPTHING|MF_SPECIAL, // flags S_NULL // raisestate }, From 81e541fdba9208e57e34629897376af469d410f1 Mon Sep 17 00:00:00 2001 From: lachwright Date: Tue, 22 Sep 2020 13:11:56 +0930 Subject: [PATCH 106/136] Allow Canarivore gas to interact with players again (oops) --- src/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/info.c b/src/info.c index e86b4678f..cb5fb0889 100644 --- a/src/info.c +++ b/src/info.c @@ -5139,7 +5139,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOCLIPTHING|MF_SPECIAL, // flags + MF_SPECIAL, // flags S_NULL // raisestate }, From c0abb2ca0962d7e498e598b7af088111f1d90af2 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 31 Aug 2020 20:08:22 -0700 Subject: [PATCH 107/136] Don't let players change their name more than five times per minute (ported 2948885660c88f103dce4995bf4470264cfaffd9 and 4e9d006c37f57359cd4adc241c8d24ae79ae37d6) --- src/d_clisrv.c | 4 ++++ src/d_clisrv.h | 4 ++++ src/d_netcmd.c | 16 +++++++++++++--- src/g_game.c | 7 +++++++ src/g_game.h | 3 ++- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 9a636dd45..bfe9788e3 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3161,6 +3161,8 @@ static void CL_RemovePlayer(INT32 playernum, kickreason_t reason) // Reset the name sprintf(player_names[playernum], "Player %d", playernum+1); + player_name_changes[playernum] = 0; + if (IsPlayerAdmin(playernum)) { RemoveAdminPlayer(playernum); // don't stay admin after you're gone @@ -3777,6 +3779,8 @@ void SV_ResetServer(void) adminplayers[i] = -1; // Populate the entire adminplayers array with -1. } + memset(player_name_changes, 0, sizeof player_name_changes); + mynode = 0; cl_packetmissed = false; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index ce8d87adb..11fc2f6f4 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -526,6 +526,10 @@ typedef enum } kickreason_t; +/* the max number of name changes in some time period */ +#define MAXNAMECHANGES (5) +#define NAMECHANGERATE (60*TICRATE) + extern boolean server; extern boolean serverrunning; #define client (!server) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 68b8ecfc1..62b93bba2 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1126,6 +1126,8 @@ static void SetPlayerName(INT32 playernum, char *newname) if (netgame) HU_AddChatText(va("\x82*%s renamed to %s", player_names[playernum], newname), false); + player_name_changes[playernum]++; + strcpy(player_names[playernum], newname); } } @@ -1302,7 +1304,12 @@ static void SendNameAndColor(void) snacpending++; // Don't change name if muted - if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer))) + if (player_name_changes[consoleplayer] >= MAXNAMECHANGES) + { + CV_StealthSet(&cv_playername, player_names[consoleplayer]); + HU_AddChatText("\x85*You must wait to change your name again", false); + } + else if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer))) CV_StealthSet(&cv_playername, player_names[consoleplayer]); else // Cleanup name if changing it CleanupPlayerName(consoleplayer, cv_playername.zstring); @@ -1463,8 +1470,11 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) skin = READUINT8(*cp); // set name - if (strcasecmp(player_names[playernum], name) != 0) - SetPlayerName(playernum, name); + if (player_name_changes[playernum] < MAXNAMECHANGES) + { + if (strcasecmp(player_names[playernum], name) != 0) + SetPlayerName(playernum, name); + } // set color p->skincolor = color % numskincolors; diff --git a/src/g_game.c b/src/g_game.c index b969eb4a4..15d48e2f5 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -452,6 +452,8 @@ player_t *seenplayer; // player we're aiming at right now // so that it doesn't have to be updated depending on the value of MAXPLAYERS char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; +INT32 player_name_changes[MAXPLAYERS]; + INT16 rw_maximums[NUM_WEAPONS] = { 800, // MAX_INFINITY @@ -2345,6 +2347,11 @@ void G_Ticker(boolean run) if (camtoggledelay2) camtoggledelay2--; + + if (gametic % NAMECHANGERATE == 0) + { + memset(player_name_changes, 0, sizeof player_name_changes); + } } } diff --git a/src/g_game.h b/src/g_game.h index c8abe560c..e3a1faf1f 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -27,7 +27,8 @@ extern char customversionstring[32]; #ifdef SEENAMES extern player_t *seenplayer; #endif -extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; +extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; +extern INT32 player_name_changes[MAXPLAYERS]; extern player_t players[MAXPLAYERS]; extern boolean playeringame[MAXPLAYERS]; From be80ef53653e1a72d1f2beb08fc863821030d58f Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 8 Sep 2020 22:40:42 -0700 Subject: [PATCH 108/136] Apply volume to sounds with origin too Previously sounds with an origin would always start at max volume. This is because the distance calculation adjusts the volume, and that volume needs to be updated every tic as distance changes. Storing the original volume works. (cherry picked from commit 4ce347c6f219481dccf03cb7e1da142c0382027f) --- src/s_sound.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/s_sound.c b/src/s_sound.c index 0442c6219..1885cfcf9 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -521,6 +521,7 @@ void S_StartCaption(sfxenum_t sfx_id, INT32 cnum, UINT16 lifespan) void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) { + const INT32 initial_volume = volume; INT32 sep, pitch, priority, cnum; const sfxenum_t actual_id = sfx_id; sfxinfo_t *sfx; @@ -718,6 +719,7 @@ dontplay: // Assigns the handle to one of the channels in the // mix/output buffer. + channels[cnum].volume = initial_volume; channels[cnum].handle = I_StartSound(sfx_id, volume, sep, pitch, priority, cnum); } @@ -929,7 +931,7 @@ void S_UpdateSounds(void) if (I_SoundIsPlaying(c->handle)) { // initialize parameters - volume = 255; // 8 bits internal volume precision + volume = c->volume; // 8 bits internal volume precision pitch = NORM_PITCH; sep = NORM_SEP; @@ -1204,15 +1206,12 @@ INT32 S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, INT32 *v } // volume calculation - if (approx_dist < S_CLOSE_DIST) - { - // SfxVolume is now hardware volume - *vol = 255; // not snd_SfxVolume - } - else + /* not sure if it should be > (no =), but this matches the old behavior */ + if (approx_dist >= S_CLOSE_DIST) { // distance effect - *vol = (15 * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) / S_ATTENUATOR; + INT32 n = (15 * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS)); + *vol = FixedMul(*vol * FRACUNIT / 255, n) / S_ATTENUATOR; } return (*vol > 0); From 1b534aabbe54ce9f75ed24430f2ad76807a711d0 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 22 Sep 2020 20:51:01 -0700 Subject: [PATCH 109/136] Forgot to commit s_sound.h --- src/s_sound.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/s_sound.h b/src/s_sound.h index 6e3de6101..4ac3c70bf 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -87,6 +87,9 @@ typedef struct // origin of sound const void *origin; + // initial volume of sound, which is applied after distance and direction + INT32 volume; + // handle of the sound being played INT32 handle; From 0c71e6f431b94c9322898eba692b9a7e09adb844 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 25 Sep 2020 11:47:24 -0700 Subject: [PATCH 110/136] Add Jeck to the credits --- src/f_finale.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/f_finale.c b/src/f_finale.c index b51e9c9a0..3c69dc631 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1130,6 +1130,7 @@ static const char *credits[] = { "Desmond \"Blade\" DesJardins", "Sherman \"CoatRack\" DesJardins", "\"DirkTheHusky\"", + "Jesse \"Jeck Jims\" Emerick", "Buddy \"KinkaJoy\" Fischer", "Vivian \"toaster\" Grannell", "James \"SwitchKaze\" Hale", From 6dbebc91788ed8befb201e78ef3db69469a39335 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Fri, 25 Sep 2020 17:58:02 -0400 Subject: [PATCH 111/136] Change prerelease version suffix to RC2 --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index 2ef7116bb..d6feea9af 100644 --- a/src/version.h +++ b/src/version.h @@ -12,4 +12,4 @@ #define MODVERSION 48 // Define this as a prerelease version suffix -#define BETAVERSION "RC1" +#define BETAVERSION "RC2" From 583500cb924dd7fbe20a645e47830430bf5c62f6 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Fri, 25 Sep 2020 18:15:52 -0400 Subject: [PATCH 112/136] Update patch.pk3 hash --- src/config.h.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.h.in b/src/config.h.in index a6de7823d..8ea73d0ef 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -32,13 +32,13 @@ * Last updated 2020 / 05 / 11 - v2.2.4 - patch.pk3 * Last updated 2020 / 07 / 07 - v2.2.5 - player.dta & patch.pk3 * Last updated 2020 / 07 / 10 - v2.2.6 - player.dta & patch.pk3 - * Last updated 2020 / 09 / 01 - v2.2.7 - patch.pk3 + * Last updated 2020 / 09 / 25 - v2.2.7 - patch.pk3 */ #define ASSET_HASH_SRB2_PK3 "0277c9416756627004e83cbb5b2e3e28" #define ASSET_HASH_ZONES_PK3 "f7e88afb6af7996a834c7d663144bead" #define ASSET_HASH_PLAYER_DTA "49dad7b24634c89728cc3e0b689e12bb" #ifdef USE_PATCH_DTA -#define ASSET_HASH_PATCH_PK3 "97b440b48139c53fa666b585a4e208f2" +#define ASSET_HASH_PATCH_PK3 "fe1e7bd05bc22c3c78c6b64a8200ffec" #endif #endif From ca6689c8a019ce546af5b4cb0470e700f77e4ca7 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Fri, 25 Sep 2020 18:55:37 -0400 Subject: [PATCH 113/136] Fix quotes appearing in product version string --- src/doomdef.h | 3 +++ src/win32/Srb2win.rc | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index 22d98d792..b9ee1ce5f 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -129,11 +129,14 @@ extern char logfilename[1024]; #define VERSIONSTRING "Development EXE" // most interface strings are ignored in development mode. // we use comprevision and compbranch instead. +// VERSIONSTRING_RC is for the resource-definition script used by windows builds #else #ifdef BETAVERSION #define VERSIONSTRING "v"SRB2VERSION" "BETAVERSION +#define VERSIONSTRING_RC SRB2VERSION " " BETAVERSION "\0" #else #define VERSIONSTRING "v"SRB2VERSION +#define VERSIONSTRING_RC SRB2VERSION "\0" #endif // Hey! If you change this, add 1 to the MODVERSION below! // Otherwise we can't force updates! diff --git a/src/win32/Srb2win.rc b/src/win32/Srb2win.rc index 293dd29bd..2538701dc 100644 --- a/src/win32/Srb2win.rc +++ b/src/win32/Srb2win.rc @@ -85,14 +85,14 @@ BEGIN VALUE "Comments", "Visit our web site at www.srb2.org for news and updates!\0" VALUE "CompanyName", "Sonic Team Junior\0" VALUE "FileDescription", "Sonic Robo Blast 2\0" - VALUE "FileVersion", VERSIONSTRING + VALUE "FileVersion", VERSIONSTRING_RC VALUE "InternalName", "srb2\0" VALUE "LegalCopyright", "Copyright 1998-2020 by Sonic Team Junior\0" VALUE "LegalTrademarks", "Sonic the Hedgehog and related characters are trademarks of Sega.\0" VALUE "OriginalFilename", "srb2win.exe\0" VALUE "PrivateBuild", "\0" VALUE "ProductName", "Sonic Robo Blast 2\0" - VALUE "ProductVersion", VERSIONSTRING + VALUE "ProductVersion", VERSIONSTRING_RC VALUE "SpecialBuild", "\0" END END From f13d938c577c7bd8c59b4de167441cfe61d76808 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sat, 26 Sep 2020 17:31:49 +0100 Subject: [PATCH 114/136] A_Boss1Laser (pinch phase): Reset var1 to locvar1 each time so we don't accidentally spawn koopas (or something else) as a result of hitting enemies with the laser --- src/p_enemy.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index aa126d244..ddb01b63b 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3040,11 +3040,11 @@ void A_Boss1Laser(mobj_t *actor) z = actor->z + FixedMul(56*FRACUNIT, actor->scale); break; case 2: - var2 = 3; // Fire middle laser + var1 = locvar1; var2 = 3; // Fire middle laser A_Boss1Laser(actor); - var2 = 0; // Fire left laser + var1 = locvar1; var2 = 0; // Fire left laser A_Boss1Laser(actor); - var2 = 1; // Fire right laser + var1 = locvar1; var2 = 1; // Fire right laser A_Boss1Laser(actor); return; break; From 2a5d892463d166d812bc2afc14aa93e7d691d34f Mon Sep 17 00:00:00 2001 From: Lachlan Date: Mon, 28 Sep 2020 11:12:15 +0800 Subject: [PATCH 115/136] Update patch.pk3 checksum --- src/config.h.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.h.in b/src/config.h.in index 8ea73d0ef..7498bd9c0 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -32,13 +32,13 @@ * Last updated 2020 / 05 / 11 - v2.2.4 - patch.pk3 * Last updated 2020 / 07 / 07 - v2.2.5 - player.dta & patch.pk3 * Last updated 2020 / 07 / 10 - v2.2.6 - player.dta & patch.pk3 - * Last updated 2020 / 09 / 25 - v2.2.7 - patch.pk3 + * Last updated 2020 / 09 / 27 - v2.2.7 - patch.pk3 */ #define ASSET_HASH_SRB2_PK3 "0277c9416756627004e83cbb5b2e3e28" #define ASSET_HASH_ZONES_PK3 "f7e88afb6af7996a834c7d663144bead" #define ASSET_HASH_PLAYER_DTA "49dad7b24634c89728cc3e0b689e12bb" #ifdef USE_PATCH_DTA -#define ASSET_HASH_PATCH_PK3 "fe1e7bd05bc22c3c78c6b64a8200ffec" +#define ASSET_HASH_PATCH_PK3 "6b200f3f49af5478935b104eb972a898" #endif #endif From 6b738da309878bc579068d624307977aa71a2d76 Mon Sep 17 00:00:00 2001 From: Lachlan Date: Mon, 28 Sep 2020 11:38:56 +0800 Subject: [PATCH 116/136] Fix compiler warning --- src/command.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command.c b/src/command.c index 733ea5f12..fc9d44792 100644 --- a/src/command.c +++ b/src/command.c @@ -1263,7 +1263,7 @@ void CV_RegisterVar(consvar_t *variable) if (variable->flags & CV_NETVAR) { /* in case of overflow... */ - if (consvar_number_of_netids + 1 < consvar_number_of_netids) + if (consvar_number_of_netids == UINT16_MAX) I_Error("Way too many netvars"); variable->netid = ++consvar_number_of_netids; From dcf358467b417df2177b4399f35a3488c5093750 Mon Sep 17 00:00:00 2001 From: Lachlan Date: Mon, 28 Sep 2020 11:39:57 +0800 Subject: [PATCH 117/136] Bump RC version --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index d6feea9af..fb709df0d 100644 --- a/src/version.h +++ b/src/version.h @@ -12,4 +12,4 @@ #define MODVERSION 48 // Define this as a prerelease version suffix -#define BETAVERSION "RC2" +#define BETAVERSION "RC3" From 5a1b87689b697af1fbce2124b913bda6da9086ee Mon Sep 17 00:00:00 2001 From: sphere Date: Mon, 28 Sep 2020 14:48:55 +0200 Subject: [PATCH 118/136] These people deserve to be in the credits proper, also formatting fixes. --- src/f_finale.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 3c69dc631..09ae71dc9 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -531,7 +531,7 @@ static void F_IntroDrawScene(void) { case 0: bgxoffs = 28; - break; + break; case 1: background = W_CachePatchName("INTRO1", PU_PATCH); break; @@ -620,7 +620,7 @@ static void F_IntroDrawScene(void) V_DrawScaledPatch(bgxoffs, 0, 0, background); } else if (intro_scenenum == 0) // STJr presents - { + { if (intro_curtime > 1 && intro_curtime < (INT32)introscenetime[intro_scenenum]) { V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); @@ -628,13 +628,13 @@ static void F_IntroDrawScene(void) sprintf(stjrintro, "STJRI%03u", intro_curtime-1); else if (intro_curtime >= TICRATE-6 && intro_curtime < 2*TICRATE-20) // Pause on black screen for just a second return; - else if (intro_curtime == 2*TICRATE-19) + else if (intro_curtime == 2*TICRATE-19) { // Fade in the text // The text fade out is automatically handled when switching to a new intro scene strncpy(stjrintro, "STJRI029", 9); S_ChangeMusicInternal("_stjr", false); - + background = W_CachePatchName(stjrintro, PU_PATCH); wipestyleflags = WSF_FADEIN; F_WipeStartScreen(); @@ -643,7 +643,7 @@ static void F_IntroDrawScene(void) F_WipeEndScreen(); F_RunWipe(0,true); } - + if (!WipeInAction) // Draw the patch if not in a wipe { background = W_CachePatchName(stjrintro, PU_PATCH); @@ -863,7 +863,7 @@ void F_IntroDrawer(void) F_WipeEndScreen(); F_RunWipe(99,true); } - + S_ChangeMusicInternal("_intro", false); } else if (intro_scenenum == 10) @@ -1101,6 +1101,7 @@ static const char *credits[] = { "Andrew \"orospakr\" Clunis", "Sally \"TehRealSalt\" Cochenour", "Gregor \"Oogaland\" Dick", + "\"Golden\"", "Julio \"Chaos Zero 64\" Guir", "\"Hannu_Hanhi\"", // For many OpenGL performance improvements! "\"Kalaron\"", // Coded some of Sryder13's collection of OpenGL fixes, especially fog @@ -1121,6 +1122,7 @@ static const char *credits[] = { "", "\1Art", "Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo -- sorry for our limited font! D: + "\"Arietty\"", "Ryan \"Blaze Hedgehog\" Bloom", "Graeme P. \"SuperPhanto\" Caldwell", // for the new brak render "\"ChrispyPixels\"", @@ -1142,7 +1144,9 @@ static const char *credits[] = { "Alexander \"DrTapeworm\" Moench-Ford", "Andrew \"Senku Niola\" Moran", "\"MotorRoach\"", + "\"Revan\"", "Phillip \"TelosTurntable\" Robinson", + "\"Scizor300\"", "Wessel \"sphere\" Smit", "David \"Instant Sonic\" Spencer Jr.", "\"SSNTails\"", @@ -1158,7 +1162,7 @@ static const char *credits[] = { "Paul \"Boinciel\" Clempson", "Shane \"CobaltBW\" Ellis", "James \"SeventhSentinel\" Hall", - "Cyan Helkaraxe", + "\"Cyan Helkaraxe\"", "Kepa \"Nev3r\" Iceta", "Iestyn \"Monster Iestyn\" Jealous", "Jarel \"Arrow\" Jones", @@ -1226,13 +1230,6 @@ static const char *credits[] = { "Pascal \"CodeImp\" vd Heiden", // Doom Builder developer "Randi Heit ()", // For their MSPaint sprite that we nicked "Simon \"sirjuddington\" Judd", // SLADE developer - // Acknowledged here are the following: - // Minor merge request authors, see guideline above - // - Golden - Expanded thin font - // Creators of small quantities of sprite/texture assets - // - Arietty - New Green Hill-styled textures - // - Scizor300 - the only other contributor to the 2.0 SRB2 Asset Pack - // - Revan/Icefox - the new Nimbus Ruins skybox "SRB2 Community Contributors", "", "\1Produced By", From c020257d1d778fe42d3e5759f427fa14ad147fe3 Mon Sep 17 00:00:00 2001 From: sphere Date: Mon, 28 Sep 2020 15:54:10 +0200 Subject: [PATCH 119/136] Merge Programming and Programming Assistance, and some other tweaks. --- src/f_finale.c | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 09ae71dc9..6297dcd0f 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1074,51 +1074,45 @@ static const char *credits[] = { "\1Programming", "Alam \"GBC\" Arias", "Logan \"GBA\" Arias", + "Colette \"fickleheart\" Bordelon", + "Andrew \"orospakr\" Clunis", + "Sally \"TehRealSalt\" Cochenour", + "Gregor \"Oogaland\" Dick", "Callum Dickinson", "Scott \"Graue\" Feeney", "Victor \"SteelT\" Fuentes", "Nathan \"Jazz\" Giroux", + "\"Golden\"", "Vivian \"toaster\" Grannell", + "Julio \"Chaos Zero 64\" Guir", + "\"Hannu_Hanhi\"", // For many OpenGL performance improvements! "Kepa \"Nev3r\" Iceta", "Thomas \"Shadow Hog\" Igoe", "\"james\"", "Iestyn \"Monster Iestyn\" Jealous", "\"Jimita\"", - "Ronald \"Furyhunter\" Kinard", // The SDL2 port - "Louis-Antoine \"LJ Sonic\" de Moulins", // de Rochefort doesn't quite fit on the screen sorry lol - "John \"JTE\" Muniz", - "Ehab \"Wolfy\" Saeed", - "Jonas \"MascaraSnake\" Sauer", "\"Kaito Sinclaire\"", - "\"SSNTails\"", - "Lachlan \"Lach\" Wright", - "Marco \"mazmazz\" Zafra", - "", - "\1Programming", - "\1Assistance", - "Colette \"fickleheart\" Bordelon", - "\"chi.miru\"", // helped port slope drawing code from ZDoom - "Andrew \"orospakr\" Clunis", - "Sally \"TehRealSalt\" Cochenour", - "Gregor \"Oogaland\" Dick", - "\"Golden\"", - "Julio \"Chaos Zero 64\" Guir", - "\"Hannu_Hanhi\"", // For many OpenGL performance improvements! "\"Kalaron\"", // Coded some of Sryder13's collection of OpenGL fixes, especially fog + "Ronald \"Furyhunter\" Kinard", // The SDL2 port "\"Lat'\"", // SRB2-CHAT, the chat window from Kart "Matthew \"Shuffle\" Marsalko", "Steven \"StroggOnMeth\" McGranahan", "\"Morph\"", // For SRB2Morphed stuff + "Louis-Antoine \"LJ Sonic\" de Moulins", // de Rochefort doesn't quite fit on the screen sorry lol + "John \"JTE\" Muniz", "Colin \"Sonict\" Pfaff", "Sean \"Sryder13\" Ryder", + "Ehab \"Wolfy\" Saeed", "Tasos \"tatokis\" Sahanidis", // Corrected C FixedMul, making 64-bit builds netplay compatible + "Jonas \"MascaraSnake\" Sauer", "Wessel \"sphere\" Smit", - "Ben \"Cue\" Woodford", + "\"SSNTails\"", + "\"Varren\"", "\"VelocitOni\"", // Wrote the original dashmode script "Ikaro \"Tatsuru\" Vinhas", - // Git contributors with 5+ approved merges of substantive quality, - // or contributors with at least one groundbreaking merge, may be named. - // Everyone else is acknowledged under "Special Thanks > SRB2 Community Contributors". + "Ben \"Cue\" Woodford", + "Lachlan \"Lach\" Wright", + "Marco \"mazmazz\" Zafra", "", "\1Art", "Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo -- sorry for our limited font! D: @@ -1144,7 +1138,6 @@ static const char *credits[] = { "Alexander \"DrTapeworm\" Moench-Ford", "Andrew \"Senku Niola\" Moran", "\"MotorRoach\"", - "\"Revan\"", "Phillip \"TelosTurntable\" Robinson", "\"Scizor300\"", "Wessel \"sphere\" Smit", @@ -1160,9 +1153,9 @@ static const char *credits[] = { "Malcolm \"RedXVI\" Brown", "Dave \"DemonTomatoDave\" Bulmer", "Paul \"Boinciel\" Clempson", + "\"Cyan Helkaraxe\"", "Shane \"CobaltBW\" Ellis", "James \"SeventhSentinel\" Hall", - "\"Cyan Helkaraxe\"", "Kepa \"Nev3r\" Iceta", "Iestyn \"Monster Iestyn\" Jealous", "Jarel \"Arrow\" Jones", @@ -1189,8 +1182,9 @@ static const char *credits[] = { "James \"SeventhSentinel\" Hall", "Kepa \"Nev3r\" Iceta", "Thomas \"Shadow Hog\" Igoe", - "Alexander \"DrTapeworm\" Moench-Ford", "\"Kaito Sinclaire\"", + "Alexander \"DrTapeworm\" Moench-Ford", + "\"Revan\"", "Anna \"QueenDelta\" Sandlin", "Wessel \"sphere\" Smit", "\"Spazzo\"", From e00753e427e68a3ddce6483aba8aa1585b2ab307 Mon Sep 17 00:00:00 2001 From: sphere Date: Tue, 29 Sep 2020 17:23:44 +0200 Subject: [PATCH 120/136] Game design is a team effort. --- src/f_finale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/f_finale.c b/src/f_finale.c index 6297dcd0f..8bc2067f8 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1067,7 +1067,7 @@ static const char *credits[] = { "\1Credits", "", "\1Game Design", - "Ben \"Mystic\" Geyer", + "Sonic Team Junior", "\"SSNTails\"", "Johnny \"Sonikku\" Wallbank", "", From 5e876fc8058cf1d1de9f70e2c354fa4ea626c2d7 Mon Sep 17 00:00:00 2001 From: sphere Date: Tue, 29 Sep 2020 18:06:50 +0200 Subject: [PATCH 121/136] Fix Arrietty's name being misspelled. --- src/f_finale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/f_finale.c b/src/f_finale.c index 8bc2067f8..57f8a8712 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1116,7 +1116,7 @@ static const char *credits[] = { "", "\1Art", "Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo -- sorry for our limited font! D: - "\"Arietty\"", + "\"Arrietty\"", "Ryan \"Blaze Hedgehog\" Bloom", "Graeme P. \"SuperPhanto\" Caldwell", // for the new brak render "\"ChrispyPixels\"", From 6fa06c452317aef2cf28fe76c0550b8c7a8a0d73 Mon Sep 17 00:00:00 2001 From: lachwright Date: Thu, 1 Oct 2020 02:47:24 +1000 Subject: [PATCH 122/136] Update ICNS icon for macOS Replaced some of the lower resolution variants with some smoother downscales. The 16x16 one is even Sonic's 1-up icon from the game! --- src/sdl/macosx/Srb2mac.icns | Bin 173437 -> 264630 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/sdl/macosx/Srb2mac.icns b/src/sdl/macosx/Srb2mac.icns index a3e37aab3ee846900a873610f7d8d66fd34bfda3..2ac2faf33e1ec211c5648e417befedce9b1eece2 100644 GIT binary patch literal 264630 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�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βhh zuyu3p7dGP8V)N9azu&97c5beN-S5(Au|V%S&_n<1%nO z%>pV?<}9oC(*JIhFoyOYFeqk<@fa%9h^TS-&gIvqGMA{24YGc>vQE9zXmijN zi8j`AJtgDcM-xvHQ6Za%&T`axK!$_(+5+Sc<-e@1ashwrA z(I7!9G=5$S^38tIMK$6$JP^O1?k)f(+hOoo4lC+m>Zo}jyPFhIq~GDx7lxBR4i+w` zi1-X^4L-WQ@$fk{5}A{A9?F#Prgq<^b10xwiFsf15S-IWCGJ+F_&71}-U{UD{BR~^ z^#AYv+tSk#w!Yb>Q7;e?X!yBK9`OD9`q(S4t6`uIO)XA?)ni@a#whu~+{nQuluRUg zr}niEr|83QAKx{#c@Zu9;+w`pv|!RoP(+o+garB$_6r$@xX1jwaJpkiIYl?Av_=ry8-T!9Dz8lzr8uGH$P1OC9O~jAZZ0y zoaW*6orA7^H~lz!$1ywMQBg#Y61AybQhYDJof;gf-3Y1Kax`P$ve`RL`5@NzEfm=G z4JFf*FDq*%p6j>wh-+>NOu>&XXJ?vTMP{#a;FBaD z2@oiLp!F1c8u_ZiM=LaQrpoRvKB57{Pt~Y+ z^Z8{$Z@a5t?9N0Bkf&##o04eGf08UVaIfvR*s`lkyog0DO3D_RN+&~#p-zTA%4E{S z1RIK+dpFs`;Ik75GV8;tls`XhPozwSO1%l*&$@+`FO><*V&J1%15J3q$5t@Bx;%6Z zDbbVtqTm&U^09%&`chJuK5s8diG7rI6RdT>VeyK0Ojbi?ve+jvKFc2VE2Lv@drQlP z7)SUk5Z2zDPaG@y!5S_TmPoB3&iTx9mcKO(DU|g7zVqV6=yTP77nc#dF?quL@`?XA zwE7|7W0;KnE4?Qqe#vV&IjY!Y$@F5VO`fj&r5JpA@tvq*Ah=s~D$eL1XS$yc60A%! zKuvSNsgq5PHfE&hB=XKcPh4hd>Qb$p#l9B1s(-m47NLAhV~`~WZO_XN(YnBn3p;k+ zF_T31yaexg)jt@XP|!3qoV=wjdO(@o3Lnv%ed}M31z#}B6e^OG+0PaTXfjUwcSREe zxh%i2(guLr3BOa>_;H$sh+}_`&Alm00=U{3>;T%0`-}b6Cp|qw`XT5>+UK0I#jnXcT|?H2L`2*O90e z>Oo}cAzI=3a1>gtEOC+Q90KIzdBRvJh$R|F7(szAQ&%dAzWQy!Xrv8tqw(ZqGxf2y zf{edwzTzTT&5zq**C*DKF%~^E?;M_linM(V@*QAT;p70{^#B0;`m^u**Z;l$E#AM? z2UK0er_5ZKi5e`H@{XK|g(?P>I2U!HH6`MyX`vw(M`JXPad~C@qIj#egT0mbztCO9&%ftMY(vJUwKU3;m`;1HM#`mOzF706UZ1D~kT1Hg_DC(E* z^O+4u{o)Us*`HzGAcMsN%V-zci0+(3Saf>=z{J8oJEeFzrApiPwPVq6e@x;GJ?$3I z(^JdIhhfnf)%U?U8AyYOoDIP^mb5u+jp>}u^q=8jV3XI2i z{|4Rfo;Na<{X@u_7$e~*C%g6zceP&cw)Q1J5$%5R-hr-le-C3k4_`P|zeZv%DpxnS z3W8G5(`lqJ7N%|7#u;Y#bwDgRDc>X9A}x1uDw|N?*kbsGxu-chu=|HPLDX#RC!4MO z^TMWc9#&gwmWeUAyw_5zJhNbv*I6rx|8m3b4udTF-DB3@%PPK?CRM4YL)4i%{L4EKm`pJj?~&bi+E_y*V}addVwimJyPIjD-%&J@d^6*HwHHkFN5?G#o>r`{8k}xk9DW1w-OO4nB z)j>?}F&o*q*U2&F!hh}$4Sw~Ag(H;5du)f~x)uB`bYRkLQtcTTuGryt!Wm8r%^g@J z#Z$SDuxeuCMpo}y?>_n|eM$EMN8R0AHPl&jtm>|}1>0j0QbXjYMyfbp>r_OcT7pd< zc@=%T70A)cy_)_TwyKVdCi(yO{9B8I#na-VTxRq#<30*HI^j`}iY*idLIXo3tSpvM z8>QUdYj9&Gc3d4ie(H4BN$v3d!}DO40cA_fpC*>(=Kg4hV`fjj`A3XTRfYPjd}exT zE-h_Z8+}qq2eJg#lG_xo$07@IRtOsL!o3KZiq^B3%{*{@&<>7I5)Xw7X#3^oQg;>b zBc&C2BvmrKAMuMB^NM+hf12O+LN0lQtPe@^5_Bi%+PI&YVVjgQVcXkrV8=>kwzjIL zP0_XU9p0|oLM4}rWcE~G5R9lr^@iftM3V|tbbWdz&g-4 zni|qUG28ncRPP~wTy}<#5K=6WgYWKzwmnduk3o{qG>eq zU+@!u^Np1s^tKeA>9e9uVh-&QJ`slvi`AV%-*OVInY08OxtP2* z&Mq2EuiS(#F1*KO<3y9=F4r*%?b|2SdBqOyK)d z#QUe(2rEH7;rXOSyTv3}r7Vwj2r^Z-2oIAZHer?`y`KILD(zK|B8Rv4Kk@87bPqfT zj5m2r0(IBzV=PPp-!!63`bUorEk2<*2|)!AwdaP#;;i9WiXI&u9N(&kHU_7*S-zDh zm!TpEOJ7gbf41V8D77Hm`>w`?87y%MZqtMQVZ3|S0DTo_q7v3-_O{yvD^XFn^ZDQD zfOnVq?^6D?ViyEJ&wjF=L0D7PKEcPY6_nkZj2*NG0ik&>lY(zNA8WXp%3kPCD8CWW z1P%Q4Yna-##G+~l7^7J#LcB|Ps||A@!=V2q8eFr=y+CT9tRrZ#GFZ7&Jy42s{4kZi ztNlyt=p|g8j#NU7F9N%D4j4GOuqxA7KcML8O_Re((tBViEwR#QwNllO6MOR(f4SmA z%SS%rA<4}MNz)Pdl~X1D(Y;Y~u!(EB;uB$g%mRw@B%7~UW@`IQ#-Sm@Go5$geyl`~ zpOj_bQ{5#;JAVT|e#-T&pO{Jgse{AXS6!eVoVL~(g|NHO%VKx;RdEVldcbx;XJpG0 z1lScNBvQkt;=hXEG$L>T3i`9;xE7@5lZVqjf;iQ#o${0Lcp_vj@cOj-)9QK=CSjE_ zY)CvojZ$J!lo`1=gnMak?^QmUHwH$TZ@YFG&>KolHC(&+wo}7{s!E+n3^`l^U)?Z& zCl@_lu8}U?443GESHqVA12Qd<OhGc< zf5&!CuUl&B{*g2?y zLB3p#*gRX4wbr;zRa-)h*>6x7q^&`f~>NzcV~LzGO3x|AQ6ZhRC-*VFuJq2t=2S7=796@8@JX!Zn+cfoN*7 zPvgPyi0AQDA8o^>0wMmUdY$W&<#((mVh^`JLST|Z;slg63Dzn0Se#F{9+RSO;8YU8 z=7c_&|H4v`>+XnerFua$FYPzsw$>fBgrEF!Cp_AB47Y`U5_U+xY3`ef+j;FLx5-3v z_L}&N^ZQG1wkC}`y!?PsrXs}(rCcIiZBewvk0QV65M7ZXrn{RToN~XUKH_`3?uni7 z^PX;gX9vZcP|kFLy6{)1P$iuH=sz!j$L3S;`j0lz0-60Eu9hoIZg?qY8{L~bmTKQe z<$g*ssSG=cW0RmO3)r}z18&Tg?P3U-gLZTkS&`FmU6gant4sS#2@`5v!Y?b!DMa_ONe^R>A^QRMIbuZX zr2-=)qX~iLPNMFx*bTX++1os*563TD5Cq3gl@2UBg zDB^K(HCSs(!UTMdKC=;mGRY2?zPjLe;%xuivV2Y2iKH2dtgXeM>>m7DLELh8&Wq)_ zXCA8>B7OYHoVddCY!wy9ptx1eDH;CC{qchz>b&Q#rdu1?S7Tqo<1s%WRs;gJqCX!)@VSHG>Yu>z9 z^Y_Cu4Cx4ShE*XE<+K6$V=z=Yn@?%_<>xVAJ$ICJ5LGL~(- zCURJ@t$V6MVVn7q{s|)|Cj+q`1JFJ-H@vBi>>L?!{G3sGLv^8MwR-Vt-*VnxeGsbS z-Hf`k*p#9w!OOpwb&1vw{lH@HdvsohI8*#2^10FJY}_8pAaWCnAC=|?3#yu5u}G4^ zdnw<>9d!64qA>2&-)i`k$Bz&HU3+?Y3O>Yy=c01y4dV6KM@fb7;6(;_xc~cop}wx7 z4@2VJGm%xd4Yjee>%v{d#Z7Fz`(0j>)JfDh&brkdZKU*l?C0EIcq(e(Q#&CdbJaGa znR?YwT{Pbl%Y^<_VgoJSrnH1-pY5C)*yK8$8oJ^#QUnwDB(pS>`+Jw)dO~2+Z>BA} z0UzI+2(m1sTDzdcW!zzXiC6KtsDbatwJ7y0wN%#r*AosKcY zxG;(x2Q>jpp7LDXl;G-mO2g}nN3Ypu)2(+oP~seD9unD8v?hK|+>ER93MxI}n9;+Ww04>SOivF;QRwmI&Nc&q+FHI%LEmbf>i z)%R+>inP7M4^dE+;i=kBKP)Vir$d4#n^Jci-Y&7{qku0%A8qEaHcOZBF@oV4sDa*n zjt*#Q(1;u^5C|4;{FRg#*L4zt_4~N`T2_s14FhHS_f0zIThFAX8TJQ+ZT@C zcz|AII1xXEO=l?cP{woZ#-7-i&VJz?QC?4ue8R$wMT;u^n90|qkP|AEqk+8Z*8j?b}QjY_Sw7 z@xycqj8f|c4*WD`R(B%_I4$h86_>-Jt#_$eS>7yRav4As?ChsOvrQ@f8fsja`i8cy+zAr`W_7Iuw@Ohi;agTuv(@qct@2;M}sWEaI1HL}I-Tli#&Zlj z1qNn*NyldXckb>G$&k6}Ax*YM+Hvt(AA)!rP&+0-T1q|-ngfx!zEwAAc4t;|FJ9~!HttoC6-7}A3P{{v8Um8g_gfzjd8{yfi> zNU;mgDU0yWVqOA9sPY0{II1*B*S->sA>klWaiYusW8pXaim$kP+}+)EVe=yAklPEy z1B=&ml}!j{S+|z9-DK>@2%Wa2a<3qks0Za z`4iGiTfp3+@6Z=@y;czJ2pS|O7x*>DeV9_ZnbYuNEP=>sc&sK}5FlcZ9$}_eD&coc6OHillvR=bDD(>vl7&> z%Vl7pFfZ&gT>DiPgXOUlfeMt5%clpv_u16cRQ-(p4d`N2ia-CG?S7&`ZO*R@e?LEQ zfDgGo+f_ELvc_IE@tl_@-v^hd6_=2G=EzyfO&Y!Z#Rn|VJKw*Z>*=-7(0L15Rw`Qx}aaxg7gqK2lYb_=C1qK)2%l#u!|;JVqmcM{wA+B_tX zE2+i|SAs-ZF@Xyp8d_x{KK=_1FZqa~u1x6VDOWxxdfvj{6Aio!`HLvYl7qT$9d1d_umSr55iHf)>)i_0qz2c` zN0tP%>*mu&HjsP3iDk?W5RH=YaC3(iV_UR=)zXO3YH&kX&37lt^5!SHhPxtI>W}+4 z+5I}g@QLkqmDD=zKh4tbvhB&uMlJuU;3XBU^41+XWYIv9lK!TVA({MANCF-XB3u^d$a@RZd_2 zWB;q-0wH3ct9D#55U$9{bK5~)Wju^&%O@Rug(X%mVmFnw6m_r`_Kc$}#>SeZvAw*y z)1K+7aDX90^J8w{AEP5KK6U#f{cpB(3XT;_>gHT}XC>bPkSE2(m`a_?alcpgao&1YkOy=&TVc-f z=;<|c{BNo4Vj=UL} zs-ON4Qbqeal?SJptA}rjc}`aj>W8oK<6L)8<0}BZQGy}S!G&ZRdZdG7<_Nv zl)xjx<)8S`;fm>^H85eTs_`0K<_s+$?H3~#TNYBhxITON0n3@dw|m?3*00S{a))gJ z8`-#9&U9xLBOC@VDvAVFK##BRa&qSSLT(}GQjzS3{8F!qK7Pd6`E4rvYo)Xrs#ujw zn(1@fL3dA7S!arTmUwV>?1vjGC1<0y#}BS|G%`Yd7P-9tHH|J_F4T!*nE~G$7S9k4 zsMc3e5mKz3=7TSS^oi-|3dmlI<3K;;-n&U7vV24wrYhZ>uhu!Svi$FHLjZ~`oM0zWMuxJHOPZ zd-r*IIxkbhw*cOb+F6u*;l*0`C30o`Mm%4zENWw&4e?hj;wb=Z7K~M6doDG0s zRU!HDLtKd*hb9`r&vjPe{}nMgU2U5{yKxPM6msQP(jw$u5z3u)w8u;-;jq4Z{#+AT z+n=0#u{h-V8*$6h-3)0ua62B#`@TWb*Vjv7rKi(0z`cMY{^y zxy-^6x4(*x2hM?(Spk8jqO>MJs1M*NDdy7)rtXB2OW}ph zetJ(mkWZWrZ1frOzdy+f*zyFVmp&&Q)xk8k13zqILUhmt`%w;o^@v*@PC#}>=^xsB zYjexf$eou~i-#60%3e|V`J$w-SilGGJ+7UC9EOu5Dls@f4_zo7bVJx0Y%ZS|JEbJ~ zG$#v_&;_qGp2cCwapu&m#&n(qXAp7Y2qBd1)cdQ2yR2Wdg0$R8ENlp%a3Dh~o0H^#+3iS5VSq>{~oE z|0oE^Xa8Y^4DrqPFjm;np264b#fuk-FI?7?+MAojO6(N+MLnxnu7Rgc=<*s>^Th~r z|HDJp-Hb>1>!QM9?X{k6OmHCJ4CWjzb1T-{n?|JgPVZ9^3gq+<3$#o@{xaG={*xGf zsX*&s$R9=VR>-VI4XveOfQE#Z@QX?h?uwo5@8TY6R9y`@)%jHOHNUk9rDkC6GtU2k zxo?|zYp2a`*W$DPNc3TaLi@k#s0*iALt;{m6yE(prGA=jTwzqF-X{o1P#ze#*q7PU z?qRud2pvq}RFcfZYwwMsA0$*JX+9qtZh+ajf<))jVQLn8+bgC%|I5SWyjWrX8ryR5 z6P8ksR`oz&@jVy*^?HigOMF__iA!1)l@_Hd?}smMKN>oTOrM}dH+ICI%xvoiq zHDBI7mxvob-ko&)G36;m9gHD{E>-%n)h||$KW=r$m@sje_ApUrib=%xQmpv>aLxl0 zUih@@Oi|=JmpjQe4V;66yoB=oL%qob2FboTe~4$vI@oH=UvwJdSFel!&Yia}&T z7}eO2hKW8`6>4)UN&Y;-ru1oR3;2*4Vub+_^~w?!4%NL%WH$iB=Q3lZ&n=L&z+nwWuUp?QPvH2)MO-a}Fq;-KZjjWc10-yR;4d2G=nPVI(0C~aNwKTT&7~KD`;DgLBWr`6B`ZLG`zLP%QDNjZu zX(gYWoNTY`luXA%cgyOOFoxLH&v>#`5&k3=uzO!?b`CH_H((+rC9Ki`6nWt|QD!n9 zXEgXjY(YaWpp@|>oB{WrsJ&3#_eN9~>3xsn7f{@XGS?@N80AfZEWx;xVbKG{nY2|v zi(=Jh#Ppx872D?9uMz1743niKE!F9qt@5ynvM&mWfwkP;)V2w=A5 znvjs0DW{eS%D&@SXLw?lCvh+@`h{pWw~R*eX0Mukdn4WPshloCk1$zMA3K^(GE>*7 zF-y}e>V#dX4`zRYAd0XoK16anZ2u~IfH)d7AOM9(xeTel%< z0WQ#cuungOCF#rzG%K>3PBNi*?#HfsQ?Y>(#y!f1TAf$xZon5guFLh)`A?!9|4XXE z3M6v99l~KrIyKv?#Hl0lEUlJlwDeK_k`pvf07U*8m3QsOI&( zx>u6;gYO{X{(4t=aqt+^KXtSVh8G$d+M^vEh*E0V03a_VsuHkMbNwPBendQz6&n|K z`dI=S?$a=y;*SDx0(h+}7?vb~LP7X@&os5$O_Kd_F#u>oTol-?OW*1(eka~mt*QH~ zJOUk;HW!D^Kf}Mk=iDip5H=J>oXKM?!O}yuK#p;a9pL#jcv+5p9H>=`&Og2N|7SNK zVNM@N8=${^fC9`rLQ&W@l5;O8q!16vwAXZ+!@QZDX z*mAM=UzTpTJ?>}6Ex4O5ySTXW%?qbNadmYc^5!f!>LD|BLMbBrtUO@S{q%bgQ%}h{%8zc zwrt0zqGdI%Kg202o^Rc>(F473mVh2$4Z2_^z=q4sN%q|W{c8( zm{R#Nke<%$9l0ljL`PYlP>REkYKH8;C;1jj9?=P2N^TKj>`Bk^+@{}WJzv8yeZVtrBlMgkl`(Q1b} zv2!W5kxHfPB;qwb7SD1^eNdwTir)3fe?q%NjW(vGhcL}fxSPR#Z~PzV)5N$={#)@- zpT8w1RGL(O=1IKURk=)Tv|~S6&h%p7&Y?L;$HrV7VOS!IieR$AEqhz=+#_+UB$F@T z2D;Gfr8~ESf(7*4hj&e+uNoSrAf>Ej?U-(yd-3)QRsZj8Lt+!vNATVX^(qvpem=b-Ed zIGoNe5!qoEav_tQ<^07#D-2my_6fP@l!(qn>+H795WVTt%TL z0yb{~mQMJ8nj+gHa@z*|x~V&O-B_6+3*8@-3*{44f)#1by!sBIJ6NLU*l%Oe$r+y<9`kh6ORd3BZZ8f}x_)T=7Z!j6 zK!>q%1TuGAJsZ(G0-?!=1P5HGcR4vV(phsd&LDJOI-h(0%nSNDw%|vm^VE0*X-h$x zINCd$GT}X{@rG7B^Fed6awmJVQCkJE97cuJmhK)dJn*$+HOO^G=WFbGe~J!SWmcfO z@$j|(;gYnr@cwhtyejnNpTjs$h3PV}p_U%Vjd8BG6U%Yvy-J$Ed~+b6!F0V7Vs_8^ zbwDT7Y7&A8G{2HNmu&|!@3>Wfi>5aRT(r4^Q!z<1j4MoOg6o|;`fX)&K)Re{TK4ar zDXIO>gQ4-A2}e(3DbpOQSof5~wUvO(BXi(1j4F-FPfg%Bt;KW%O)Tuzzg6sosW*L4^!h;EIh01l!wicfKt-?<9=4iTOiE7@V`gZ!EVKp>uC|DK8XcMf0U zExlmQH2uol=MY2@mO-q^*~=1?jzv5#>F0eR`gMQ0!-&Ok=GQurXh5N7Q4LLBzp!^> zp0%{kYY|iPn4;`J&qTX-(oTh5&pGuEW1J&C^KE)49p+K<7#UiSD1vN(k(-wHV)iG+PtQ;{K}^ z^5TCCyxpv%Y7zX*%)Nk?LXi(9=EDbU57Uvc%5@N);&1JA5jKvadrNbL{S&C6WVXlp z{vo2%|Cz@cP`k9GU{se*7qC&O+ku-~umc-T-g#<>9fjoI+Ql2aUSQ(l-TZoFu4R;{ zqyB5o-}Qv9$Z#p0!6-3a*cf#>+UkQse_?u` z)R$)gdf=-ou{drc=nE+e1|RtRfrd`l(B%D`C3?qYI^La~^a8zNh+H7B2Bb4@;vaFF zM+_DRVh0pb0p)ze$iM|x^7=ZugwpE$F2m8$zmY1Ov8D#jVrbY4tgC>#mKFKX;2qC7 zs`fiFc^tRn^5&gJK4yLNS{xL|PbNX0uB%weefG%N%weIqVRn#VNAk-$?0UOEM_iw< z6-+25wJmuwJ%p7zKKkC)`*Y+C6)hDM^-x@ddYhtS1Xrb|P$147ua(K1yxFcC;LcXe zAMRJN?qK)+9!mAEdMwa8>NVERWzpphNPyI3aQe>TzZgg_PVu*0uXf0`+BN8=HHJ5E z1pKG-^;#``p@FCVuFo9vbdB+%rdzE2H_Q(fk^Tvd#5m(=jlnuM8v|)pDs?yz#h3&)-pzje`Ezn`y9y8vh*U?V<$>>OmCB%_L3;ZGRu zl|fE-eu!xqLw*%Uyl-Y`7+G2)CAVtz6=vp6nJjpYzE$E;wvPsvPh|nqGIS_yJ;}-^ zApuf6JqdsaLce^eEA+X!sK%T^`eV+UFp=HH4)V>!4O8h7N;yuAR{bNhzX<&GZBXw| zwP;KoB!ix)W$xB)&3rrrwT_5ykjES#ZVsB>_Z@nxWX0xgfKg0&w%s;nw=Nv3hAt@G zTX+2y#)zRR>O#wCD&6cZQzx{2sk?M+m|9F8s8>ZXLY+V&Z`un;`codvI;V)~WNaW~ z6p5%x(2z*OnRn^u{@zZY7^tlOcZQ`Z7NsB|3v6Q}nbS5oek6gi4EL{xJ0q4FBFz;6 z`yX?y-G4mvNc3M-58s%7U+22gR24{Jl1}Lk>F!SH4(aYL z>1Na2-MqKY@tprVeq->3Z~MMtU31Mf*P47Q;e>XTk-Qi1MS6ie%`BBR%+y4W565D~ zlc&25*FP1!zXg(`3D+E29__DJ%0KiIfy@UPJ@%#J;t~_ZO`JXw(Rm#u>XuP2Z43Fr z=260U3S^r4c+e|tx5-Mg)J_cv1~36b(>&PF>r?9?_O`hD&hr@u0OM@Iv`rwjNb@Ud z`tLzP0ER{JdVUJ(z?T&HiW*PIT&=8cWyK&VDAFmP!WKL^SFXJz*?hVEDskp4M~VbJ zPac)ZlU1!G@Rb}^*R+R!B%Md?VKf7=1U>sX@GYU)sFKD%=bQxU9sGGaa!#Eia=6v$ z*Hu^&f*ak?X@TKNc`P<-dVGJ3Ymf-3MD>4bBlGRg+F(_o`ZgY`+Y8KgzIB>MvjJxE zsZ6gzt>r)2ICK9T=S*i(Nl?;?GZN~4{*u@jnuG)L zJ~`q%VvfNPlDG95TRvXT3n3Q}`U6Kisu*c&D=fEM_4BH{x?2C^Dz2DGz|}pKkn=_r zG0M$uv;`|r+ZhRz`tLe?X8JREXq9OVL3c`Pue<3Kj@Ug)37bwG6{9NYGJxg4XBq3e z#3byHnTf7+MMKiAOw+-w?3*z!E0a6h9$2p{^3`*Ms<#SV2MDDhxV+9xzb5g@z~(ib z7RwFMDr*Rx$!Lz-<;^D_hnj#_jJ%lcYfBPPb3lzIlKlgxFu!FPczLrHIr;mypk*yR zzjt+O{c;91#*`r{=2S@JeKJ46`(G>+ekb7v2hy57$94?xQH2oq7@_p78~dJ2HSV2{jTa23ij*(x>O5JULoL%VrOSYRe84H zy-IJQjwg3{dFu_{X(|XsQc+Q{etWJ=YtGWMg&Ah$bAIyw*6E5A>MdO6cV$gi*4l$x zyAdstN|@K-)A$n^tmPH$TcEy7$O8-~|F>fJ9?IEVqUUUWnj`F5>75J6RqMRr`(|MX zfJ@LBzBDjN#V=8|$5;MbcjyxGWgzMK67rssK==O9efg!$WB%8|^7J1tgRs55y@M1D zyjUN6f}~87?k1%fduI-tnwlD}nEQ#udVoJIrT^wBVSt+ z;@o3hr+?d|U(xx6Tcg_FJfAcHQ}zDuV&=kmb2f8^`9?PsSL|}%2L!+l^({M6tc;lg zBrk=S02XTA{128e%Q#f{Gekvm+KRXD8HnWf2QQ%gJ_!}KH@>r*ke29!yE)4QO1qS# zABYrBDxajKq!cJ}P&dOJ6e`-sh|=<;6P2Q;A}$n24h(l{A^fC;U!mpHoEddv z$Z8y&FPkc+R&6v&=C{qba9l!FDFrenBN5O-E0Ew|XGeoMi}OY)jcM47;E9S$NJ@3H z(7e&&DMRY%2I=Q*(8MBXt4|IVHZvaBbJ-Rp7Z(FX{L}I{BSPUjvL{u^yOb=QD3Xa+ z7@oPZxqt5%a4oZuD1bB**IRIxx%P&`r!L`|d(mm+_RO5YRA7)|WA+dFGLcq@ zfNtL9ogGz{BO@V;xB;>;k@s|TxqfwZ91{kDuBxi>G<-`iX$arXdvP$ZQ24H*f@5gQ z8-8s0!hL^C{xv1m@m43eiE+wkZE+&Z2!!1}-aoDvX^a@od7d#pZ_;xKfbEbgU;6!d zse+7zDGj0Y4-7j=-av=x&)B)4UMCoCczNh+kT;d+f0r&4S0Vt=3sSKlC3Rex{qo?c z7}u#WL7NC|JX3EBfLklfaem%>JiScwytG%=+Lu1A z+3R)6uahKm$pUviDwtEMC*uW2yN3uNa~!AY^T@rhK(-P#HUHCDH=AouTt|-I$1>%7d`H4^W z4sK6l!kkal=F(@6MVO*#_!M)aZpH6#G;iEu5yHP%wAkh1z_@PRnL%v10ha~kwheSYa5|%E^zt!(( zmH1rUbX9G%!@d7?g?1(VWXnxH^7VJil97|Z>B@cj!h#38%+h0!sj{hsN+|bga_3|> z|HJ%}fPtzL>cUFdGbdnixvwC>2L%HYuc%sAcaVaWKebxE`_A5eTJkvnmW$V0=`!6{ zrPdi8{0&3f?!v8k9iTOGA@r%T--aZgEErcUP^dF~b(1wv_`z-#!7&4^+vxTv#IOHp zap~F##(ci=iNAM&ZGU6=xAPEuHi3H0#0=F`Y}rHdsrzLZPBV3-$Mv z?qzrywqqCdY_6Dazri7z+3x_X;2$Ulv^v7`z_#+Pnjm)Zy+*>KTe%INqXe%~n zIM0BGN5uP8G3HYtP^F)S`koY_yjIl!-OTK>go|7RKlgetp1gDARWx`ZxX{DG7W9eN zWIvYkz`)}1w7vaHLPA1_g_+r>XbJVFSZeu*0b{ai_>S()bgYc!dzlkTj3YKW=To|t zdwP0TIVrV7*Z1qD7M8uk%U8EB?Epg{U@e=XaXe!wH$aIs=yGWnC+*VG*>#@<4=1W75P&%;!LJs&0a z4=W6g`J}cBHBPlo#;BS_HiGe3%%%#HSE>25&FxMu3u|u|jEM*PpA->@kO9lWY{DTL z*3gb#9C5CmuGrymuy%PBHZH@OXcoLP{o}&}H`**h>wJcoQFg-&SmYrQX7ty_~vLvh0hJKKdc8NhBo|`+CLR;NAX&?Z0 zWo7+KK@)n~QceL$onr8tm%k3h5a}+qpW{1w5 zvx@6I;GQ3l>aDq~t$ns1KecpI(Y`$3YPf?l>~Cj$BjGMZANjA} ztAs8Es)RpWN3%&D{%*Q(Qmaj`q}BPSRr@K~yul>q;_^D|1Gp)_O@xfvkmi&yt7zo> zvU-BIk&N`TEKRRrtYcA6!1-IHDk;}p*f{dacq(ufizK4QISZSfA#LJ@)LBCZNQ#)S zceg7%KH1rubfF196Gs&$)mi=D<}NK>Ipvf{x4#}AFV2^Y2wh+|GY&1oV=)H=$6Q;qNMHC{HvO_q3=|{_)pdcBNSIa&p_pH_PJFE-;cIq z^7Xf^BNO+uF$sw~*|IV?l+f|Fr|v{H5;X(>@|p&+zlegEi5l9kuOCDXjk)^~N3D_h zzw({WdI8yTUa%7pH}_0TX1pp|h19H$D4_q+C0SW5f81YM#FkW)9)^t8)*dCRGKyq> zTJ^ZP;|r=Ob+x3_ywjCUhKE?0123Uxa#n~6fQ#LY(67&4ac{1>p-!G#%vhgRYwCAz z9E;R8U7>4S5tFI@XVwV#H4f-PGU+ID_YR1DKim(dp4M+WX*%3EaC-N0*0ba$VzU)z3lqKT}IfZ5xsNua2y@l*On)h{SpG@{#QBX+DuA9xdHD z&00By6Xoqk#-!I3T?FWRK<%e~E!#J` zeC;C7cUov*F>LlXJP1MxQGqVmahY@Vu}Jf6*>YG_=v)Gwkvh|T%->D`yu&;# z<%uPLBIbDWFsbqRv*85~a#Y_}L~VMamgBegqoewj4NXn2i>12nT;}9Vr=a)h3OW_^ zGz{KLY+lZUK*maRL-xOt-@PJbw#%2k@X9~tQm%LPdH=*^fN);@o(1#L{Sd7hH_sb} z9ZvI~*Q6N8XZ;N5zC#YYwO&Xxmz52t*yKYy+lrM>a2)5o43jurGz0Tz>ue$%YfXg0 zjrK0ptVU>J>|%9bcuQ4Upy7M74Y6vwjtifS;baD~&#DWDRN{9$zJ;-FwwG-OBx5W! z)@?p{S>(ORXZQC_(iF7P0`gq@wA#C|dkvM2;F-Jxtkqw^&)0;6c%xx^?k&g_)yiJP zX})=pnHaekglJ2KR1oeAy13-w3$6n4u0P1f-1a;Y5s^$p*2y1U5S1}q$`Na*g${nc zj?uGf!`}w>RxD|yxeW~IE##OZT~Q932>Xb?A#nxqsw7eTb0~eH1$DW2LC$3N=@6|s zjPr0r&Vn(Odyfgo_IyASCruyT_a`FnUD;<2D(ci8^bpEN1YTATGfG4 z!wM2}Pj+_311c<|7l2Ucar*aJ#uvc7mF8E8dEP+8TlH^uUpsAmMQYgC6eBDk?-&s? zQE)r%R$d0qI&?pyq}f%@Uw?^Sy>r5yR=*5fPiGslVO|p;a9?{h;_HYHd%L>?v)H-q zodq?GUyP}HH;b(LZt1V)W7|io)L* z7YX!J)R&!OXlDl++WP>TFFoh9;*dD-vKe5=W9op+T=d+vVQsC}otL*wI{~=g1`K>f z37m`SOz~6-6gKps1f^nP^Ut2?{XECFWcL2(pCFw&Fh-g%Iklc$Yxc6() znD{lD^HEhltU0GHH{C%G81FV{e~#UBceb7_gJOa&Bi?3%SRbTNj!un97UHojrF|$u z*O6jg{%%3nFK-P2B2v9T=gSv5)pILv^`)|MSG(P@`0`A*lg2{fy_4B-4Yy`jj*Cm_ zyss5`1hpEA-yEGvga9Czpf22<%2Kx)d#AhSjGSAO3<(#b1bJuOwS<7A>>}z{y!S&Hnk0r8Th6*5 z4w1jUha)d5cFb3ud4Ih6-2YnrR3KBlZGaH?e9%wq)$&ab{81lJKz6*lHQ}73pu4xM zpNX8=j5I)&m-NQrr9L~;ofA>u1!a2Ttq5-zFf`*N$Q=tng8m@xM`@>d#tS zyt<5dew39ZmQXZ5#Q6+U#au~aa__5cB&mMWFrGxH&y8wWvHOvWbM8Z7{{Xi;W99gZ z<$H+)Kg!66gcN;u1)aL9PqAkb)FrQi`{%2@g2)-Zl>a^@5@CXoaG*&0z~qor+2W#& z3MVz%V%DPkFuT{_n|t^Jb_ zcQi+rWKImx6$^0d_;tJNd_4AnQK2c*nI9dBC62Vjryu%?zfaE5+7O zVA9yKdG+ZfU|D&s|~*kzDrtLGEi1 zH{9@xOWBD+#4C2T7jc;_8Kn8mYgHd{YR@ar`#^Em#(4223yghksHL?NqqS)+%DJ?Bv8xq9n%lX*hT1km8b(ynS(EQVN(9l`dhsVfuOz?A-S-D2f#i%#JSMlJ0G1 z4iiU1L%v&N0_{@YiXCA(Ci@qj=;?K&y&N1WLV6@UoN09`auh=Dsn4L#cy_~Q-d84f zX7_8pEF}ocQQT~6P1dgK7osOLH~FPcq2$`Va`fJH)u`i{zd*ezpg5?!Tg(8w!dk1+ zu>VE^B$2FsqFrkojB<@jw{+;uxPAJ@5R0uQxiBqMUKZQ zK?ewK?GAHu841+C+dD0@b4U;?RqnzNfx827q)m`egJAC$&Uf4BP>lZ|Mpxiz?CSRkY_5?Zc;jffv$iV7jbhbR_?S$@Gn9 z@{&b$IOdA;W;j`u&T<9+1z>2AIj{}L3hln-i4cA7LCd~fwj0Mn2qjMM+yLwPB2yq{ z0Dg61=z=v*_q&;lFqLXo`a-^$b>grV3~0M1Q;p;C+Fm;D}1 zn!Lhis#Hya+4<(?sy^3b2lRcR%To>H!sX>ErMyBDJ|I=YBCD+pL+>VI7ulx+2 zt?L<@`DJc7bMNHDDbZ#(UyqF6VNRpT9^#GK;o%1d_jrW%RNgF`=C-)Q$~fU7bWWyx zmM>X4_}V|YclT3-N56R*qoaW8ny>2rdA8S({+@$EWiC`TzmHipD=8zpFpyodv;w{| zlCAN@Rr|r^wDneB31awkjOR4&!43G&`xq$ckZ%KUA#1?pa>K2wH|4_huqaPet7M`g zssCpn(Fa$~6?^4qgFX`#&YL3XonnEheA&?V;{1m8kjThp(Hmu6Ur|8%yr{Gw6vT_v zlxdRt9enNK0OL2U>uK=r870MRO+?#+G2%onxrGl);L1Uml=|w>O_{#Ok?=ef?QRtR z>5E%e(3kf27FSTitF+AgL%L|d8sudfclW38BVEYTc{NJn9x;z&muDxI!Q3neSnu~z z2?S}7)8-RGJW1uQba0XU8#jEW(ax?xM>SJ&lZTh%hfu_Hw5KUvrNZ5K$AAi+Ue;(l zgHTyH4O9<6cB$v~r)@?u0OkbnxRqTO?lhHK_OO$*KzG!g01??+0~jm$l9O>S9dO; z-o4P&x#nisPF~S}JlyVp)b<4O5HbT!{Yv;a`B)qqLjB<&+5SX3_2rq0M@FB9|73gt zF@KGd3XRVDSYb4wqWrzI0(2^f57gP_MZotr98id4Wdehu_~}+w9G7G<1ujQe5ggX$ z;1$8nh*?hbD;0#Abtw(G(b{%ml^>Qi`GE^q3a6FBPFWU|+5Yne zN`B<|w#2^%C{LlN8j83%lSFXu;12emB(6%iE#xKezzvty!&XRS2|(b^&&x%!=}QAD zDOeXTDK3RvuKynszS%L}&MkH63R@610>+j{n|fw$6l6SF}9 z;+VW+O2nL2U%V`tw>qjp(x*VtH~KTcn}+;!c>Tcj^9-l`&L5ME14P~Jaq0$+b$7-^ zHEioAa+7#jFwUf(E&o)|r#$q^BeJu=xr+WqJw>K0klX=pjvorg_`as#a zD%W@{jRxIXqr1&xFl-a$>a;eoJXv4Bi{L}<2VQh%9-ZRswoO8u!+vp4M#?Z_`xm^g z4l}CfG?j}w?&tnr^yUOVNoBKjX~9=%L7!IIFo4Z;zS+h|madB__LU-0fusfccY-Xh zBefo+V;>$5zKlFxo%^`@(*pW=XNQ}M>A~go;0o(NJw@4lksO~ecaMAvi(`RaB_oTCcjz%V3JHWfNMyYRNJrOH_eLkp=YOvG@G7Y+oaAE_b2c zoN&N6rT9^kJPb3IqQf8{i2zIQvsW1gE#dv=k0lN`KUvc|0o1bR@LSvagUZeo9@3#F z21G|&>^47oVMFmAZ_xZvq>&#Byq*tra!fUcfMzDuKL2`hBF_dfugw+ET6}`@rx_ zEv?gf<_U(*MFJXuV12(s2TEFkL}_8p zG2=oIM3R74TP{9+ReoNXZnEBSO+~p+Iff-?)R@nZR48IzLkrua{_xl&^;09_H9}i; zi8%@TTjLHL8=s8(h+5+otf&khi1R#;bwGVWZP3GwOZ!=O5X-!cdY+(;6JeO?xS$WB zPXwg+#Fcz%Zhjp$7%6kWP?V%%mUmlrq9fY{^KlQ&>-$w_F$EI|#M7>TE+uvh=x+e| zh7Cbyay?>JBYQkf#lF0N?ySQ@_r)1f7fnyVt0kemh}bbYv*6Jl+;+&a-&`w8U!P{- zLG_?}@@dfPK7CyDWnYIeq|}_?4#FdxeKyK`XudV7-a1e_P^ZVvYE{(rEa#%oI_ACqk2Xm!HXUstj*E>pZD0iV9W>hss-z0x?ah%EyE+i z<3&{T0UCL@s8%({Xm>1~c2rmIdL~+KuAS%AGvgWB5hA6y8w%LEwsvWI?4(DGDPfl#2oD$=tFU5T#GRe$N&tHtXs|l#?={qxpOlZ z%s6zJck(|Z^YKF4fTyc>m&<04jWbTpF%Yy6eoY+^TA?=!0Mp0#xQIJ)>NxLnwNGYJ zduh>ei6HOTP~@$T{QurU2@-nvwR4U|U4;PlBH^-0cB;A4C3qrGsY|zTRBB0rtz}tx z9w`UN+QZ!Zva06s198Bi79;OxSMF^d@a(F^vDfc%%QF0b89y6K8Hp9*X!cAX+BQGjL?^Zq6zGz5#2^NFcmKmLuYF`uz!05r z#&=PvS$&GabBBbp()|d%;~umJCy@By_WMvH$@18}*W~En1)+%nI zfZvZf68u>G-Z6~%^ZaQhSgWUmjm$ffWjQmCgX3t^YkV@(xi9RJAYb#vm5P%MA(V)S zxaTbiIEUdW0yHfyDxQ`Io zy4@m$0$Hh7iT!Ggk1cniWo7y_y?;C0SOzpyOO`Hl>j&mp_qLI37>~xs6^j2Xl;A`7 z85O~Zsni-Co*0zh*O%(ft|`bUyS%(IOf}JJ!CJEBzS%)j|5YkCZSnFye}MivX%zG;g-q5@p`6Y??A6De(bV0Ec;kIq*$DP`C8B$Z{og|1*E|d1MjOFe z0y;@O<3mpL;hE%IbQ7K?-NQqZD34gx*A!9TDJCujC?UgWm4BVX0N<@6!} zl!sWRj@xlhoqC+y>`Bmk3oQ9rN-LVdVWOP(a)9Sb~K zf%Nj)z}i18)dADmwx!cKk1+R3JVfxEpeD!WWFSeNMKb!J@ZSjzz5OUbUkmg`jVR*~ zMAke($&eKjnD<6idA#LIXG&&*ScX%70!ZGe3CyW5z~DEt@($xuBz%|9kuRMIT5$j0_Gx#$?+g-5puAIqiC5rat)#RWiCbmRVnznh4v4!OSt zG2i{3e<5*aUeT<|>$(|(vN^vh&${ZAefFs}z{9ve6V_aANn#N@?m#o6B72>ol4*So z{QAjtbzh|xUUPAg(qr!@p5=%gBy1b=ALdYn3qa_i8-$q8TAjAbfU_-oaN#*S9p5U& zK{tzRU7qQR0xrb0A9tep{)%ius^3o}lyF084P9a{m&O1(z%z}yHn6;0bE4k;zKv{Y z@nv^LV2S01C3p?inlE3H#0YpTd%{An{<7aBcJR5eaJsVH4^e}b5Bi=vG08DstY!Hc zOLAzds9(zEw7a$Kpsqof`KfTA*s+P&ZvH2Wj4c^~1D3cT-u>KIkw5A^G8Z}Tjr)m6 zPYN8)WgB0k+@n(znac>OeFO<)vr~CYx|ivzS)G!b#7qInkPjc)=vgp-h-Z6+{@kjm zE)a_E{I@%7aFh=sqR__iX<4^ z#$yE0LFHm}LErMXP{!iG;2L?h!4*iFPQDST>R5V)#!;>Tv*`U5T})xLc9A2>KT z2)f9k)?E;8dwqFi$pc7M@vUsp&}_Ny`tf~Xf^u5Y!m_jJDwNmf*izP;DFN{@*W);= z*!K%9l&c>yYBG4%3C7TV*Xr-8L>92bLXbu!7Th{4H~4<*K?oq~0p0w68JiDuO49c`@S7xVLJP9@;sqg znz2>oB9a%KdO5Fq&w#j_(d%Bi2=M!n4|N9^1rY|Fa_Y|7T2U)seiLK9h41ah4zDkt zr%LiXO{+4<6~APeH|9u(PzbcE8_-gqZOc1p1#Wq^sy}KiWItTOZrK!4}+Q7%-Tr;F*i_i>DNgN;UdA@BU^#@zwjS16h_WPO<*<&sYXLs*!$Gi#36T2E%;{{EWtY?JUbMo<#CGUHL*BHn>Q3o# z|AjD7f^^V70QJ`Tq4k2_mV=I#{0!^jt9#1HoA)EJN9sFt4wd*>wnI%ZM5>0yZBB<6 zP9q_ookV*C=bupc4`JU;C|B&`vCoz>%k;peId25Vs46Ow{%YW2Mr94@rC zr!Z{V`2!}KlTm(dRmWu;OH$x^TA7U4>*;%Rcz8HjtC!fqJ)D7fqQu0#I3QhhY7VDt zomJ6}b)eO%*|%XFGdircQ)?b`>U-B7^S$Nlb1qyT_w_W_tuSFhs!KHPOSb+D9+Lr@|h{mH~T;j7aAO$Va{QF%cfo-Nu> z6S-l}Yh|<1Tj>R-^tQ>Z^h~qChTDa^x|8)Bi8rL`)3cImB5mb-0#DQCELVXqtm0dN zb~qtDnhpyld=nNGe7A8=;hdj`HPM-$2AQn@i@#sfV)mZO%CWg%Pp#W=l>85jYWLP$ zhAk?5_Bi2=8ErSvlBeyy5wzpkx9KjpX~zM$GQgO3%O@SrT8XB`lU#Gr8!(}Qm9V0` zk6jpy#C@w|A(XZfP*1|m`SD{A!Bo9x_$p4#peK$m%axBUYu);CvXOH+uKnCBy_?u{ zkaZc+G&hG*Pl4i;2P?Uy>NX$i=+u3hThw`0m_r|LIALVDxtCT$IwXi#vUc0IKIr)n z9hlUsV}$+Uhv1k^MCb0OQ*k0X-}!#qytMn3k4?*r$Z4C9pWajVo+o9R-8ge-gvb#R zPnpHDMeH^3(|Ip8@|+D0y%LIs@h+rwBHr>Q0dL-_E$|S@0Ncg%=IdWb*D*kfN`V48 z2}yYvdtPCoaBt7f6^yB0Sc0toYMhqK)7&&+C72vvCjX$SJQwQ>&>PV(M-0^l9>`g(CA+ z4fT{as9-Lu1{%51$af_2G_weMiqMjo?YG9@b&7|G?TxRxeex%H| z=hvoYDU|GH<_>8d#RArIfhk{B3&Ij9P=$amfhUmp?@LHfMSw3E@o@ak#o|h_ithYK z)1*^Z`o7(|>a{l6>OMzIGX1-9L2wTP#A^TqV`q8bmm{_M`}ttEF?|FA87&!{eE8Xe zR%VR#C$><>{)R_MnGy1J!4OvL_5S)o-vb;cz7xfutt!WJ%h+`jD-~R-;7rgmbpa$Fcds1nR$HMg}&n89$~oFh8^p?I4O%>q;Vx4_~Y(?wC+(dY@Yj@N>F4t;B$AW=H^el85X$iMrDOiWFus2@`!Q%Uy?k@2<>yMI@dq*(7r zDS8GI2}G&I&1>9yJ|t)ALpu86r=;ZNfS>LaqrE(@D5DV*=Rz%dnxQGSUw8Y#OX6=T z_*UqgtY!QPXm|ntTPzo;sA+ASF;aE62B5ya6KLoai!8xMFeY2aHpILdP3Fsf`tJW}6?ER!}`16=(KFf7fJvFzGDC4{e+P&;L=dnNCcTdn|m~38OGDxAER%9Z=|w z^&vbd*}Dale(qOO=Tc^4XI~-vRefo0vWkckN3lj3b&_dy>iqvyI}(sSG8Z6F|F&ba z`2|_*ulf5W>eZNrz-0@M8}3(c`n-lb;W<#N5ZW1ZO$v3^_kH~$9hTV}-^WJOQss~t zVG?)ZQ4CO|;Cp$23xUU0&6h;*i$o8|_#{8$=SqDF!+pWa21B%Y1^4WM-Y9Q&vUN6X zpOfObHRwk+BQy@#?DqLIn?MlSa@qF9=l|N zfWMM+Q3mx0EiCwZ91W$nyqgryPust|E{V?-SyV6)2(@jV;f$m7ke*(?hm-dvKS_f2 zbMdy_)UB~Z6_^`h9;?#*1V;%a!)N0SB6k*b$60=IkTGqFTtGS6c7@&AytX1z`nfiH;H2VCX|6E z(J+pC!!4cyft6EWyuX2G`cRR$>hmUY`gtj11UjbjX;*z|Nk(6-n<0UJJ~zW+Kh1NM zK9)>I&@wwAp)n>lvx3MQ*V;{e*hWX!y|?$Ev$&%DOTh1mbJ74MOPs)@78IB`@tzxAV^zktcOX0LkbKc4#)1W4qVvZQJX0UL*x~K9|lG44jo=?;?Txya~q8q;Y|N z#wA2ZUz|CXH56N}%v%UL*G0Dpo<;lDhb}`}kyp!C=dW8p9+CgNP@wC+Z6c{-GqOtd z3PyM|cj=ajCUoTQ7(ace&%K@3bb^ve&PvGoO!QgaI=4i zS8Io&IWs5Q)iq~gvnyBmBo=%3Q9Y(hSa2N5#PnVY@H9NVG z^*og}WpP?(iDqoQOdilcDcO$1c#=Ba>bGkzF@G)~{wRD9PFV5y(0n*^5$HO0WyR1_L*s#Gdd7cT z-WebMhv-%4may+avMhiHM%vob2>&jF2&G_kN8@GKhy#^jG#j-RPU74Ml?KHp(ws*%}mN}3u>XceMg-9*o`4h zBB2D;Pnpi!9)qv?ZJJE!edpbbI8rZj*p`ufhjL1*yF_`9KkHX*ENK=kG;39>*ljW~ z9&BvE-Ha`i*Jw}*O7POl)6URz6;ZAJmClc`xqVVR!Ikw7PRC09yJ=25j^*)7Tq|Ju z=lsEYs`DAXiAnXv-g576$1F_OKvXwrtw)N=@sX;>vw2pCSM~p9_;uZH2XeYW`jdTuK1`x@)8`g58W`X6NVEv z$5e_(@s@yHWF#HUh??|$M>5T(qSIYc5I{j z*8}<27`#8cvb-!0b(gmJJ)Y(2h~ zJjIqY6Ynf6KIJ%W5FX9+Izob>#@k3^OU#v|zx__eYkw2k)W38;BsKkGVd`ePqJ9EX zb!IXD*XZmdv$qhBloHJ5S<~^S`2)WaQRX)F*6zlxk1CN-b{tj(%`j=g3XUblx_5WS zCind*e!VFBrhVU(5d1zpJToxP?LGW}bk>hqlzkJoziY5RStjbFmFh|SXz!N=Yk{+` z+%qggEr=qR57rxt{JXQ+sNO%(^Gte-kIkT8%xu#naUHr&29DI4ir&&xa zK8IowL8Q@V#+N0C>^HCjx%~_tS@k9T5U)uyOr@9=5o)r`cxDuz78$YL;QAY1KxXp; z7o>1nndB{`4+lHJf2F^aS2-<5#Ct^Dj+4&Va$O`|FBZvjYdJipjXdU|zyF%-N2#N0 zs%__Jr6aw$>>ZzwkNXwU)vFRw_egyOD=wW7N&c%{HSFWuG|8QS%<0?akEChcp=iRu zZAG&y;a2?nWbRP{-^Ym9UP52C<{VofUJ~R4KeGHe=hyhEF>4V=pIr9k3HIJEjN>;_ zVv?;JZ}_T8EG{&Q+X&4UnLKi?hg$Tgbu+?JwreM^NfV!hwl3{-C)~u@R$1)le%R@_ z(5ky;59Y|oF`H(PwzZBsms^sfJ&5#- zN>4R=$NoNR#2vs`-(L83kp7Z)S;PPz`9OL;`n%U{_sq4dxr|DtIVfO?Do*<0mvB8= zEK>6|eDugOlhFsRfH91WX)rB%rrZZqlyF6|$*0^_zQbEp?EERol4q0Z+mN z(1H=YKLQ+o!JTF4F~7g(LLcCR1xE@WRHwx~cV8?h>zq&5Xo5^Cdofo;Y|crWh@HBF zv6M0*-@BBAuQ~O=Z($XQgLdIC6AtQDP_s|TUG>OJp-0H4>XhgU`ZH!so{7OP4ff~f z*;veuFrh@|_dz=X%ZqBsH7m%>W`?y_B&feu7!3M=Rm8%QMGHRt?iyx0lTYxN2h4dE z3sBMVs&Xwr(>kEQ1=ql zTu2WI~K^X3! z18JMxDZR1S-)@yQfW|r#jkW2dFmJ3M)POSmFrX~0r{9?IzHTXC@#%Nj^|wyh*~v=? zT{MpReNE<7<|yN4Xn-GL_z_cO=Cb1g<_1NAwgi0@y5%UyaU1!ru1Bta|O9;oX|yCymu}*6B9%4SSLy*h|?S z%|DCo>~?o!XqY?XojDYh@hJxUp1u-4Rq!-=m*|F=G{y^g=P=lk{9WuXfE9#ngVr^= z`>ZQ8iJ<%a_S2Mmf~4~swlr|T5}lhL*kf~0X|v0>4qZb-^ZhRpaf3HDZ6G;t9>F>kq_CHznciRDmEtI6~Un5=fs73>`j7Cn^1HSbn#to-90ngqN= zEz3p3vlK~$aV`0}N7 zcY@$?`aL2qd4h4HJxaB-eI4ZlP{!8Ve&UDWbqlAkG(y{}4f-J%CoOVWo7=)+%)+7w z+Fc6A09A&c>nzRA9b4b2Qe~9B0qTK|-~Nv-bN^iZfU_w&8U@43IF&&rfRHX+bREJ} zrcxW4$2u$FX$-$`T|>Jl!BePQ)7!u95V&$XP8vvpCC3aM(R$py2T=octY=jvpemSo zOVqmGa0J@mPqn-}7U4wHL`KQ|5c0XQ?8CW=;>}|HPK&|rxTjBe+TGSxbL?}G*oD23 zPV`Tk*NnN(S~^AB?b!(CTOYCA&vNEFGv`el@V)KYYp-IdVkz^J$(-Nnb{z)T?$%Y> zdKCHD1?K=wprSQuV3D&Hu&MZ69=`|zvE<<3;GuZbz};*_Y1rh)4ijDY@l}2@LEdY zouQ!XNeX}(2hqKfU4lvlzS3i)(K6X-@i}FcN+jmr&P(KsE%C4$Wi_fMc0Ia)^zlS9 zrRoxh4^%w39XD3`5odmQ-I~`vRaajxu#Gzk^IimKUR9p8Ykp>ws>0vEFb5YDbTZ7; zt}R=X+!YQ|%Z#zV+y7WOqKAsFLChtn8<2PI;_Vkm(ns344h<(#Tf5Xp!3&#I=c?Y& zOd{68KNAwl85J{;vp#=W#3i#31S5z9(uaJoN5P5VoXwNR`U;y4#8aVmUjl)@(1$`0 zG;0JYyYMXue(m^2CG?Dn(@DXs056O%o}6NvmD~WwR91uF)ER18_32JF@h;}tw=QQM z%TtBOu6A$+t*qXlll{TsH}gQvv~`qx?T*RF`gYZ8hg5I7NOdjshj9(O3=r}Mi7a0| zvmb#D9(=iW^Xyqd#AAdOo4>pF(w9Eeeb;jWIU?_Q*qkt#w^d2DOo1|d=4-04;rDji zvOl$#!@!vT)UB0p1|1wQM85!h#!UniQNPA#ZodY~Bfu-P?;U6Ly4ja?2nWU{^RRg{itB^Ht8 zQ=QZsD47V(A_k&ez-;^-bg`xWc3>(U2LcKt1#<5mNxbyugpXFm#c$)y78cfUTUCqg z))A68E5KebDC%G^a}h(B3QEvJAkgJU5m2k__;0MIsB}fRay!y$?bbV`A*rn17pdWz zz4`n{B~S$)M%uCQ73UpIf=;sMRdd%QqD-6lg=lQGtUgf!5~6#ZX-q)$KyOAunx3tA z>{7BHjkTnJ&k8j7*PEqO0(10!?6ouqyr?%~E?c|uaoEhSsHovDqWAje%E5%|MUjPqI>fn#qCS zi;s!&>%AA4^7pJtXn&93q2Mn7aRweQUUT<}r9(#it>YV~OYin;rn}r{g}yaHlFLG@ z$v{kC$KU_JyQFkk_HLe*K5XVJ7>hO{aV#a}<>(mmRa`LQ3V(MSV~>pwqo;p$toFEAJ&rL_u79TKp zU26h#05V*N)pN8;1)rxwV` z!a?GLCcDO!`dmiz=6eRCPn54OubJ46ypmOUfMytBbN$)C(1q2|xvrG$uhwV!iH2_o zUm;LW)-;=&-UA>s><@P8Tb7dh4?9sau@KiA>~^OBFNhSV9c%TyO&Y6DuYt#Lx}CBC z6kIK9*~c-g4l76tfqrht)f0n5%4c&^IuW5x%y(uk_w}LypL_|XlX8QBDhWwS0^Nej zHz&@wi}so7IEU0lw8fI-0G7<17+q8NyJxx$0O{-2@L=$VbV-&3<$s{_(jIX zqn6s3hj51cjl{^=bK+C!pAyXmjP)642?@b8E_Q;kcc71dF*CED5;s2#{aSw44#}dd z&AjSjY@b5p9oE*GM(}SUOzmCJxR*Ebr=;ui6L^%{+l%h2#R=&oGr`%sB@vr&mFBv3 z!-3QwW=NuAA8E+IMWf9=QwtF}ISnb}$(0p~2SB`*7O=C^?()bp!t!!Lulun9zzy&x zt(yb?RC^!0e56gCiHvYh^z1@s9&f zN?3@fzg`gz4lANP4-OeQGX6jA-ZCoAW(gP^7~CC#ySuw5I0S;b1b26r07(cE+--1o z_u%d>!QEYUxc7bUw|mb1+MmmT!}Ij?RCQN(S65e;7&*r3trCxIe%yE;+k2gXe_9_c z`uu)PnE62mtm&sz1a$Ce9*2R>7NFgbZHz2i7>AXTWlsy~5+eXX$mon+2pM(N zV=4+J#xzRy#SHLGFg}4l%cDjq@AN=OlhFxDR#_VPza|ujwaI{)?#-+Q-=zS;Qk`*Gz&(~Rk^97}E+zSZo$qkzhyJ0R) z25LYw_JLFyD0q2jx|kOje}XUVqd)NkOZ?+<8@Ndxz0%*miO=-}F2Os1I`?}lLGC5Z z0BjW3B6>h_UvNsbxy13el`^`9tEv$(0!PM}We!DaMksY#Jzyu7=Wf}rSsD<1wE~>S za|77_Uj`d&U-=^n8B9ENAjGp-*H%B8T_xf%KHm4Bz@1a|9}2Lh31MCXfptg*MZ`%- znH75ws_LgSdNfD6fHc@jpQiTUtfmJsc?AKtQ=j2Rbm-q*?*~AKGa@O)f5d8dOi@bP zZiI#Z7*vJD1Z2wiMg80qj-yHHHSQQUewbE2k%-etU0I_vhB==T8`ifK_pjE`>G+La zTgQZ*oLKNzK7l5yaG!Q^dF45Jv=;GA<3|HhBoZkY56p_BV&Ffr!4_4(O|8dr)qk$_ z@;Cy02nf5M-;586fT;K)5xHecRn(j47PYmR6vl=pgAB8;j^c)D@x+sv@u8M9bC||} zl$296Xz(n9bM@)T{>KGSxuWN4dZ}^A-SAg~h>Uo%73V>6EVcvml z!8j}kN^lnd_W0lA{~?tGn0B3?;j6*M=b~tE8x1Jm?`FC8OXP`-FsQ+Y+3u(6ZoN|R zqQT1iW80)DPGg8aQL|S_PEHOPV#cf8Kvw@(JmrbmI>^YB6UYiJA0T)%D8MMrPd`Zg z4+V8V!4?@q4&?jthGT^gdK_yaSthIxw`HC##TSI&l>$V;3@p@A>C3HKX@0J(FQjEz zSNKZ%J1$!G7^bjnHt8D(q|@fby156wG>856ZZ6-XLHMuIhVrMV@vm<(M;VdtD+O%K zC}^0QWUFG(u(vYKc;XDXCZ+xyF~}l+DC0HO1w>nuvfmlRX->}0|M+BYsb#=V%cyHD zO=N0bu{lp%{tLGhlKN01^TARYtYrX4Od!PghneK1089WX3Q3RPi5nyj*nII|T05;$ z04)_0**X!$HLKf4V8PvRZg2noe(LxaZ3)dlBOJoQQW@yKSe@R6a6T4#rY%-XvwM2=q2ai_tuakJt`$c%xDqB<{l(eBM2JavKj)iTkI8Pwzp@4d8u1p#Yt3yez+>Kz+ z*49BzPOC1{J+muXZz)bjfalfYi-uzb9R#Wuz!;PwjC}who$1{b^Y7JWCHnrICXbC! z4aI+mj{@~yl0A*Q#d(`5)r0J|0zp~c=mB4=iH11l7ury&|A+a`UsjEa)_Kq z%q^I?Nh=rqgQG(Cq-xrBV*->lg6|Uw*GxfLTQLw`cEo~u=@6Jw3BvPo8!dquf(}TTAHfUGc{$rYdryf9` z9YPumHXyZ^?0;av+fTGj&WzMMZC;^fuH8=AOV}yAW_^Z*XSSKCjjH-R^D+zKs!Z}an z&_<<%6#v^8W8W!28Lj#s3OJ%^nz>f6Kj_5qTAeM&H0#%IOHp62ceBg6OR3)}954sZ zso&7n)-%VZJUm$6*YDE}4AIA2mNE);RhA(3xIZp+LFm-5>b-Zr+&%2?Kki(CSnukN zP=}(FcXkOw7Vz;Izoel>1{{`k-=3PkI2(NOufKBPNRkq@?MO%%&({+REWY!WGo)3e zzj1Qr!B|}4*P(p-fANn2FPD(YD)jf>$XW0n7NwbWLj+} z>?8C|An*xXIl2p#Q{`Gp8C!!pXF{L_5gar_5IxWh-`VW_uH*i{gr)^k9)qgi?fpI4 z2a>fe`+=Z^w|BdX`i$FB3P#v?P)UA_ldmuD^sPaXB>N3|X0DyqGd|us{P@s(-$c{T z>#=a#)bQO5E=i{^@ZV{1`A%!Wgc-^AX;DNT(Dw{m3Is>k@as%S`Q;2*UTww8VpyD? z{U?@sX*qE~&AQ|d#`doICsnvmS<3YEwv3$)Eafpt)_37%eTkEo(VD>r{(qAo7Ap9{bv{b66We&%9(xdM0HZVAX|w8N=qS&GE-Zs z>T(C(=f&4wHtM7SXz!@ytltK#k?TW8fw^;Fizy-NKT{3q{h%pfE{*&jqB27{7;kjg zijJqK#;vuP5j=`CV8XIgZaIPt-|;_!-O8ykXrQ$7Epqs@sjq_G%`^;GXrf~O*=}QH z0egxxYIO1cRXF8AL#%e5t*mmV1d~g}n-4N57{B>{&!ds&Wyv}4zQes+K)#bB zuJHD!`i2OytgM-V6pOyvCsO`h^q^k(IQBo$gR&EDj5n}~|DDcYC62dwK#xq&w|zqA zk6+D?WCFFcngz1jd{NL^O>tAng4ucBh1RjQE$M%SR?Bxri!V$6zE}f0F+0iM;<)38 zV3N`6VOu$+g>VYU-uciu$U*vFl3BeUKvGz5-shj_Ib0{ELn`tMUYslS8Cwx7jAjtI z<~xqLmo1BbaQshv{r}ek2KD(HguE5I{%AjRBmn#)^HEW40PwAf_^c>} zjDU{-{s>t{T2ci7fB-*20N`N2|8$)S&EDUfRiwlLoWvQy^qNyOyZ(?uDZ1mOM*p%7B)&X1_01))x2S3`Hx)_mq*xJ}R^Lq%9|EC5& z`1!q@4i;OyC+!&YpHIMjlLd&J_Oz@-G}oQ)d$=O9vNA zdppv1xJJhIt}a64%b1A53=6|8xu7L6-MVSlF0ZS^ftb{8hpCQhp^T zOH(lBclg3=g8!-c|0w$hk08rC^8Z_7{@c_4l!Ch|j3CJJKe7oU$am9d{hJ8X1L8Ox z*5~)f$9|f&3GZ*8B_%&&eO6XwT)@s>P!tkU5*mJJNNY3VJG;IX6l%LVZo6Cic#Qlp zzfhGfNHhRV7Eb21R13AEB^}Yg)8WqkvyM-mFGn|3oIg* zz7Z{kRS}9l3{*w^|58-hbS=8Hdr4ZfSE!7dSK<;i(e{RrpQ`rCGGFz};RmbEz#HV| zrg(V~N4djM`K~F3 zN0MMK)h-TgZ`ZPorn+MqC8iL$@lInX4<|r_q<)c+3!bTjHxtGdLQR2PTtp_NPS2Lm z^xeZ+G1Hc5+0)tnTE?ZXPJx5i`GQJqa_Q)_Oy^3q4Cg8gjHWl|A767ng#0?d{e72u zj=ib7DQHy13Vj%OJ|^ob`}Y>0*xVNSgb0fWi_n5dA$~OXB4483CO@9>l+xOI3({$b z;df&#(`l%QU6--14G1GW5C2*Ek$MCWK$ay_YG7cjUY24ySFUHQUXfuQ`5iaq<9A_| zHJg`jpub@c^n73L&?NN{Z}61r&FW(R8j5^kEozrwCDlCZ)PM}{|M zcZRs}_#h(U`dPogSusqn16O?MxQXy&;obrTjviM9NAX?Ou>&1GEi#g+vq+aCL|-e8Pj|Jqbe*k|KU= z%kKsa2hP*yuYJ4z{POJh3Kf-~PY`N7a=9}-y3$mU#OJK9@%SXo^T!}y=v-s7ECzwI zuN4ajjuMh9ay zf3oM*T6H_{85SLYxv9PBWYi0ZUM|yI3I;X8a8PVjj~j)Xa|jDC*RL^4$Fkti9BZW* zaa&HOuJ9cKp}Ks=pk~GPkox$sZ+DpGjX2x}>FUFT6K7Cr2o%u^t&P(UFdw;|I|O(( z-!g9UZjNo zG>E@SJJ;XjQ5&On`~>C*uRFS>a&Jlt%Cvoxz%&bvkO2>6At-5+f3{oxubKOgNNvhPWH=dC}x3L4|U*vtdLIU>a`o_Qv9PJni0YH3A3ULn7pfaat4Uenw zl$oSQMxHkCNIUm2ikg7@uDw)GWGM6?aN<%;8A8L=4V%f3$v;;P8@-YhY#f6NH5$`S zt+=H@w|OlRkX+|cb+@i3#a9lI3$gdS^MWsW&-J@cP_xb|C+Uw#^PUZM(H7)=4Lb>1 zH(D%)&uYr=6YC$JCTXJbIYJvU&!vrEjR0PE!kA8jG9A*nMls~b+n8AG8)eo(kEpqu-; zhRN<1`Aic_m2M1(S?|3V%AmDjv7ti}O=K2G^}TJ+|CFazvbOa3@3`4QrSnOa{OEd#`5|D;lH0$1w}1CT z-x@*L&K(>vq^w!QnSH9%aS}xdQ%qufnX*^>)RBvBHnH)2<<*oI8~;9$c83z4A*B7m z_-l6_Kwu#*hYEQfF5`o^h~&M2k-HJV$$2>ioua&2&S6pOo9iuIgu-F9>qam-73X#TO9S9HpHO7WqiO zhuxoP$g!_8?JH}k+3R@&?rGrfu0=e}%rf#`9#n2vVl_HEH&i3(YEu5`!hIJP~$Vsr-tJ^Ic!x`axSow2L{aa(Hq!<|0#p(cHS_&|hLlwb4DOO-8CSxL4Z=RgTpY z4_XLl9rT0o_xnq9mGGE3w0Z9I!^v*jRz}uhY+UAK+qZQQV)!sb_b$m&tVmef6+6FBc!y2r!eyD&7kQXHKVdRSur+gJLd8 z6fV%K%`UY`pLv?qMywQ_^2?sONl`X@{`P$6H5C0M+vKRR^6Lg9U4; zesyUq1C6W6sE58qjfZM#>T+ARO~z=tLjym*)DAES%+LQwejnbXhPofrQYI7hFH3FDAIq;y^072%iS5dB%7r>-Zwfb0^xzn&%)8^0+Y2tjQu3FbEeK##RSS`A-K>Z2&%*DJASas-BPHOp zxw#1=?PhQ0=4XX!aV$qZ^YpI0sbG0aDkC=bbPZkPQGua26hr!PjUHJOBZx-kx- zvJKmbTnL26m!gu8OjxHB(V&7z*sS~h5R=yK7uQDd#;(`Vfq{9%H4T==+(m=aC=?>e zRdRbAH^Ik!Vti?bVSsMgOmAmLFQtpda1^l4w&eugYcv-tY)v2q4H@S6UJC0h0x|pf%1RLyI#&^$qUA*5i>ADNC)qSKzycv(aEwJE??cz#YO%n2 z@ngA=J2;6M-xr=jQdqc{M=U#wu{@nQ&J&~f;Jt;LD%jvar z`u+i25?cf|r-w{e`*SN-uKGSaKH<}1dt*HcVqJcZSc_dtMCKu*e_EYi5T*fIG-A3U zXZVBb#qo)tQ-L!^H1u%|)(QP3JnVu-b_xJeZfR-lRQa32Qa|TUsy_%JH$dT!Pm+FJVh(w`cP;-wl@Xd^&M zU_Q}%1vjVy68DWaGaL1i(lK1mDXSFnPM>Syg})WHm7v}Xp8i2G$|MZZ;@;vCA51 zqun|54sovqrZC>EG8;yhsdYKsVyNE(4^VnOupxn>JoV)z*fX0>pI%N-3ozACaTnw{ z2!#0&N9nOFcr{|-E`~_O3cZBLf|X<42dUp()GZL&jQH6oO3*1b?U(nGKTrG5+<<~r-swtMfpTx7%*SDH;*as4pR zP1xd3UhnIL(K!Wugq%A7)HegO!5A?kc^x|dv-jvOHzVSnM4MxGCbug^r zNeN|ns^9zFkM7a`W$E7QR^q&QyQo}}yN)hgfdtLnp+~0pMrUMDk)JWBiMV9oQg=R{ zK>&k7ji79oZ|4)VU&>6P&qrkUL4R2{3A(q21m8BbUpz%El5wscbEV!ij$S(;|M68_oVEoL@AhoN(_c5>Rud)h?jX^Hx&Py6VL% zr8f)lBl8ZmvF+N~`EI)9kjDJ)lQH?U3(T&eCtn{vxz;&_-59{R)}TNZs55;J!FLGBTsxaG%yeGyn6@K&H!5+sVw zJ<7;%RB*>qK4o$1o5d%a<;mLP+j5SWU4hw(CXy!%X;~BMfft~k8w^RcWe(8o3%K9G zwRhD#TNAs%D3}7%iN2^-n)@#-mMg1t1YLKw_^v-ORt9NpumAJK`oQFjkfBO+X3&6; z=(_z%NQXNoBG+N zH2PiF&>b?e0p>X}Z$Y*{|Ku`%>IHCKG}&*r0a630rM3RolX{Xrjwo-gMxx0Itk;7(hIFkljk%RG+(VkUU5m~C=I;Le zSy4#3eXB6*s=Gl=z}-qdZJi{aT ztcS}(RTzc+G*EZ@t1m#5NwV5d2Zn64IMPR%M!a|Jl-`}(l<%8+v-SK*YG-Fo-%r?u z)uTD4#Kc$Gt6ofR&8@wjJXn(F#&eO_HLt; zLm}L2de_TrIXH|GU(F2f+`M3qLdXuC27q5^NOq-#CxFXm&8-E0{X-zO3X1FzUKDpn zGwwb24>@(*y{ z$Y#DhZmN+FvGqa95|1jbIe|&{^t1(X=UXcr;vGX!@`3ZuEc?eayk_!m{#?)fmBCnM zsj+zYfs9^6Q5cS7T=+|ku7Un*E?)Oy!g;#OdXs_q)%_yu;u8BdP_w}?j&6Tq%4U{t zeMBeJ6^E#h!&6ju#Nu>ng;aOn9;b|up?77496TjAynnjnoY@mKA01V2WiO!23R^I+ zqrHex=-B)IHbb$CjZBGGc`7-uRzDJYq#foGT=09Eu00q`Pv{C;-ttj8>p^X6^0Mc* zk4rkuclz_%qi0tipW?rW{sn=j%;vk3ayBM^8QgE5F1O;rJUkbyIy zBnse@Jt5b{8CZnGtitQ;Iz-Kcnb7gKwuiib!g0l9xk7`+HfAAb629M1Y01ah374b* zCpM;-K^AtHravpM2P&LBz%!44CcHy z6>_T`GAeIE%lWn`J1Hh^%S&m(Uf$F7p5^iP=vsc-pro7D!iqrmp}S{tV11w|U5pY9 zW1{y!jU~{#_d;+>ifb!|^YQqwjVa}R7M4u`AV`_PEF;qbP5Mk8@_mV8O{q6~OMpb4 z-QfYtIa3(B`eb_Ks3MaBcC)M=@N`MyG!U0SmlrOwVBYn+p}V@$q3ZF(gKhNgxb-!f z%zVLiRHxyTaEl@EKKbvr&1yN8=!AOx5dR9ww+G`&0tH30Fg~yOveC_+r{vX!1G70h zS}p>fkKW@m8_0-vF?=WtnZir)M-%}=R0Q!xFpg{jk+SGLF! zV9@6Ht~2kpdK9Jcf6H!_#t;14@7^1Ffe=C?FXnZ8Djf~TPKamvt>K?GCaH{jw6kf%-6 zBbOD&efoe2)~XYb)4`cwTP^UN27T+O^~VoRnfw|o%u{rcFTQ{KJDA3u8p46}?bZe;GuYC>#<_~zrEn)gq z`L89>k(nT>xf;sBis<_5koa5g;|uqillpIies`2DM?`QWMdO0c<`@AMP}tbc+uPpa zz5dBe+-8~v&Yic=yR=0w!;y13e@@Ex;HnCSEu)juO`Ncs;*;a7xl8HTkJNDfCK8@yNLD}+FRz8?6ey2O6Yb~8Gb_Es^R$%KW5ZEx07mUCfB}|y!N(? zatG(+ZG%1fguVbr<49Z1UU?J{qbh@&tVVU*6!rB}>R0{7_#f5FpS5Cq=BdD4@O6)fB89%On`x~1CUV_o%vJim zSex}lN~q>_UxRa1>hiha5=;a)8}to+gU^a3dc5TsU9RJ?bU^Kdwhqr(b~&?j+GS$> z@VSAM!8%0f)8nQS4%W>P?tu;YmBsfo!rc!>;bE7h>|QQ{9Stq%vFQbBg2?Rpzie*2bmsdHnP7=S@%ciqlz zi$TKjw5R7&HlZYRzd#O7;T3%N+n3F@*OXqEQOV8q=j0-nZQ}4gfX_@?c1aESyrJ`S zo>h7Di559BDT^^*xc_Q%K|apwY=-^1U$hZH#aCFKPCj;bMJ%FDf68)#Z9x)E4a4>f zdj_AI+0iAZqYo{BV*B-A`ZG)~U>>6|K)f`0y}%bdk9SyjS20AKA9@~L*yoAsN(Qkm zC?0wsa_kHy<~ffh26fP}^gG`SOs{n6P_`utnH{2_PE1e-T#pj&F9qoJ>WJDQ9VykS zpo;|DKK=5ma%EQAGU)C=1SZQCkjkP;ngDE7+YQd~U$>yHFG4nCQjG(p8EF}G=w{zA zbV2}`n>}E6&q8YzftTpwV@IhZCY^K%!%|0Byl1;bhLG_f{M~lPX0hHnh;p6UeMG*q z^69W*y-~J)pVG>MG4v<6Ep4)O?#wJu{EIUCY(s&8jsqh=t9mvCh+ z{~jMrv}9V&>7!~8XF3bUK?5BDCnhX`<$4;ly&ef?kw&tx$1Vnbtup)g9mK+Izvx-2 zd*4D|(`b5$rP2LO$5q`YqNCt!;~8Q$00-mW4bIrf2{|}NIrV^U^YnX3_Ma}jkGixl zd@9rYXkhZp;}P-sfGqq#4}vm3Gk6Zb1<@xHldl z8lU-7EOz|8)0 zMuIHB^(Gz5I4S?}Hjn`;Mf~t7fbH?G zFlkC;&lxjf1j$<_&e%)Z`md>+B^}@I-%P%rHR!Qijbq-{j7mfG-*YXZ}-F<)>d8YU^aj%dG%< zmwCNjnf5B9w^!DSAL*?#I|RM|+v{-zs?ltjsqXl-XQr?|lHaP;l@HoTk;$2fhQza} z=@nOpf7Q$@jSb^l=r?4J4uYni3R2Z~dIZ&%m-53=!bfB2g#{)GNe0{0qRR`)2^>kR9@^SHf&8`9gvqbEBeS(^MtKL4kRIahs~#~7@(W%{vavs zn7f;XlaTt)7KFlj=j?;|<9brRhKy}`w!mgAae8bv{qYOJeF1>zO;=O5Vvws`zr|u!XWc&Z^N}*O82_Dq+U1_ zPHLZcZnm<$kr&0?sk5=2x<2V$l1O<*U5mDyJHBr$rejPyV3^lCIOK#p?6(s=(U5|I z__kgY?_cu8El49@h0PnhorZfFUB?}$D6t@<*)3jz(6Ae+d(jH>h%wO6Z~5H(eIf56 zaFb+1?mIq9BT6NSaSn_UnJ6*QGjb;izg9K91s{NXzYDgE@Jw-W_VB-LA>Bu)!g}&) zW#~E}Hs!=SKO7u6M{23ABtw4383$rP!@M7G zd6<41f#tw~F$Rcd$l&hGI38B0wm=D8hVkUa#n+ij-$Xq0IJAD^9XB8%glI9Kpn;A! z4b((3Y_bd6Ks!g-N^kHU(eG0Fjw7ZJ@FwjqZWbqo7|&eUeH7dzGV(V6~f;UyIcPyVC`Fg}d%dn^V9mV(!>pVuB6QO5+_b|SWW|N60# zki)EdO0nDPJ7g=j+6H=l{pvC+-}7b3^ug2;Itlv2Ze)Ql+n(TkU~-d1zGXsB`B3EG zzz-v(UT2B6%7>0}xZht{y6#D7qKr-Hdt5Gpq)n^%ZqbBEpQZ2H&(SxLyC=KryRl}f zVU5`UpX90foWs?Ax9lN!ib&scCqU%ocg>bSs#I0g0m5NRSpvGmo&~LW&ySOc&)W8U z9TOj3nk~p#lS)g;*x`w>g|pjj?bcrX+W8K~ct#No#@Ibms4UdyJr!p!W?EE~p;j0u zB(72&JD2M2)W0CqIJTpCV&b}}6iI~QB4lC>nHW>Uvw-c1!i2JnlHdaPd?<9S7);hg z7_2RNBgv4uwIX6mSkAnA33wc+j|kz`0tfHwh~UzRyd1CjsP{z-63syQTycD?YZ$aG z0492$)wH*PuhZ22Bph~6o32IL^B~IxLp~a)gM+ebf{IVsMF~j(>W(d+t+Js*6hiS9 zxtECjT2FV7mD^FPEGDIvmY%uN?}li*bZHkf8Vk<(H+RHmA}qVHRHwmX8lbdo&Dnnq zk=5->do8|pd8Jf~L7oy4%9}7;Osc}HoouzFw>QnZq^a8Dv<~KP@~gbpX&k8H<_ev~ zYHPTjnjL51A~APvuBK+UarPTrHy6rFJ9l)>-TrHwCoOIqxwrAF?J@3Jhyx?6N-n%Nqg|VKEy;;R-yYrE@pgR~Vs198;ottyf5PAIR<0$4t>*oGt+-5= zsBECihl;4`_8h70hW)}hz7@ zaK<^@eK|}1^7a}%d%jfXt&GGhe8Z#)Z2S_ES+rJZ6;3%>9M9( zvTGH#L**;AnP#EZO@@LCbNtN$nrF{v!I5!6o#ud2jDSpj!*MgXJ2O;7JqIN|I?+7@ zMh5mJuJJpBqOp~$RXWZ{Z{tVZ0#ePOI6(bVH({1-6&f;(S_V^RxV53LaIuI=gryMo zo-W8}c4BJHJ5ss>o4nEFr24q-$SkF(1}DWa*NyYRlP?BAV{~|wZT|Jl^Xl(2l@}-~ zWs^^)ce&iE369TtPM2t4bbwcTcT?F(^;Y%GOX8RCH;5}@40y0+E$_oId5v+7e58{# z{yB6>HG7dU@%6+eq!CHBW*xHIB*99xsDu!8?SvNH5TKbKr^o755~A3o^5vsPSa^|p zof~;bFJQq=;MYF>FdaB7vW;kgVg~);?dz2Yt!cQCXr1{X3QWLC19}9@*3K`a4 z9j}jy_Dh^SSmk)uvOJ@lCVha@j%UdSn7m1~`Ge}Wr(;)8a*IdP%&Cxke-w%2Pg^*D zm;x}L{i(X%p3OGNxb2n{@Qp~_7qk1QY|wuV7Sva&#Ujh#tt$Wcu^;ZU1`%>=&b4!Q zVkrA=&=+J``+Brt*iH$#a;?u&Wv)b2bkVjVl8ZHa!d}^rk)^GbXYJl*oe({^<~(*j z23>S#;WZ8ArR0}^i9KU{h5kPeuq3r5Hf$OLaW>qMa**$-u%|24%{?Don-^?ZU|N7H66h#W=gio_}&W8R=LAXL*f!xPpXpiiku8XZ= zQTFMIZyeK_W!{JJ4G+uZ@7XbhB?eUGcElZR^NVJEOv#mv*Ag`!!P0m7j%t-*LpfExXhkg%A7^v?qZcfn>M#?H7c> z3->F-M$QuqLj6$QiIt^rz@O;V_$U-wL)?67} za6%R(J<8&ZiINig{H8DVQ-Ff+(DVD-*jRXMTl7VH8g!xf`<`u!B~ib=017o6E(xoU z>idX=bxCX=uW1+Wlw@*9NU_gu@MTamXb{~RNu*A@V2IIJe*~_&HyPAI47&2}kvhQO z*MpXGQ7VM?*aQj_uk4;%a(ydD?+Z$80=24ZGx;5IAFfa3Mwo+Vn_kCMGv!m;7LL7d zMMh(>OFrgR$a?eA=1IG*{!I%Y;`gBiaj+XZhF6+Mg(jci`fjTBB4VZudYj9a>NLm( zZJJ!|B=QVsoM*o=dvq4SqhhK@`Me!~66YwSpnnJricdqg9Bc%W%zqaP_cDgx)3ZLnMIF^;5J=r6y2 zjftiey9Kmf#PEzzz4?x1oUNs^ynt1{K}d$xc%7%pEFk@)P6${JkmgJfvdhEU{`bui->*a72rVg@T~Sx{#rJT6ML-b!jM@GJovQ(4wJ2p~rJY_Fc&@ z|Kw!g%shmAZTstegTZgvlT{e|X7en-!`Em7pwSMt$xjma32)W`6u+E3pEi#|EInl@XdxTy?bV@1w0Wt&*&14d< zhT~@+sa`7YrTmZJI%~u_y(bEGdlt22NSpvc!?32_bwneCR%v|;m0)5Ta+fd6!T?u6 zl0?QT2zL3<+28j?6aqS#1!II?>j1AjR2!ZTho@8dEp%TS;|L}1j`IE0%(n_q7r1?2 z9t(Ez2$^AbG?AJA$oBwyp*ap`4q)rDbIqXVQr$aJ=+d(9((O|k8goANlDtACp(H0? zpo`3JZTEHbX24qjlTy+2O@J>@&O@WNCCTMATvV^gI20#4DWTj#lJ|AzWJXh%9ZMD_ z-h5#aa)~i9F%$$AL4r9MiR(M_+XFLXT{H&=C)Q3{xd~;33#5?gmQ;8KoABRkMgw>0 z4at3Sk0PjqdspU z{UeiH?yJv$*&?IT&K%LCcYlJvEke-ba!@TA-5nHrWEyd_Y+P;^Vp zwtil&Ycp1VvJl7x`^)H%U=SI?AmdZ+Ca^^8Am9E(&;~mp@CC94mXl#*Ux4fJUgYl(0_TI1lE`#XcT-*4c*I%MU0j|Z0i zXB$!8pyk$PFjR#2Nv8rYLhSulS5*fB)e*n6s3>(>@!A{g*dkFiw( zuM)fU%ismUPYA)HU7}Bz0Ylv@sac%knquV<~B^WWq8$%#yr2Fb> zDz6xggT-(+9n4Dr5Eg~=J^v<*0ReJr`K>0kFPib*ezNQif~J)z7qB}|!0NZtnpgz+ zujpq6>>r&EBz1*`Q+YDGYu;zBA0eeiK({CB>U_{@oMHhGjGEEZe|SVq;Nmm`3E%-z z$CBSM1feK8l{$-6@IoqDFdZ81OEx@*v6vO4FY;veWUW2N{V%@0G|6bH#a|~{g}6{2 zegwj?2EuJEAy!d!5Csq(sk#+MZN zrV5Xq={&|~cDL%@fRPr5)*>MOyo%Moq@HS5zaasP2LwLN%QpHHeCUMQwNA})MF^IV zAl8Zb8 z%?e=%#Tsay;rZwJrotb&D5|V|@gxyhBo#w62@w}ma`J0H62XrU)Sdxh>>e`C5aVjb z1vTT`MvsU*h}O6l1f7|xq0je+L|tA02QJx^0SjhU;>Gg+0^C3&zYI|KQ~@*q?3NCk z>Ct1f^?(-@-7O+Pi>V6s8}&JRi!i!ZJ(;9A4A9cjZx!J+PSPKjhsO&c>>ac&`I>94 zF{ht?x^2C)=^^~q`j%MFo;~G|>K!8SYx|h^5707z&Ye4J`m32F$LE3S%F1Soxel|c zbRaXy6ZGxcwGT*GykMs|GcuS|s$X!L0NMcl)mgK=eHd}Ezyz2|y8l0}KefJh7gQFF6vOrJgn$~!$mL~@H>=4CoQVfD1Rsgga=$Ma+GdF|D!FAP!nvF|Us=pyrnKm0+AuZLC7oo(M- zixGNh4>O1#&wnBT>5uuqJ^E^~1L==hhG?N&0a$P7o<~YckoT(z0K~X{{c{>8?G!(5M21}5BEvC5AyQwh; zE74y#Q>fJ{jpIyHS5<-5$_cbGJNp=^x{qlP{va;;nH*TBSR%Od&O7x`?aVE=++v=1 z;t5+xr%uzMH<WkC$&X~y zyK;#-;wYRY@pZ%zM}+tUa&r&SwA0J=W=kik{nUdLUb1A6i2omATnE-0fz`In5PzCR zKHo~gcff8`&Vg88%z*n97XD-&d1R>KN{U_6v7^PN%sImUxXgw;>Q%*t($X=uKz8;Z zaj$p8In&MAXP<5DUkHMEZwEz16J0V%j}ZUSA;gbbf9|>Gn*aUpf2Kf0?i_qP0Q3BK z|55iFg7~>-j+Qre(K5%!AAg*`0boPV4I4hU12>SFq>0kfMG^#q)PKoG{B#-a90;Dk zwy(c7=AL_uY;|urIJ3E!3rKH>HcrR4>}s<{Im3Z@CwifT5}RDnc8 zq(=g$fhA?*7#d83%{a2?>vm^mDKa+G&CR^+b z*`{{|T8659!woms_9J;6@LV0VMGY!H0Y8lai?mU}!w)}fF1h5A!0lO2-?{TF{jcW2xz6402*%7003ADX9M(JE z=>J&Zys-Rw`y0sz;GBT=rfF&8q{cr_q6J%BN&K0Uf%6;GMKgebpk4IteV}HS z3T)tz-mGg_7bW%=J|_&=3GQTl9IG_}h-{=|Hf=)k6l<=vpO}GkBp=`~bAabJnE(xC z&6m!dm(mDsd`x7$!grw2( z#%RtKb595yVrOVDqrm2`eCOeo&?@ zcEGO12|t{bPkt_e_Qn>M{P<_?f~Jyp}}i{0fiJj?_knt<+De-KBL(`Em& zxu!O16|EOp3B^B-a%Cf*Os0oRr@`s=A{x~y!qUeZA!)f}cx=yB}%wHSyWlK=*g zEX3~`jSu45Yp=EKb9t**pX-vTV*~d9;~GLI(1$5SRx7^#xEmDkvrO!-)iY)djOq+S>t$RO3N;`3gz? z!^OC~8BONeQKv7y_~Hb6%xkKc@Jn$Guw;Cm&81C_ z5nv|pZHxEc*Eh01WG_t5Ou$lxQB=nWFg5b?+n6z9o|BVzr@(twVn^Z2LVN>&*iT|1 zP6n`6cU>+H)mdghz+n++34t-h1USH9SlvUrvh{P^qyUi1#E2#Zs)s=xfBf;X?Lw^{ zR__fSJUHROJ%Jl1C^2EegvzjEum9Jsy`z!)I6)7#8C1hqXIMcRE(&qV6g>ULuA(Iv ziOD1wMl^s4G;7vLLh4;};DIoE-m|6mZI!_OyP`5vhr~gQCi!oI8M4^yyz?$0PBT{m z4S)za2Sf#ymMZkBk)XVMm7IS63)~&`{_eZ)wkp2g=|_4SEi@xI~j-x{vw(rUL#IiL{wz%PC0i9&p+L+t2g$tEWU+d@`Py1iquec{Br#4qy)3 zZ-0u}V~-nL7Dss&Y{1Y7{>-|jBQXG)(lS!TnVFvY%z~J0x7{`{PYz=sh8;-@@a5OW z*!Y7^EH3_+m`_mqIPbjkY_y`^aY-9lx9?|Kx07m{N?JBj>G_P@jn`@ z6!jQYBtwx)Gp+O|Km}s&-@m{03AiaVw@8M~GB<2sGmBs?4UsA@FP7^6@4&5?N8e9h zo50TZ1MO|hC_jH&YLaG_V$VJI3|vA~W=V;R%@j1L9zfzl>f(^QekFT27t{Ea6}tGwJs zgnrmBzaar;vuf4<$-_U#-j-#yEazpT4nLr+K>RGbO*Ft*rc2L*=`d3Qjv6(}MoA{> zUMyAS*#LdRPACsk3Yu&WFm93lI1kYVUVhn&K7{xIj@xc~g2vQ6{IhxbX(1$RxSPwU z?J*hyfQl6A08k`2?@w?F(`F6|MEEXz)^AS}7VI9vm95X?#*IHn8frqe5^(Mj>F2lm zA$}M_T{rP?_wXNCzI)P1Ct1_zZN8jR2_I;pm?=i~0Zat+?%ms(FVw|U=lb<8G`Oaf zi04SvQMCs!5tuPx_^<~j0Y_*FFp2H9J6ZL2!TVfKY+j1S23wsK^pc$L+ z{h*Q}UHxixMFo=yTwDLZS7(Atkt?)`=>)r5uRq$4nG3xB{x&A6Wk(%#R3Ng@1xc|@ zxSv_Fpy!lRPO0-PF}wU4*v5??iScQ)q+qk^0S#9RL>CfMfRBK^cZLl+Pr~E@oeTH` z&eoqZDl>Gb8N^}$U#co*Ep_-n$YLP&X#d3?{Xb{`c-c=l;e~O#2Czm#}mw$aM z?`3Bsw=|l2agR}>Mv=5LC1;*_X22BrQeRiO?>=5!-N4PZPzym%p0-uDI#X9Ce-AKo zz{+rK6CtPJVe$p->Tk5X1}4v+5l=c610ejc3ia|ai*SIz7?cnm9`GPc)4cq*_2#64 zI~Et;7V!L|azlJDBL^@gw)CoxIDfwM!%96r@ce#8bLgRm+TDd+8MFYl0fr^OHq92}T~L?mQr9-29!M9(;)9dS z*Sck?{(Kk!b}4=UzMK<;Wg^h;?c8QCFL9}|vQ?`8pMeTl?6bAL65$vCN8%OOVyixY zzL?9mt9-y}L%$Zpse=wWC{W_jM<1=T89;x-*c=l`{*71zkDp2;s0UyK85x~KZ|}%y z7>Im5PSqbR^rS-^_o|KoUpd zIdWw1)=I&USfk(wCFEos{}5b+_dgtniwK9yFTXq_E8Ij0IT`1l>lEJX7+pyTz7`pZ zDcItAfLS2EyyRm752zEG#oG~8sX6pYg(ZXn(kC2X3N8%TKkx39`$RVS(u6YrwEW`Y zJ4FPFpb+5U2OUvxMC~VPu|5Db9-F>40P)rp{M(qp2z?Hrx>lXKzKQh!w9ui$7h(cK zUCXB{k?;P*I#$O3oO?T9RQo?#;jb750MQYA6AI5Mn4ws{`2Db}Ry!AOvg#8oi3ZqAl4ph+?>n;K}PbHF~2h!7X zHU6)bpv|H^J%o?M)3E9o0CmXc7_=#-Few=UoCdJtjyr~(T_bz-O-;-FoIovrk7ax; zh7)wAjUPWg#2Zh0`~fpzjjHqCs+vyhD*2F#(@tI{-m{Ld>#ji$aB1l@)fV8yGt#LLosyp)2AzQwi}ffGRqFw2x&thZ}nXgu;4BsITM; zs4}S#S}e|g>()IWZDN%zO>95QlATJ92EvR$Bxhxu)z_(5wEOylg}A{NUwl!g`=l;T z^+2>9$js~|=Ql0@OWwnjxTLadmFdBF7y!m)fC)fo;h(LltUTawbHwQK`JMnPj90Cy z`=DnQa@DFc165yr_0={EnGDPmOp*GU_Yd4uiD=G6zUvps4B~TYe&Fj$DM&~rfU&OnbCh5;S`|l8JPZK$J*$qs6IvqsjipKv0I$1qbX7f#D}fCx+{8lF64m7} z_g_*%fT9OP{$X3YdvatRK9Y&zu1VC9WxkPsWx?UQ4^b|BIdC*hAyS>Gyi^a=v#e*-ASlQL|i-!fC`}6A4yaP?83w6wyFZ*s%s^^KdIGh0K~s><613~ z3_3OsKm72(+Ia2<1E`PXzEz)vEui0zl&yW<3M1y%x5O_mUmEegsmv|62ePxbQ!LD1 z)B%>v+bI!Loh!Q~UIwsHu)Lgz zLN?WyB=nXg4;nNmpo&BM^%3uHZx-Pvnh8K`z~sr3t6WjvZ7VLmxmJv|N<&i_n@ta3 z3gqXXs@bqTcqStFP7;QA=;LJoRPde#F}VtVsZ969}+sus$IxYv=l`K2>*w z9zZLtD@Ld`p;-h5wQb7>hJduL`uVdki+=#NLA-zlU?=h+@K`o%bSE5YGn;1=YJKyU zve$POfmDBc_P$%Q=6e-fBlIp{CV~}+Ze<)<49aVrqA3X|>>95`NaK)19Q}h7CMYtUTb(ECgL@QV4ue5t93+SteP|H^mwC%6r>dVPd*YQg|i-Yzh>j0Ax!I07Rr zfA!nGlnn*^05}9&>%}aP*L;;4-A4auS6XGXAiE0@=g$kIiJV(lc{JvbLk_VEf84fT z5WfG@WGcAX0}RgrFE&Ay{o6`u!peR61qq1-kH!}_t2%vo*g)K z;DHBvFnO}eXHLBw+x(UG5F9=}(@dBE_EL#|9&yr&&{b51{ z1qJa|RBz=R19D#fdb?3&#s0Mqo(53gcdYOFvCCof5Hm+_wg@S{Y11k({CUb_;5(t9 zWJsF-De!ur+%m+nvNd)`uHxb;(g?a~N^l!70GxsMsjn9`Q`xg>!T=m=U8xJvIjxh6 zVBJChHy#LAI{KFK28LeK8x>>(K zw4tBTOe(ztG+MOilxN+l3_1Pw=?#rwu=&cngvko|Kska-R5Pn10<8^tm zWVn`o@@of-3FwEuE)sq$&7if+fJ@cyHYR9i*Tc3@oIlKMolaRi(W6F&u11<-d}(R12H_Gh@WpzE>#bx*!h1G4rN-k3-ceC#)x zU3r>*bA-M~X*6Z>5Ukkx2w>N*(yt^mOG^XOj-6`506bBwYTwk?3jXnre<)QI*sy|c z@Ga%b1`%PIa*>u+nTS}z%cO}g3oUGx>hIPYG`92)U+_eGhz?{r(my?E8y?|@9gJ!V zOe;Djlb+sIT-}=%s%?Gsah)T4>~thN%fY>Yh>SNeJ2Db4tS` z7YL^a0s0k30A*0v`R^2WC9A^h+Ax3%R1X2hhHbH9f%}MPb6aMzax^eghrm@O~pYt=AjEt>y-wxqL z8uY(M&}AZQK2}<;h|SqDg+5gq7>rIjxk22ZO=|)-bJCe3{pF@6IYPkD2y{mOF@pvp zjT?}#?gnrIgf|Wr2-pvEqis+it?er18o%sd zAJ89Td!`V&{9FiO;|QnUpi>wCv>O+|F(4c|{z;(=W>};&=inTu4)H)mZ5RMggIFdk zxCP(Ai^c$0tP>m7>-4w6zCu4^;wql&l|Vi#|3HWzrs5BTp>#t7wmu9Ff^>jt&VnR% z{SEgKxH5G)vYy~!eHSWNH^kPXE>*^LErefMx=MEZd^tJ4u4FiFj3Mb$5M+W#cnJDy zAvF5B5Op583F)AL#|3Yy6ybkPWq48PZz`{(5?(+!(Nvy}{}9dq`0rRa>Li3!gqyGEAXXowQSv1FTM1VAwVb6yg!n=dYGpo z{}b*nM0KL-O#%PG>8GD=*l7sc8r3@)cKpRBF>2H(8wu%l0fIrHp2HmQ1-PZkFsvm@ zmgt#0;JVttt+(FlyX;V{ zLHK<5(|s+884CIWCPknR9}a~n78DeO>S2#g+g(r_*Y3m4?uf@>7NLgxK;xT&6a}{gUtt z;TuBUgtHGq2enQl3owFia*bgC-*($=cC{T_YXb!OAmO$8FHKG^dI>zG5_OROQcf?{ z{zqZ}@lVsyU!ncPPP&fxVI+O}^ht`aU;EJdVfvFj2pt1}L4hy;OccIU3^+}|{?QNN z%Qb~_&N;_-$stPf>Hv>emo0nFelNaRvq6&PNU^Hm&DRr%!@y3HpW<^7{{l&SczS4k zRj=M$avXC*mSJ*#Cq(|Cb;blP!05+qk9u^ou!Zm=;b+1o39vu77SxxLo*A{5g+X}f@r;K9s_{o3(NFOM+iIP5+G!fBG|eL#%!$Uf z{llMt=O)$x21Q$8qK?4%4+Dfj`r`(kd+xa!?O%vLfelcR*q+2XLPy4)AJS#7FR;=a z0{4i#Tt0$jVoEFQJpHJ_i7+Byr$f}( z?30B{g*XB>TL@tQd}50oZ5gaVd$a1ZFJ8RZ@UhEDlO|O;i35xUI*&g3s11~H1+KjE zN^|YC*SfzUN3;Xt{E?<;7&y>q15%ZM8*J#t4EhOaY=l3TUw*my>tFvGI79n;_wH?p zAExJD_=6)a{39WO3nL_8wEOM1pY0njZ2Cf#f_b2grXX?;K=j!1-wU4>qU9j{qxRW{ zI*C+&M~EG|UWlEv*+YNv)`y z!JLB79)Mmk(>bCoVcSE;Api$J9RiW?w+P=8Zm|FZKrh3yOy6dis+Qd0?=2+7pY60H~rVufUa3FetIG5_!Lx_aiQURjR6k@6qfHhZ$Q!k=T zPe*+&yjw`YuY_=n6!>}o2GCbHi#!+rn!xPYv#tG&q{3N+8h^zVd}oZ#m;4Fy5im<| z|NZydbJ2cKkzoM;`q#e}Woi=s5F!7BU+bu|FvAi0~mHOn@o76h!F( z7(h!Qoz|D273SKRvI=)zaXkYYLdCX6g=bJ>pZ@co|KxPSV6VOQGDC+BH4`RGu(JV? zA`BCDvwEuN`s=T^%nU86rn?p$fBf<0kw+NA0I>w83!Cw03j&7bCL!hz!xOC?4}KMZ z$U#(sBZXX$f;fACUQQRDA;b%6KekB_5FYqNix%0rfAXD^kaRf2Iu~Bn|G#B3ea)w( zH?T9()6BeISC}&0*AIBWFTC(VD?OPCY>Ldl|2);D`RFJ<81LJQNWMP^cNIbmTLPeE z;>`O+_^xoKkU*V?Fip9S3z0xoJbn?cNM(lf01NMU^4)B6-zy)P)y1LjlX!Yeg!9io-`demRZeu98nZs8 zU?KkCXT%1pIp+!yaGN{8?DB*k2|pI%#Saw*%J2jBe1G9$e`YFOvmS5^fU8Kua`)m7 z(|8aa(jAk4xcB;4;da!%!_5=7X^jHU!oX zetXO@$Jmf%+POJ^V`lWV2OMFzcL`C@63={9X_5fXihVy-_>hqK`?#k$SxI-O&~=!S zDX2*gxDn^SX0rb5P6lDE*!%>xFgOY6oi}e@!nJ`rF8G}}J5d;FH-;2R{fX2Au+>N~Lz#~YnQ!SJkADv$`G`ydAv~OMPBXw)0M-@`8M(a~ zHmHxrdxSdhJ!~vB+0q6MIdFs--lwb8MrO|Y)oc<2BC?KYHr5iroSGW&32~g9PP6f( z{xNzCB;e)(5HQ4xdXJU)s_=Q?A|ZZ`-Gm1S#|a_)c0$Gi!i6J@kTm8X<)QPr!T@Zm z)New^O#&xUiP$~Bo2d?X8-0am3x8Kx7%d^qMYKWu!2w)Zq6UXs&pq0lef)u;CP1|7 zr4ejcUt*nw$6j@}Sz1_R7v8WgA7W}sOyHMaele`S;OGIq*z<**EB|i}2N)Sr{0$+_ zy%j=+9U9I~!Zt#f1F|TB0I&)vpUWOBfzw~ipRLz3G&^;<@K#+D0&VpVE|Y>ddmtVL z;IC>s{lPrAN{C^`+pUwR+I`4YX7YnqYSH`#RaFBJBlvCkYO~|-uQ7{tgaHwjOrTLR zd@8KECqbiPSK;6a$!uM6t+2f?5+PJqV=|uSIf{h;70wYdRE`xgjGUBLG6tll=gAK> zRzYLlmu_8ADWRGz`Mvg9c*tdOoGC5`4rH=ecG~L=Z zH*3~M_6xLbmSb8s%QelU&Ub2+XZ9VswR!EPGizt+ir|~&=bH2WJi#no_?y8b`1G^S z?dBV!MvV$Y8LC}L2M|4*rx0fm1Y9m1g-zY=cQOS@4{?In_9qF?5cU%02vH(q0Xs>x zl#};}D#XuKF#KlCj*uT}iGtF;b``-rmk9~`O+g$z;NDgoMPnBEo6cTLi7UtLVm5B7 zI*7~4E6n!2I+&LE*?M^z5@Gph{^t{Bk(`CBpd&Qd zg)Y~!>Uz11ZNv%loi)pOy$O6dT6m}sRT-%XR_#M&HUZ!no)W?U`U?99G0}NWl*V`f z;%~0tufyyy`W4=?_0_8{)dnXws#uvF4oVy;d^-AdsWYwz62$;UtHQUaw0(;_Gyjf1 zn!@6$Dm`4=(Rx|gCevlWAd}m&m1|4gj?xLgnl{Jm{-+zw200B0s7W@T0DJnMb=Fy7 z@e~jTPB0u}sK%XzU4`UBOz{$piY5g-p;lxG_Z4E2Lz6JH_yHTgZQH$M$`!KJx#mjd1e!{1EP3`5I@7g zqBMf$9Xgt<92`Ii09D?#OFJ`e&mGJi&%SSF%$Q+!;$_KkGH3$lop+wy9-UZ?*w zRlwSd5-?Wq6zIA_VSppVQ_q%IrlvlCXnjbexdn#=6t*2kUa<*(ww&VY_zwh@hL*=y|fUduuA1&yT}AE%egwYr<9D-_&oU9!L}epzi6yV}-3?0K@up zHr?B{u+pEC;Q#|@*|C$!RB%xup+XwSq5BLq8|8@nXzI`Av(G*=^XJdEPPc~IR*bz7 z*yV^7eOF(7wa?y{iqx@<8Q8n4*?0IrGhv@$X0IK$HhT{pU~x$Qo@V)~;*juXZ?60Y zby1#hYhh_3yDRDH%F1f1F!Uh-Q!(BKqFrWZ@1khcH{=+EY1*&MAe3H=w~j5bT%vpc zuJ0~a;#&Tku*X1i#kieKrfl;_P$Bc6Rp+jzZ4WW(Kn#4OlC{g*q@bb?zWLFNx!?}7 zc0-Asa$`>eHrT8$xbemt%{%YBV>e7?L63`)9J9v|bMUU)nc)Kq3@ad;k|^ZI4!3eAXaG}k})ntA(+={C;6D}!A%6SZ?zddh{~+y2(Y?kDz;#7g z*?jZcZ#zgE2!sRRg`Dfor;edLkZ1-#?URIiw$b2z`rJcI-%e4Sd}7&|+J&gwAZ)$J zOoGbl1lrK#yC3J9GqsigZ2&mpICyrd@geD>LA?Urb7z4cbeWtqD7pN`nmTz$%6 zfhEr&1>*SgtKu6DzvvG0#dJ4*VFJesakn(pS%O#(zDYYO|4GCft4ww0$~7Z!gB-1w zx`G>(NUWOaYTgWv^}zseU*pNj+^Jt@^T6r*o4jnMfT}{dLU}v%*~+x&D9?9Ao!)xY z%3Tf`i0*&**|(UtznpHRE2q!~{`ki~+NICzWE>6H9u3DGJNvR5G9l1>KjH1S-?nN# zpDT0FLU9T0TQ@fs{BD19(1^jNSHJFFfD`&@Z9u_J<)xd;kHJeP!mcuO8w!7TJm6graR%)a5&SsOvz`9U)Gk;PL+5`2$0Jy_R!h3~jhY#;- z9y;Ryvs}}Hk#3f*ZN_?Rx1&jmVk%HsLO`(sFTVYydH#cM%nSt$Ayr#xm)`xg;RyR5 zMgnFJ*u@xio;46tr?O-<6tc52&9+*fFjlh<1ABKfhwL`kWJ|r*=#!9608>-_bLTHH zBTo67&m3TS?Iz*oqy_M4^+PoE*E6((Yw-_@vbgxa+S`&1XRXhKwJkdfnQBRauLmO0 zr0;aClg)J{^H}>)!^H6;%vlEvi5MY>Gq82n?xszz-X76#L484HhAEYOjic?08FS5< z|9Dh^L$z}fpLp=DreF7t<_HmfD-nFRZXG;=M}MuW2n&DyoNV*p^OMYp*WT|tbF$Ky z0%A0q1#B=~yZ%Y$U}SBvHjlVuNjEtJ7rRUUs02bO1->4rF9yIZMhKr1c5c@^*Zl9) z(PpQ8WUzPYY-Lmyjmzkdz{N}6tF{N1{w>ba|;50D+{2X-$ zUeDUKcWLSRX|A%#N{kh*a2ctiw=AnZ`{5~7K7kty>(j~HbJ}RrDnEihMx#5YMGMoZ z-vEP2(4c_nkySYxD#xAnCJRb%MiFW}>O5f62~xYMJAhvG_7`_S6fNT7|2$`Ic=&Z+ z;p3IwM_8AsL1X~Wgs&`gRzevK1m#-A*-oLqeE+cWUDWx`e3vmz>n&QVJ6F5f_EzG3 z_a}AqJWBQ1510XG2%9ZlDM_WVw(p=`rc?u*VHW@qLYX!e$;{5t)EL%70|KHI!UpAV zvzwAAl$bpTY??fGK1N_u+%1Oa^K!J$;z~1R#(eYI$5YMwU(PU}Or2#u{`M#H!|a8o zm8J^YwW{iv+)hr#S3a6*male>$E|c*oPo7vO1uT?dPCSk*hbhGP_F2|uA2VqEd~%W zGV0J6zH~)wz)t=ZgFRH?Lv^Thv%9pu#jU8y?lB10?QrMY-ziSN%-nY3C`Aj(DH!&y z3m42YIr4#ofwDTr2!^sXsL^RbbIx9Yf!uxI6e9U28ht@QvoIisWrw3HT5MD1t*aLu3j zlY%;~<&U!ir>Wf4uIvLJOf!GF_g%Xx(B-)!8;h*?WuEksM5%$U<|H|4CgS|to-&u+ z`Ml}czKuEmn9=61e>$RywEz1uF2wztaJQF)b|IQa)Us-H_9mBYB z<(Y;%)ZhJ^HxI-F@Hqc&xMxcKJ0S)d0`AaF0!-ksk7k;QPk&lvjHCW-SW{${%=kg9 zsiB(I5RdQOqe3$fcTN1GsrcbhGwFfL%)~Rt$+{>vMQiN>7R@FQ?i(HQbbgzHF3}6b zb%uUL0A3?xz09#fB4!&CGR*q*$cfsTGO^m8F4JPXWC|LN1-M-bDtn+IscfR|=|oP& z?Qec-Zhh?=lOv~_U-`4U4~q#DFIyT>3O408XJwq2Kn0tcYR`nL<$0oA#O}#9`wATX)60IcDvOWt$xnK;&LHvehD zKEm3X5xO*?kglD50`dC^tr=@7H+MgEz4a|I_VyKKCG;juw6ca~0CdwWLPU>c_xDel zW=^~3T}=-%NP_?q_-)otX8kG+3f|!603zN(dGPz}dAhm%>Gw>&0(`DI`A~D?S>sKv zzLLZ<1Lq#C@#*J?mCwFxX`y-LGdHt~8iN5a%JB(Y<@)mRk7t@Ip87;DU((S>>x2jY zDu2Mbm9ka+C)|a}ex%Kc=VxW&0uk@!cRXu49C)souWulB?aOvLR(f!$ykC~15@Ym?xIYL~t75{l_Y8s1%em8P}>7c1D20K7t(9)l0 z+C@Xn+lw$FNJd+*F@A7Vioinv6&=FYLZ_)e%+&`^mz$5jn`Q2LakBm1VN0!&Jpb7J z%^71ynf6`Vn+kmrjpC4OOD!fkV3$D}sFKbR_EmgW@dA7@%>$+bQRV$ooLcBonND_Y zf3l7^7zYZE7xoY`9Y(oi1AP{*bh+m2f0ne~D&eo0VUwM`uYAjV^OF9tYS905oSSSt z(U9#)hLP|WJ@7w-75YGE8q0^D{J(>>YjD2VP#PTEXb0^J`kH3iMaV|VHAF8#+#*W8 zSy%lI+WDuD$LsfKqAo5U4k=Fp_p(wUcO|i5 zX|j3r-n-{F^WFQ}d+#~FbIJ6U5GbYcKlZOz%;s&otapFImhFu1 zm#3Z1=XtZ}_FG~W5l(#K(KVdmi_CK9$+`E}=hj&bpn^c5Y|_UYVGe;Qr(T&E&6x7u zFX3+lELoCC%O7p@^-FBG0j2v3K#iSDfuWcT{OtfzY3Wo31)ax?KnIO_DkNxITCOBS z_!r)@<#koCTG3LTFMHg1p)_0tmV0lms+?q&UvZ8(?Wj5X&M-_(IB3pXGkxCSe9P|$ z2k*X7-BRgaeznnD`kgz?_NG?*XbB;W8Au%{2?-NdlwcP1C~`1Olj!&VIAaHHz2=i< zI$DkF{D_2!>S-QQ9E4U~>PJ-mTYk05E{&GDrii^C0;%<4-z4x5xDV*zN|Rc9b@44o zAF~MrX}a%Y&GhM~jk*?~>?e~khIMCxsp>d&LLk^OMv2td;}^P)gxmzwR8i3)=C;qC zX_lN&W6Fuu*TkYC%_LMlwUU{HYHZY?JvmlC48`3_YyL2fH@#h}5Z(XOTGK`}Uq2#! z2cx>HJ5Qqnmte~;&~j&5@Kj;N162Bc&&fSO8rEP!EWF}oTh&vK`5yn83uOOq0McYV zZ@$=fI=CE25bP_UIN^Ir%?i92&%K1$Xj6hP>_yDhwAOpDR1MihMM-`V04jj9z&&6# z(6=7>^r=UgOHZyfXB{`sw8!FB5Xf6zLbPDz>_eGX@DLN#yn<<;0r!k9sYUh%3u)#e z?Ms);M-o!f0F?64m=6GxVi(%SCD+}VH6h4(Jp3G>z?n_ppFouyQ^nf)=d+b>fLMdT zQo@=5RaF=4XI+58G8l_Jz;gGgm5zV+dJ?l5NRM6w%=$NXnMVxS%bX$C>22=crG#4!hIM$F1wPH4HY zSnSU{w@sI7zzUE@t>bLS$!`Kc?0yS82GqoUJLc2+Hwwpz${cARD%}LT;j_rrbR_gwjz$HKx z9}Ok&nI{XDn7k$4j4bV7QPBCLDg+{t33fTL-d=rT)`23gsXef-E}|bl3IMTR&6)~i zH8=xQf)Y(P{=kc`o2UP`)zls`jm1aGtp{ETd0#wkTDNaA9nDR4s&Nf1meVd!M0$H- zo#vrOpEVa_5U^V)L#La zo<&7L`eNh~mIv&v>=G&`c&M5ciO+ubJah4)TDt(L6o?pe?5oFM!X%cBV)?NNlO~&r zs##3fsSsF5w!JW(B#lInW07}vGT@1jC$s7w>&-KDTg=KAH=0d5JoSFCgsC-@`Y)m8 z^#+`tK3>3fD$utq1g{r>MCTBzNU>B{Wu5cDp!l9HA0 z1?u9i0qPjsjU5tE9grR56*1^v={)-rZ(@(zk+t1l*~q~`Bh@HHTz zpnk9nY0}Qc#~)^{{lr2ulfa^KLW(ESg{LmW6qtspqSD3^XjWpG5jIH2ZVOX2rE%B+ zJ!yfGU!Dl)}_xM!S>@o zvP@0>Wk9M7{hxN-ZRLG{vYg4Kj{x}$-78riw}stAnb{7g0K8Dh{WpH5H^S$ZW6`dY`9x2T9XQi>@w6O+Kk=B?n}0yvOu) zstYS&wl%k#7(E!T;>=S|#$VQKHtQO8n%0g~(2kg&RRz_lzY*L49tT6AqxqABgKGF} z@IEjD$T5(IPUM_iDenS@0^KSzfo{v}lyKxvN-}xq3?>i#1jXeEu|iXtq^u*Ru`yEx zB#p(#hC-hh#QQS24`SkT(yY9k3qAm@0;&-GNWwsajwXYn=FG6Zf=fS8YiBF|Fu_I& z?TyjDpAi84o{&C4DRt`WtoL>zd z29JSykdhAU6hOyuj~oPN0`WnRK*&Rqo01uQeJl7Fm;>ZT$>|E#DftU715kxT?y|A`3s~LOWG^0 zb^%ZmC{o3h<}h4p;0E2>3f=;B;CZkb=p7!3-+rk{OGwL^;B;^bP%Y0PnF;DiI2BX@ zxfbLz@I(FRrAMF|P2haAHOaNYY5VpW_-w41*2Yt03#lxNf{IZ9^>wTH zQ?J5MNfqaxK=-PZ-vNR|O8*5w{(>ukDnJgYl|2l|G*}Fd1d?k9T9)+^AhZCd;MsQg z1qmvMT3eUW9r$bSUeyUd32HB0W3qJuU`*H=$cIW090D!|-v)C*9+GQ8euB>fnG2G8 z2a+N<1Wz#)701$JQ&>9M+kc8L;G5pf-(u$$?;z~kk?Brh=T33uPc?ZfxE)l3r9hs* z9B-gn{(pfl0-4OJbp_Tu?i@KKX}ve10mKS^)WnJBWZx_EuxKd90aP^kb{QR<1JVrF zf$xK7fgAw(R&pxGYn)mI%7M&;oXeaU4@xBV64JXDbD*Xme?T(X%v^*kNUHU1%x33~ z@kqBH-r)p*A4$4~G-PW=~b2WpAE4uafs{|BHNe<2X*8kI#AzaFRq?;FOi$QmSh9 z3|Iy>0ol$998lLln$)-c9MCECtV$~oNh=r&V#H-4fCNOEWHGD*yMTm%dK(oaIX2YOv%@+BOG+jV z`2`C7?&?}aSK?mXBPpyRmujt%G-@*QPma~WTt-cCyjijvI| zdU66F^hJEYGN_vU1EB5CfWoA+Nan!Lfn5ngIU=p!np4QKp0Eazj;zkd=b=wAA+o5w^9z& zZFoCa20Fn=N!@}mCj3~pUsFIPKt;t;UYMy86&z$~DkDvcKkv`v)}P69sR}6v)VzNO zybJsu&H$K}z_Z9-uDrF)i0tyC#*bRb6iNG$QC@ft9(zP@y3JTaLqKFDe zsaU8N0Y$~a040R;&RW;HUj1F}egCoNey*IId1B_7Idh(Q=Gp!Bl3d%b9;=c!3^X(- zH=5Hn+}QitxeyuITVT@Hc17!5(}JEbuY21-uG%d_yW*t92E@hpVzG~p%}cmZw7j_t zYv?)n?!~k^JE?R-LK*7yWh{R4>#e?YVM*s+(Z2O&+EX6M7Ejimb~fEPMdP%4yPqg) z%yH3*Bbz(dcDdm!HwPHpFfwRWnQ1Wp>a8QuLxepJi)YTI>ZI#Cj?Y!xg9<+CfJxEn z9()Bx&=atx?8$XAa@x=W6R&{$pnxWU*v^-dy6&}x-g}j0({@ap-0#|>q2*PO%a3em z6Wdm)X2KcNo2R}7^^m>#r3^j=GX>F?HHSZ3kAJ-6DY{xtfa^3`f> z@pz?6JB&-c48yGT);??$&+lE@RMgX(sg2_$)`|a#$gr|XG05!gJti|)wb*G&Mqz%ie9j&Z8 zZdB2&k-3{Dwg%<*cryH^cM!Vwm~(tww>G;PJtN$laqQrVjyKYTcU1wVW*7LZ4GK{^ zCt&Ibtvz9=^Ma378${-G9%~JmHFw^S&|r&YXN+JSWuC?BRoS-$?v{vwj`1z)>lW%$ zlSVYm4R@n#PENdb12aHafVPsvi=ww<37+Y73C;t}l~->Z!=T*uczY=8=1Pm-pASk> z|Ck!i*eTXNWN1E;&|}vw1Ggr%Ggahv#^&ulc@p*Xk@v!mGb@-F6$P!saLsgxI_fY{ ziN3~sWW8+c_7k(8B-#Z{z>xyB1?FHkw$q1CVCCcHJ`mT7@jYv?00f`j5v3TmuVWfW z!xUMiU4L|TU0{;o&?`Q>MOP0WyU{8V)Vgtzy>a1o|Fow%sGT+;w@KOrJzK>5mAlAs z{(>Nk0#G2ffpXUpD|^AM2Ff~?v5zqw%F5vEx(nx1ja#45tXcTw3G3N;ksCzKh);`1 z=kDpg#66um9DUcUAh5ei(k`aD;vUv8b>59G^YK(Q?6H8u3pA!x-DR&FNL(iy_h_oV zzQ>zAX5;yilB9d2L$^M`au<^g>Wu-LOHbhoq=Vm<70F$AY@#A3TiE=x^|fdj%2)eM z?EuhQaD4wgLyC4OXZ|+fT2nAgmuOdZ*)^Ebx+2(CYnJ#M`SX;pWDZ{VR-PxdzJs04 zy7t1hY8`6u^a=82tQAs!8EvO!&8XG!2}?e8h+e4=JGE@Lx@izIC$nBE@b)7A78b@s z&}nR*%4^*HH^ln_vSxqK0@o=6Bg=4gKAgKD{N2yzoA!px5g$U1W@^YTWIXVnIiERg zx?pfLW=~x10(R#Nv2D{=ERmSEAmixjP|`x$rHC`?!>BAatIMKS;KW4jft z5o^2rr$6y`y}_+4V5H8k%p$Bk|Mut=Va9YpZ9#DqK>_Q>w|HPmqUD;f7zSZ9{(ILL zfM7BU8)fC^M0LV%`hEe}t~KeO0Jcji0wAe~nq#H#f(O{ng?i@!wtoxj&I4@c0k-o1 z+j)TPJivAyU^@@6od?*?18nC3w(|hnd4TOaz;+&BI}fm(2iVR7Z07;C^8nj3k&I4@c0k-o1+j)TPJivAyU^@@6 zod?*?18nC3w(|hnd4TOaz;+&BI}fm(2iVR7Z07;C^8nj3k&I4@c0k-o1+j)TPJivAyU^@@6od?*?18nC3w(|hn zd4TOaz;+&BI}fm(2iVR7Z07;C^8nj3k&I4@c0k-o1+j)TPJivAyU^@@6od?*?18nC3w(|hnd4TOaz;+&BI}fm( z2iVR7Z07;C^8njnMog zS^-&9$!L{}r;oRnCo7i8$MW*_@p7}Z)KwJ2Tm}RY&BdF&e7socOft)pTVp+6i$=Hz z@Iva#HhX)s@|i?f?#*cgqGnkuv}%IMtfv!^&fd zu{^zCwTH_l2OFF9%PiN~ubhaj09cWkF5X_bOaUm$)75T?sh*~qyu2)%PNYiF=+ZJ& ze)RG8L_VxsCcmebo6X#*iqaG*3Wdt1h0?JMX(~VJFu?K~ZQ@c8y;y4vRTxqfS|mMy zfumC-M1-*i0hTmnBexpw>A7wyU5Xk>Covdws)UFD5r>A{_^BJbJ#%2Sw~w(Ex+jar4fGhVt2L zDGvY+4}?SLI0juxkbo`%7-1Vb>8ST&SlLW|Zx;h0AgZ&>$w?7K3xH${iVz+J2T@ST z!CfgFU{F)J!X&)B<_MsbmwRmU+T^t!E5@KR=wc*v8NdlsRb0KaLIH+2$D5VK6lZy_ zmcy&B_u1-XrY39YF2E;E6Tu$_cwvT=zPG1lFhEn*`D8Q2yga6mCfIE8v70$oiYle< zE{PY#9t8wp2A#Il$75_DKx?^qWidrr-s`04tG4*q>dFX;(&;qWP4eY{2+62p?Y&(j z*#JG+(=!XoW6t2yWo~sb9wUHN5RjzP8MX@LKmZcbDOw)h>v1Su7Po!gYo#StZDm<0 z2?3cMcUSX4L8}zY0ZN#`rV}W0e629XKCDcpq^GAL-?YtJou^1*i0d04J=p{;NLkFK z6oQmEn*40sB=0Pyl(&N-eT|Qg^*8~PUfILWwr*!MH1%9AA(>0akN_H<=l zI00!ATI~bNfdfJNF$apadjvB1yqu@g2};_luTfdoY*ESf@{$clpUx9&R-x}R8@?9TLZn@NK<#M4M5 z`&;*32P5b^WC=DMMZ=r~IDQ6A$(0q!M7sw;`Gf;SeoQYLHK`!@giD~NT?j$YXE78= zOhpF*HtHb2qa>ldmU~4)72Apq1VfhMa8D;AX-WuWq2rbA1S6<)z@`f_q)-4SVzhkC znzhUHX_y0m!0i-8iwU)1Jd#Qe1negsC@qR~nIlJ`h0`$%!i2UUgwGBRJe?s0_$ae{ z1HEuQ%ymX|Y!N{5!)b%2n!Z=;nY52xLO8HH$#b4Eg%$;Qag$yKApGy9kUlUAQOv!A znK&=VJxO3Uz{+TkK@kJ!80s7^&qyZOV|!#|D07Lf0)-k&N268`vl0G>Vxk{-MJdMK zVN9%-kCUNz9uP$td2jQw66Lm?M%7)SAl5qcVm(i>PN z09pvNAVnA#43|@38Y2=u$hdLtd=zLfhA8*(!%@asvAnqB=FKt}O#>K-Wi$p?qXcy) zU4%j52XIy|_YU@$r3}Yh0_w+*fe56)FocQl51vMyw9?%hnjYihG7XyouyQk`Bxs3r zaRyBY0NpKz>RN7hc0ah9q62_1Yyg|vKL)=LBZy9<&{U1rGrc`oVN49mdnF?o;FOdJ z085RhlNpjYpcqzr`PS1x_$UwE3*m!m;b<`UB|_;Ws+6?4$y#^#_6vnAcQ?Q$0z5?u zXv*Os4M7+XY|k{@cpZjd2U7@W3S^@R&?2~O^l>wmIl=d0Fx2R?Ts#2~2vUNj(LjmiAJGFtC_8Hhf|7)_HxsVgKu-}(o0oi&|G>^Fy?4|-Rl?x zvky&R6w(E`4SR12?h-Hq69;gt<^}^G#T{-*lu&kK&C^%}JF*lC-$&=?_I{2ne08#X z=HPb%d~8Fr9T0`a#nW(@piAfP$03+n9GS77PKKlt=Wk?r`79^L0({b=D^Wm*5dmMe zLSSX{`P=abWY$xK<{?z@s41Mq&x!$JayOGZI6PS!OIh9cO$Ov`|Q5Cr1{ zO;tuG&?r>3#a?UpqX9k$>A#?WW^mu&0wA#Y!n05W^<0MyfG;G^hNE_vj-yg1tXwIu z0}uj{;p-+)L>MeXsn=e(9fsgzfyx;jf!1II(}L$`9HnEa)G0IZQGnoxARW$t%n0SS z7ZujnFc69m-fDp@$Qj@V$zRFf7{}q$GDZv)2?&c3hf5Nji!l z5CI5MPmy;87(nEDA4I{!HTT02VkDOoSq84dR)hm72Bf9o!vRU49YLyu0ZIx^ZyXhv zG&BxE>KFig48Ra_d0-BfkH?f2E-Zp^qq_lIv|7r5XeS=85{?` z00y2$0Bf3B;KT;2*#H}OROCH_9~=n|^y;v10hE$ti=jjXg9QkJVq)UZ-Gh%Rn$8f1 zf)+wSP62=usQs`evDOdtTL0DzG>)6oBkKM29Rn+j<0P!|DumXIHh&IDL2Sxf>Q0tu@Z6VOKiHW)!hbVC4!T+%WOiIaht5G2OmfW%?&ISYeUX3)eS?Fj%( zp#>a50NSAT!43pd0r(hP(yM{+xlo7w=e{5rQi8ZjfE8*(kW@$++cLNVA>QG~Dc}Gg zwL;1|3=op$zJ2N9B-9Cjp)Z_{W&=zzf?R;^q(fbIG(sFP26A+9jLd6D?+XB0EP#Qb zQ-tvqfS=rg44*azK=~T-LY@t9WC5GHTO%Rld+P+*fFKbc;^A3vWjGElMoAMbE<>7^lj{MVJeB}Ra^zru zpE8dEbkbhLApp8ys3H4CfZ>y+;z9vo3Gys&22ebhjKEa_x-+6wVL~u``#)qO*rsJT zpWy@qRZh49#|sOKRbB)lm_rPUzBmMRO(qAv*TlpLB>*p{rZQoH?PWOeI*jJNjzv&c z<#V7h=y0qE2LNt*+aG^CcoGViwRxs}(Fp1wJqHl5RPOtqg4qx7*j*3X?{*GDg6CRu zUPK~?e)8XOa3Bh zu!YoMIv~OAMZlxGZBHT)!FW~6eRcJXC1LBegw+CXg&?R(aw^w#4uVUL6na0v@s&J) zA|nvKmj!b*#YJf93xgVl!V%yB$pqm#1lJm$YfY6&aGwemB8Ao_wK$)L5PoU>!=7pmtzU6 z6@s;B_#%PT;~VaGyiG=kEz=e>=)XY_Q&`OhCp#dP0_=<{T^(=Z5x%z#&MJ|}u_X93 zNx*t6bS0o+$^drUiASA%5s2u>k@VykTN|^f1|fG5R+#X24Jr;TS z@y@5cLvaXtAWbrp+dNo9!ySR|>X`{m-7nt`??BLxl67(bio1fN82rZpfwC~Ey7k%H zcfkm*|8aBvM)^Fbi@OY?7~&^^AfLRETk?^kM~@eW*e+3njw1?BgRE5aX%K+g&-Fp! zh!T6bW)6fb0=)o@p?sJReen4WZcPS*U#b=LLcK5Wa28={;cXc zb`7CHkMK7U3!#q@3xcp`T0(q!b#1^WuAjs#__;9);uv;nLPFfZy1-8ye;c(}mCa5- zr<{d4h_%&YVTkERaSO38;}!%3-;~6-g4#gHaf3>(28nd2zPYZz7`x@G7zTOFsoI*FhK3yy3%`kC5G1AP(vysG>H}-A&1G8izl&oK z6yxLKQ?f492GwBeFKEwwAIe~S70Mu>U7!n?T3s6qNh%cPy$@zM12jWvEbhx-23|QS zF(I*{HiSzyOXDOroDroFFe_3mRA7S}g4mW10) z5|Z0c+|*RhuEjK5Ng{p~*1!{(squ*?A&XFLQ>FiOC1F9avHBab&o@Jd2VYy?aIxmn z%C90E)TrdR%nRW)e05Fr)dx#Y)HXFYa6QcWQ~9}Nwe@=iV7%dTr~{`Kn;f^lF0uw& zd+}l&>?x@}!bl#_j$N!#g>;{T9yqlf$qD6kNj0R}^8IQu{Cn#gjbZ(tVGkT5G&%WX zU3?8+ZGCg$IQ?wRs@nQ#kmOV71E(6rC5Ws6je*Vl^=ESz%WP_jWi?$q#Erdt4uIh4 zzNsn4>T1JlM528>H=EC&K3>bQwE5!UQ?-|DzKnq21=b{`?5l=`5v|ROa&vTW49!2+ ze6gmcuC_Uw|Eo9%LCq&Q>*xh;_fYkX0nO;9dT0<#-KA{mmtheSUdJaf_t4qey4t83 zT*|7PiFI|*VpV&cl)sFUhzn1#@sBSkJ8`z=!iDo^ zD-Z3Cb2FCwI#NQE95=^ii?8oC=JJ`-7=$k)B^VS8LHvIrSR%}rIAd+#8b0f&gn%{c zEGACWl0pu&MiOTGJqEM>^uPjagSRsmhaEY zNX<%ADS-hYueRfe;G265M+!J8DXD4cS(%PBIPKJ@!~xW_ngRrSt-yVIRTM&aeWSb} zEhRNQAOpk63(-smScS9;(ZbPC2D>*8A-*m#2Y^fh8Swn&dE?>S)YSCg3`|bSGJy<$ zv(9WlE*nO1Mdl%V&m!c23j5Tft_4!P@PzzTOIa2a9Fl>_%=VDu00RGxv>OQWYH>IW zxOqd_xLj%6(yF%s2)Y%2f(`rBS&;)7!l3v#15_r!Xl18dXCtV0n?m8Eo|6Y7<7NVO z@Fa!QC$S9~Xe~=iNe|~XdkZ5QVAkZNT!&HSVHX$^p4Oa;VEa8KY_5eO_yJYRyM-yK zkr|lm^d-b>7*N{+<-(Zr!2Cdfa(SJLV44l}Y621T<*MNT1Y<@|OOMJx=Y&tlg+aLJ z%#`av@SIAM4N%OH90dDr2Q3ID8emT@r3!aKa#P_HP6W3V?!tKhOHbpZaL;;L`7uFI zaMMhU8km%D%1mggjp7k5wMZ%lwmCCr6(Ju+Ni#E2ZoosNBlsjp98L9k1q1Be8}JG% zH##Uqamj^JIAIy+tYpJo0IQpuk#ZRxP#q(Aj)c%v*B5Ux_>_#P84sUskR$z96Vi==Rgiu5Rn;@fzAz~ z?*Z7=kmDpQ?39_+mxW-4!xzHF^WQNNTjH?ZetU9OywXZo>kCEq+ z?+|2ok=%30@+vI2KL8Op#*kgV!De%OCMP3HjtzO7vC)}9P>l!QUO;ALXQXGpM37QY zl7*lL)1m2!vE=FYb~c++GTE^CivEygHndbAWLZbp2S{Go85yY^$U|9HHXLF&Wk_#5 z@#Y(C*E=P10zPqYZ3{o-u7zno?>SN~B3AM*3J}tWJ9(+St*uQ+7L=H+=m*8m5l-bm zUAe2!#en!;{Fz;#au*D!D@}7|+oCse1!FiVBGGJwG&os?!^y~i#QOok8@7d$Dz;`1 zLa^bp++b(p3EPR`BnXtk!Rw_?%*n{eSp~!Igw60S*MhGD683nBDcRVCWuzChmm$XG&?7e?7R=bU701eEP*QxW*~LLC*q-yR-(B)o(H0Pl8k&$k`RGlMlQQc7r^uxVCXp$yQY6X zLK<48p2f+?+`(TBiEf|{!Kiw7v|1h@pyT1l@29&SK#0#&U;}epsqi!ieO>rQ41#`r zz%U1Znee%~CEI!c!PklfLwcR5H!FN}kI8lD?UJQbZ z9UGS^vJ283qxZx^y0V$@^(NQ}XZ~}W*9q`UFWE#m5XQr2r1!ytbbDc4MR){+-la5@ zh@gk}sKRAVwI7}$R6mSEgxXlbfULFLE7-$C5rFA6@Wf;&#%K>znqirOpr39f!b}8X zWaz?Mc*->ri-^9776IB{CL@s35)qW=jx+>yeg@1tuzg_$WRLhY#gb*iy{xi)0N4d*Au<-8XAgC&}1c2Z8e0tFi zbmLBh;0NTt$SPPdyc0p)KbVD}I?dt%-skeP#$pr;9?SWEN+Q5KP)1cjqIVmt%0R2kR>LlzU z{`2|29X#8^4UAL#E&%{DVxTd|r6s~M{SOHM@_#_uvL7A)J4y6bAi(?c=|654e8|=l zzB~L!hhgqiAgI^6@O=K`+5g&Jcm(x~Zus5NKRnRBc?v;Y7YPT14+sCMO~DAZ;QM2L zNO`>y9>ab<^d}|s2O_w>nbO~#`J=WlEzG*m3O^1JhX^mcF= zzWayX_B`*n+uqg~T#vuhe)mC7FU(9soq?$d+>?GmCjiv5ul78+a%2ri`SPeAy$E$H z1K}Ha^W^TOGl%!?%FIk-r-r5oK;I!VZ|~vLE!~3%vL+OWzCZ1U?nMMAGd(3+oBGXx z|3vDmWJI9v@twx1k^)ZZdq-wx201+q-g5R;cQp%rI{Ie=(!$(yxQq%e`7Q&1ZytOG z`T8DSt}M&v3Q0>(&fw!@W#{JRq$UIfBpHMP;rHnP0E|iJWaPMm?~(%0<$xUn-HtmK z4&|l8Ws=^H1~9gg688jHF7N!*Tb z`M0e%RuM)gf6OX?>}Z<^Rq$PE0j3TtxDkTjhVP%sPDxA3AVcjrDWMyTm4(qsUnLsA z^Z+yge4lHug%Fi@7f!L>8)fM!>4_OcPIm6jt;==jgtYH74@msqWgZAT64T91=bX6F zeEv{gYHDHz0d^qOcdiKZ_x?T)0q=ti&4Qi)I|Y@Ro&b5XbJGIM$H4XCXW0puTKp0? zIq;de8C&O$!R3FKso=R~_m{hT>`P4r4ktAOmz@*2OaZm$%d7>)cz(40ryW0bXan%g zNy)%w=WN#{!(8K!2@I36<82jy3f%l-0>OgZ6@p}jXa`~BURz{iJRX*rX)bmBRhMOkQIy#V6RC_g=ZyX2xMh4 zwbUKs^K#OU-n%us1Q3M8MaAXSe4&Gb&5cwrhk*jzRe6DsJ=yUYLY!dpc>$R@oQ%{e z=&JS1g1!mWcK!MdE@_;g3~W}q>UyEoSTxd(bmQX`mbV{SLa&oiS9DH^G3$2g}&4fxh0~06OB%lu50{iuLF_~c*f;ovc6UGSe z2~yO|x1~e+3=(|CGc)pLf%Q4T8K_K76y&=U3UgVYDv`AtY+T{yfsd1ul@b*Zmywg3 z4gEcGR)W7@1Scz)Fd;h#ZbQOiIFYbNcVVL1J#ol3#(IN|eSED5q>bKU2rayimE!w_Is0vXnBu#L)0$`I%543npjg0pfa3MRtw%*yY^n}_zxb(NR#%j3jC4b4zPuRP=+8ra!yOM!zy<@mcSvs!57 z;GdS82_0Wf?l!#9_HhChN!f95^s*mA6|6&mFvikuQ$$)OoE_+_oLqJuIwy;3(9FE8 zVnCcQIV_ixkbzDu>4&DQ!yW=8tnxxzx8NiWcU%NBv$7MG@WJ)hB{x4e3sRzz&q2Zg z%_D$}kv3fEsfUK37x`GKja%lMk^6oE)rKIzv>o6u^6QdwxkH_PHy8n1?g1=g zT~cmlI26A7RS*J-x&W54FglmJdrLVv0(YC&@DJdZDb}Xsazfz*fomx0mFy#ck+$Px z!wp#4o;T0|o%0xACiv&V?G-8o?uvlV6M!Yoj?Lw6gwnI01t6f{8O*$0pTXV7q?C`q z&2af0~S_{+DR=fgaiOsp3 zjPxA12@8A;NVvtBSs5wk5hS+{5U1?SNzds;&cY-X0)0zvN)yt6f+;IRo2#u z)Ts>eKN0m?ym*?nO>}0}xsE3z$mi>M>lx1GwbPVfs{6lr;nkdXUwZb1-p>tqe>2ke zM@8Z)HNk(U3G`)|mthL--(CZE%a~fDJs#9$MNj`$^PdDmt;w@T&kTgw8IS&y{i}C( zl6CN-F%oi~&A+Ph8@k~JN3nm}f=MaAWA|?wfFQRwi~NHYpmYl0Tk4-(w`~8{1`y_+ z82)FvKX7NjFR}doMY3)f4dq-J)yluG-nf3Wv_m;9zdH$}(qR~2^scw?%b!2K7M`fy zivF%@0nB+G#p{**cN#txtbm&?WG~z$eR&DRkIJjU-tC}_o&aq|3O1X&;O|DM{Gl3f9nehj;gi4Zp35DA9VPeBIUlXc}DTm zP4#bd{pjPAQB?S~sFTt^()Vqd_Fp>EUrYP#PpJams`}yQRlhCxml*n4-@nj)sDIrr ziThomM{|Ct><5M6zbpDz=$j_|K+}(l5`IPAZ_=Fl4Tn4NH)a0<`6bBy*)|G_!Gun5PFw%RY{RnLZ*c0a%DzoZ}a`;qd$ zc@tocjk?aS1->i$%bgF`Fw;kOoAtHs51;y?7=Jwk@LSve{`D~6ubxKisKpn4-iv0V zADck9>*suU)A+P;hn0V)dOJrLy~{DuSjYW%VOTR&u_upe_7#Y@lWfsyj@J?h?tKv4;U65 z**XcBekS<$ZhCJPyBShHExI4|Pw#~a2hPO)N=C>3-n8zkMZV_pKRO0__P-1LMb5hg zZpKo7dHnW^LjFgxzM2U5W5PeX;E(zJTi46uPD#v>K4eS1uioYr6TXLZL+a~n4721A^nvth(Ir{Khf**Wl{*9iWR7+XxsBVAq z;dtoXqq+o}3Ew0XNiE&=w+ZQ`KT+@tRYH;)2KxH?29w194A_3jM&I-|X`;7&-4p&& z?yuhyEXv?-5C7%$=DP!@Ul;wWlc5vJwN=Pw87w;Ow#ww=~MbAB7ct;@9=|2>Mx{#{+^bfQPUE?JI?m{ z8U5(j8CHKY3qD1;M_<*CCE}gGKG3TAG1J` z_`BsIQJ)usr&7PmJSz0FKIQN~`Q$Q;ir8NlBANMN4E}HF2f*!pRQVb0*V{bGl%`zw zXyi8|L8IA*aka1kKmYc{XsY{OmQV@OF7bWwe^)|U`c1yef2W$K>Hql_aJ1Ee30YEC zBPx7Q34gOo zQV})BO5p`0SmC&MIcvH3d>z}(o78RWHm~2H?z71aQp4XL@zH^|n>Mg)WPLWdxH5Ho z^vAxZ(1G{dVvVt~?@3tB`eT=yTgn=5_ShghMSZfm=2!!=tgNh_hn>BSmC5Xn?C_KR zSO*r%O-Dn++uK{+dy@KQkBu5y)2C0@(443-aiSWeP-AX!W!dB$(WCPRI z!_kf9xY<>fE7xW{gb&moJC>{H^Yx*fEJyo4HMufBb_;e;gIlAarLL**SvF+V3e3au{!7s%6eI;^c`?C(D_VKdwu&-H(j6Q-8JYlq(d>7 zJ>$jq@o(-))jqP-u5jhEhux1KKP+B+BK{9oWdgy}gZWxHVK4QL(QI3>6%)4F&YR~L zv|!naq8m4oa$?2W8E&ZlvHj_f?vlV;eUXYYHOgLV-P|su=H#%FP9r9-UPX>gx)?1^ z4!m@#Stb=w&_u~APc#-SYQgO*8lO|2^186HGN-J(TtUWgd%ze2LU#A5J#Ef|l*+fW zhp+nN`^BB~y%ZT9K0U#9eX=$VXRdVQ)*|7f(sLUc0tG@D(h;fD$x4S>jaF`&zGRud zMM#LQfP{pv(4)RvHx?e@Yj`k8mbL%{YIVNaxpRl_y?dLZ82VccwruqfiEEtd6HqSg zOyFy@3o)CWU#leM9CEV+>tK72q_gFfqLbs6hq@>K=+{nXh|=D?-lz>IAzDje16EA6 zIeU?vntJ~J)29=NjR|z}oWLin7k+O3M~-0ol%1SUjyt|OsNn5@lO_WYn;#Wl4-!J< zO&g@2RtE8xiN=DpRd>1`ru0t03Q={va8Fl?uQn(jQIfs(`o3az&b6~SHkUVSn6R@@ zznJtc=A2$=<8{2b1DIxHoL;4qc%=H=o9c5@dz2?O&PFbsZkyA(>#^1@j|)tidG-0- zy94j$x>}@c?bcen>s9|Ek5@s*3U(imTed7ZC$D&Skm4M-Q@ z*Qd(+dNK9I(bLnqx=y`OXlunLpDaHA;(>;py@3pkrZY=;o67j6pz)~FYwwyUFQ|xl z>EUoL+#yhPW>>70U!|1eyKdgx&+3k!Bw-v}SZKn2ja^zU>E%zmK`u5ZnanqV)QBlM+S6 zlM=o5g?1XR_BkI(X)n8La^;?Jf-(}_BsI?Q?ek2#sknHfv>3gan4Wd0{Wl|$UnM2$ zbWUvAuPw8+1brZW*|7YJoH=;*`W>m6NBO*J*1qj`&)d5wJ8YBRA=xQ??sMm_b~i6- ziYYF9#Jt(>N1J3n{mA1BEvDHO7G-N!b!*I9`AB2kE-QCiorbfyw&z!zziM5MJBZf= zC#!T%S6RK2oVKA|E)EN>@GYA$RsesLJxloF#ivALx{BH+wP=|ctd13Khsu){WZqIT zi9CE*fOUVl*tO*10@IE6FI`d9D)1r|EPJ!mO8090`i11SwTC^+S95S$D!OOxR))+F z3KW?Twm=l98?LPOjeFxIc9W=PxzPDZkvMH%UU2a3q19Vr-n!hFP?*^DQeP~^`N)#e zz7iF^ZM3}{F^3x)?^-1e3iV#WsEgCJLRP;szI1c0y>C|O(Wx0^gEIpAyCSd_NgKBF zl_|Y-yyJqcUxJAZQYHZ*?HLwc0J@A(;Dn{wdrcwfE^ij}ito1zpfvJGE_hbx&TMNZhgw0OU= zDnq)xzgH9EwUu!z)h9i=9SN|>I!&J@6BHJr04`B)OE7i4)-!C^?THXuCNF90QS7NN z%-7R3t8}AQK^gPD#9P3zpCl)L&qFbqF+GdCKvdcw-dw10Tf`Vng3+eJ8`$Wvrpnrr zSawHl)uJD~v7hu@tzxivw%&RC3;W|kovYjrN$I&<%`IefGFHxt_E5}xzGS{0e(lx? zlXKseEpIe7p4Fjv?cw;1tmn@=lO~m82&G|-HHY#wG7l_WWMi&Y?(1N-D>R*nA^uV9 zj5&mt^`F!&JV*h{ckkReB63J&ZETp0M3!O#U{_KBTIk}Nb%Gr|qOW?|K)Z*aWAU3; z-phU3irvKb7RnCZIDKw>%E`UTEjL1RA02HBU{#3DspPA17ar$KTWPkDU*ui*A5BUQ z0o8gIHZ~jMbZf^s(+=f~`I zx1G1z=lI)R)kVqHIE^zR?h_}j1nf)>UMp}J=i1t)iYrnYqM5p;y?=P$)O#8b+q9us zqq4@(Q&EwTSv4{3jr48vY4XEY`h8kf=~`Tgh`(LCqDx?pFDZGUv4H_ob*#~0(WBzK zu7qsco*$>$Ru~yk7kPHCRdUrOH``UW74Cx;QHC@1F6ZQl2q(Pi6EYW%Elke}9qr*Jbg6t>@2U z9>k$uAD?pO5B>Z0rz+pv*t99I$z~}=*>QWGy!^wg7k$GGM~de!UXk%^GS&_CI6tJV zAfRi_>&FMDx^)&@^9zhP6((RlePF0aWN=4q|9m(5LzZu_*w>=L-M8AT^^CLvnytFK z?v52$JJtl1|I*o^YD0hS?O;AA3MVK>i&v)@Tw zn64;Obj+8;PV4AE%8%{eq@(0wy!!Mm+k@=E$n2+WIH3bK9_BVCB&Ip$7Z-;l#>e9U z8nr_{DN+2gbIEY@(ajo0$L~Z$;JtgWiuO@)oSe2hGKVf7Cp-&k4{Mh(+?IFl@)e=- zaYOX$M8P9ZS1D*J$T~S&REuXjozrTaJv7x6v1YPn|qApc2#z2KYJ!1tUbos&}nDk25l+R9Q&5>Y7z-*26CNS+QD29Zho$DlS0G# zh$5qoi4Qx^2Z=rPy1kHvs(zNH0OA`S+&s@GrF7+KtH{kX>6xa}dheij<-L%xo-*}P zl!j{#-8I!BfGAP%SpV|QgPe?8o9`SKY1fE^-%+Cq#2w3isqyToB9nNxb+IRN8xS7Gb(u=h=CwEf9?&hu9ye9b2 zNo#E#&pp#_wI@y8+{W*Dmw5Hzl`t8c^V?4 zzUcJY(^2+~b4AZ)h!GyB+nw7(_wvv={)o*6PsK_X?u;W3`Ms1cDR!f{ZgJgSHW?d~ zvsB~!g1)vsmFh}D%;J6L`Yg9^-@doiB_<-ei{cU}E0aGn{?c^+y-SMTTG~Irm>KO1 z={4NXD4cow*yftqN+OrfUg}nRnLq2{!37$O@x^@&LxOuYfUZ zmunD7pW3+K@?~3LXAu`)*%&bY_R&pZ{U@Frc+$Ic$r4e)=38&7 zhm+b1zFPV2F(gpw(Q^{Q$Y-yp@F`bncoiA3Q4utKDt`(yTZ(X^sv};DtSy4CYY6)eXl;tndUfj&k)7M`mC}Y4m zwXr$tR^O8wVY>?oE?wWhYT+eMElxT0) z$@cKz*OURyE&F^8Rw@%60Q|WD69%gnsUoXxz9kV4kS$$2U#ZtM_fy=@p zO6U4;H=_sou0$_ycsyfso8H#v_hQ{fJ3kP0E6i(~G zw_Z`$sJP$j#@d37jz`>U&1SVTj{6$;?wcJyo)PkLu8>-Bj+|I*JGo>;%gbY$o6E6I z`m~KKQMIfrlSPXzTzdOZePL%}>tUyB#@ctDKD|@m?R!E}BJ+HCn@8&TiHi?eSa%G` zXGxZ5HwO({3Z|kQI`=ciQjov$(Ag6 zUvb|2+4l~tDAdMQ+MhUhMOweiFKd}{jId+U)q2dT^GFX6Una^ zUn<&s+CqITN}S)cJG}N%%ws$!)z815u+_h2>7cr=u435nmNk5$Dta&UGS!9yc08c4 zn&*hGeV%kvqd#y<*MR)~^omn^svTeLjYD^6n>$YSV>ll>=32uyoEx!haQ6Y4>?wV( zmnC^Zr+kJcTWo9=xi1wGF$suHSKYg}EX_cl73!pR;Hpb${K7qoAxkW544M+t#OIK- zI-ag6WmC@1nW@`Wzgwa?85H_njG6)Gd@O7KOG!EWdm{I#^tepl zp6wk_*{o4u+#YT{;{@`cJ5gL_LCE>8!|topfK9+@6w{1@eW2Ql2iy1#&XP*e>mh_%TJKHf8+SR91%-r z;(^fNp}{R3cCB#Tyn5fd?8^B_r>(vZ{Zkmyikv88qCg)M{VuPFGo9#-^DXSif9kVx0*DG;d!%a>9 zuH&_`#Akw@M;3K;DUCaQ)$rlCCr+>DIW;Yw{HXHWP*32Ks%?H_Fiq#Y#}v*yXOd@9 zxM;PhM9`&#rQtJny?t3e=BUhAZIh)2hswcelR3U5`-5bGFcV{xAuT*Qe&ZP2gIygP zl^FB+RPStA{p5I#(0WZt(Jdusl>P1IE_zAfvtRP?k*snPNv~8cL*qVvwe2ywuz*$U z@(Id&+RN6AyfN%4!Lb8kw06F|Df!?`nI1d;1odX_JuOs2OM)}@A?|W5pZ%K4fuW(r zIzb)Tpk!U?%emgPLZL@(9zOGY%57@iZ0fH&FVw$Z!{ccrQA?f?F|N+^h2#kl^%{b* zMcY~a3CW9`4h`J4cwwn1&i|^}0tXy#O>h*U7_L6+`iQu6DIeBo;W=N-f^+@HqlFyu zL#`e_bxNe;MUZV_ElMce&k2~JHHB}IZ+XeCnAoW|k$QJ~X2e;`hH{j$DLECh`GSjf z=^s`xE$z)OH5~@`qGd_z&o-jA#r5;^@tdv^+EZ_ELwcoUR4IiN_9)_YlsEn98zuG> zpA+dT_$!~qwg$$YbiE+xGu`jhx=5LaXGpioRCNNLGG^>w6kL|ykBqIKau08QsUq6n z;-Id!%OL`m^3>IJJVmyt&~GVA<}xeuq;Jfc{4MVo3SqJR1DEHU*3ic<*+-vV|8&6j zk7khIQ+u4)u%bnymk%9e((aEMVFz|_Je3EyA@(`57ML6`vJP}~nFM3{_~BxJ6PA>q zijfn50H22GGFwL{XNUERXH6X&4MZqLYgs;Co-9vqFQ+w&=IhcRfIGkk0IZHM|Jeh0{BGcX4%<5zmgD(n*U?takF+KdV(zm$H+jJL+5hv^bqO{E{CNuZ& zVLube4LyjjAXY#$97y3PlzE!GEB@@@`R6LBGcGHwc$rmizhQ4B(J z)9K=F>Jn6Qrmy$PDZzjyB}1iCDWWO~w{LrME2$JJRn=sThxbB>KtPalhSKEAv zdc;aRh)$qN(Uc6`=rNeW$z4K$C>MN{v>Zy1Wm z4%SQ1A%3u!AQ_KAp#TbnC1Rx)NBaX?m_YY-yb2OYriv2LkpL%}zQTNSO8|n~;rCn~ zpkn}@Pk*(7CL)?y?4gV`izesQ+&U(6)ub)F1y&8EdSMIh*h z=`-PyL<<0D>F_rAJMaIey(^E0dVBvL2H8R>=DHGYR2rg0T5b}F>|BIMvc$DcD9jjR z8B55%?^(KyElb=YWh`aO)(|yB+#+jZ$@2Lg({-;d-S6x7$M5y}{qZ}0e4KNh<#Xn7 z=6Su&Gw<^};~;lB1C`QVYwQc^Jw(SdLi+W7FzZxhQFiOQM_H*}a()owfyc0y^%$5Y z*OV0f;gyn*YWV{wL2{yM?mkHR&brvv6l-QraZXRxBR~kH(gy>eR@uyun?+(!zgwa@ zsC%X!Tp9o~k2zpdLtoop5(YDeZMq0B>wH{LYG2`}p$87E{t)8E{EKBDn6)`GtqX>y zuyt?N3Y(GK{Ggq7xyu0jGZ~uD-cBIFFm-uw0+c>lxi0~nkS+%Jgav#xa6${Ab0E?C z3P^I_a2B{ArR*9QF3zO6ppwpDn2vm5&Hely4lf2y(A-Z)O$fZBQxUkHDt&4r;9PS( zy`_FIa&=*5a(sdcKNNzXa`jL*GR^NSHrC)^vDv}a>Oyd2%Seh04i^J{$5RZq^wsaA zw9mgO&q#<*P7QN4Q;`wG(L9ibE$~3wZ(~2YXWmVSPf88Ht}PM(F|x7KYh#MRC#K$v zN&;0}Vlg)if9SrS6s=G%-1>=qRE(ND%BqwH0!1=&2vFw)- z2fmc$$8*-P8S%x-y?bLJCUL8zq_!_Q@!(&I&7pcGKnPxorZnKDECYd?61@xkN=t0d z@IM0`vZ6Gsy>8j6ptefV!=x17^Cy_;%Rs@^f1sH_0Tlo-+0ceuzP_8$V2xAxZWa3$z!pKkBuE3cpglZ3gTem;TMqvp0JbnIt^r#NS?csLYSHt1 z;QmANtlRjvCA(Yi;L_u_Jl8I#%fj)>RyDu1%=z-ghca(s-C7cFBrG|)en_;Jx|o<# zk8$3eSm3G6u_e#a{dmz9?+eXYT-pNA7)Nt3_io~ z*{WpLCo+;S&$CN!vZz-z1jUnUTG-~Nnw?{JM@==b&Pwv+SO`G@VUeK)6R_S5K|Vg{ z$;ifG>xjx9>0|NuM8^U%?*X~EF}tKaVPZCA@?wm4JJ|}}iP=)0EiIptl-;H%j6)%g z9_2>zFE>!-2Q&$my-Q3gijE==6yRvrFkutySV4Z{M;u3U$fPc zea+R#9%^&*?%SFg!u+>|TJN-pjxm2ZF>6NJ!SZ|9TLA_QuA9pTU`g|beZoG;OL``* zPFf1i&e`a`a;97jYs&?mnct<=pf)d zJ2)Rx_nEC;G<$`-U_L@rRCq@}Y5wW8`pdC=)oTAnqJDZN{ei>V`WS_N=)|5>?e?y9 z6gU%}655yk+(k%CM8$ACk-nxcm)W!_ZuisPa3%VH3&PGpAN9GN%IGCd)P<3Wbo?AO zAFgRtVl~5eTetBsBmMKQ?3N`e3Es_a)OjIQYE(6yoNVem&JcAnF_4#6#=-SCDyj2> z2a=tGQ7cf5i%UD+Qce!TXJgOke}2$Ly~3%Ar?&Iw@zsy)Vbv)itL@!RJc-ji78G)H z4Cc|(p?jDPn`YzDdG~Og+e1xK+RV{$XWFkld{T~eaM;Do&4}Di=p=P zB$!6VFG}w?Ta&JDuwoMXlic%R4W3=co|*6VXG;Afs5`@Cij~8iBm6eqrR#tD#iNA* z#BPD|bei`v^WgSvm^>_kh+SdkhV6}lJa2^K@E^671RgVfyKs455z zINeO8l(Cf;6kH9EHDcu7&et9%HF&F>w@%3HJnJk}B*DvTPY46!)7(Oy$ z(y!Pur}&JpxVS1KWs;1KMwy))b$KuJQ)dr@Z~N3tk%K zAFkrq_5M(dB$7RqgYf=}cEq)@G0&)H35xdioh@aqC@A+9$D>D&oTmmfZ+&RK6R31h zjb4kt`5ioT;tf_h|i_22A{w8%!%B7QmTQ>h? zY)>4sL?QicZr6_|st%`ccr{wP3rk6pj+}YXDkomMq=3P!NEDJUn)^*>@wyFowoO>J zMh`f#x7~Y+%gGVtxqE5EnNK;)L?kba>?C_uX@xiaoqUiEf2P@cat&#Tm;?_q_S7>u zi_BacbhCgW>}8U?^Y!n|k+LGjeKlpT+PO0771(xRhs^Wp0+rYAZB=}cb!y3|hXK}wjZVny3%Xzul z8EJouIY6P}wdanvBWDDqJ2)wMd@iVP-MAXYh~sbMg=A;)PFt&F<0lM>L*g>Qt(NLs zT57@jF>*XrXtXo7tYt#Jpannfx3~869%gFVIdfb3-Hf8gXF8ll)oVR}Cd@MAEhz5H z;4M(H8i~6XrIu4vWD;wvF>Ut;-^tjrBnDC-TP@a6kPG#8p!ApF6SL;h2?M3Z5gDrH zV#mX!yqQHm4!*d8geQi#rR}8a6A#H{b}(R#HoBaynti^z*l>yB_=8j5*nZ|BS*-KE zDGdY4Kr(-__%U0(ay4Jm%A22e`G%3Hy#kblLk(|dl`Q7iYwQ_N$)avgociB6pHXV^ zRIs}%KbjKxnM^5(%3S`HTg9Z+<@BZc*S{TUTPb)Dk(V76m{5#-_eHqk(gRT4r$<*wc(ShE2;H8Ds?LQr~NY>)cT8)VH(t z=>E;{wT;*3>=^l+xH z&h?oLi&wMm?CouMJkK}aKt9xAlPY&z2A&b#PrO_9=<*~ww8Tc*ZttQnOMe6vhDPLX zTsa*O3d$aC2#BKB0;2RRF%S+34zoxpG{86Gq#z_7f{-{g19B2VB0;P{2Zu(GK_7%W z0Z%?!2Z{8B=+b}`qOlQh0tryKa~Lp#YXyxsA(LaF?=CLu@5P0H z{{PbAf@q7=&B8T7Spg6|I_n|mg@Dze(IFwRP!vQ*p@7g4ct8je1%g1lgF*sD)**;Q zA>v_HG&rwONF<09Ljgf2V>AUO#^4}20Lp>sh%r9Ej6j^eu@RKjTeBfJn5@|lgk;tX z2!o-)Fd(r~fB}g#1Cnq=T+DXdx)F&)oRyUi#7eWIGm#h*L-biQ=V3H14npIIbR-gp z6e*)az|a^^gh+UZRax2C*vblriy;z&As;IULeqi+bl(XMd_4fcVB^5x@#hHq+1RJe rZlKY~-;CEb-rusD>Hk&VH^N`#H!9pp|3>Wl-HQ+vfkk0S000RkT180)4Ve%b001;OSxL2jz36`` z7~x-AX5+F30N`OcNihxY>=QjCdksyTZyBe|7Sh)FI3HtR{)9u2kYy<=qoKx{U!|#K z7h{8)kSvT8YC`hCDVoR@j4GvE#wupyMUwXN^sy?v-|1xOkdT&&=)TYO-mKl>zrJ1V zvrdbALtfb_40^pi^#|HkDWW#dX)l;uDH+SBio@L8U@RvP2n13j(pVBc zJMgrTOO_MDGizaYk`rUYO*WSEH>iZokFKEd^9SiD2{4ZEEtZrl%QnddGA+*E^?cjS=0CinRNdHDytZ8`syEd{m*)*f1pC(S~)S(*asZMN`3# zj-sOA+cU&t;#3N1g%Imx8c<`&ftmOvf*&TXtWsfdP=UWm(dl+HOgqOBk&$+PLHb zk4&g!ozqyCFHaK#MX#)aN=kdWJ!50j1Q+XFIxs2*pyDe|Ntn9&Gj9mAwbTiVgcoKM z)DDLTpp%05+`+~-whxW>_nTWT_Qw*pYeZSix{=At?|j4`2KY@Er;AEGf*pjZSh2eg z3VR)xesPKCL^0OdXFTUf#omkJ6VvfL2vVmDT<##P2XZLmkP8{mcWdPqHz)Oa8XGb9 z>{TlVz6E-~ppylzvzsLlJh=uT+8j%`h8{r6OkPiKZ_5| zY|jLGJqV{$Kj}xJThur_GICskY%a~};V)sM%-W&30ZiR23c_{feR+7u$Sc2D4UpNK z^9<3~QFHevl8{9E^8Go~!>FV}D?SVps;i@b*yr?^plAc} z;E4xsxZ`cuv&`f8)QK%v-efk-5`0L<4(8Eu-x4QZY4wP$rgwlTp^1L(@Vomk!-X%O z;(2#!5EZ9CQw9spqY&iwh_}21g@=caPN^11=NE93+6D>X;NweJSo~mkAWd(HBj!&c zNhp?s#_+vDL`9v?V|OH?$^ba}AgSd#3zRjSgW5AY?toT9-Izs^pp`igxyo=QAtEds zTt>343pu(VdpwRVY)WcsqYX?0j}p#{j#C@;V+hc!#H@v)mAZ;fB+%48Ap82PvMbU zpSB9Dt`*G93(Ej5W#<+ine zk`QleyL5Y$8AMG@zn1~mf*+onZ57q8?B9LT<ms4S!e61d1!4(!`rARFCSWKHb_WW z$<2(|KnIlT|N_x8Li0;!hQKC8QWx_+f^j)zf+qImSf_(6CQpHG0#C zh|Oz8K2*Q#y-llORS82O52}T}!(sd$DR=J&;L9UNQ=pr1-AHBI?3{Fy^M z856-UX~Mt4y8;g9l}~oM&z@zoZVL9Qn4!A8JI*zEf8ESrCkIh7DZP7JOovAN)lmx> z*NEZ=|DY+`O3AllN&#N%QrYUV8HC9&tw zwS(Dx=d}fHO3})E$7Kce%FSG}BcIELZN>(`t!CNlz5^HR8HvmsBtqh7?0-@Feg46E}dIl#*nXxN(S$85=JEkbL-W zq!9F981%oSP@k~L2mla2{U0f0fY^*9g=J_s&JXty3xb19=Zic{1*Q~JeGit!B7Mi6 z=Smq#OYl3P0!j%E**rg&V1wYueTT;MQfx}TFGG~|4!F5fm>54EKmAW_zgu|heA})S z6r7&cH27CEPr&Nwy*Nc|;q(e7tswfGMA0EibF2ZZ0nBl%bd*4f_Pk!~F#88mObSP@ z^yj>yF!(Ssq^i+I;NN=Ta%o%OO0c6sOkA8lD$=Q~kDL%6_+k`g|rUYJT%Jx9It5YaI(K|to`uIEefx%LMp zEe-dSc;ep+W(n-KLsUI5r}0+Te``Z?__K0WXR<^q-n-Wp6bxhk!1BcOF4ii6{N~`S zHvcB*v_r+C6UT$nuzN9>&pqHWhW+kc0WHJ}`}^_XP@QCL*dl0_&S^lv^EunAZgrHW zfPU%c8`QvSRa9GSLF})s_WoDPG8q*C2F`l)qdykdMC`qR5xUekvt^Bx%y{{D5x>La z1H&)q#12w=d+Are^;sr8uyLceCvUrDxJ3)|FSjc2y`r#Z(B@`T9_J0Y<&16C2#ho~ z`*e{1Ttx=9*V=ELKw&QE?bYFISMgnS9$uI&&*l5SprTOnljrbqEeAOguO3eXLiicJ z$yzEi#Y{HOG48nC9X4H>k0m-&9~d-EnYeFo=Vv8a-g__YVCKG?+0C5&ySf~NkR|5t zPecIcoP@vhqDbT7)XNQu=E;4HlBny>9JH%DitFHdh}j{UQNE7(~S{(Yc= zHOD<)*u6W;u_Ekx?3BB)p%N)+!+^zAVrBL2+z4r3$5VRK$KvZYdIaef#yyB{>63pM zbvC!`*+&e6ZldIGFN{ZP28VecQysC<2pzTppLsowJnO&ptP$j9>&t!eGV={NGr2#H z9p-lC-fml2F))00_9ly;E*pjSLYz2!6S?7=A_%L-7duxc)4Zv!@!mu7<hKc-iGcO=toGgBN#{w^e#@JI0^G|ZyH;D%lN!bw85^|L^TfEtjPsEE z1qDS|WdJU-B~ZzF@kGg0B3rEz56F{1w6fAFo6%YI?w=j&TMu|5t@$?POYq@7Xr)_T zvGThMXP=DGkC^K~=idIob!7#S0-rNdp~BSQ>ng~r=({&U3d7Z)A0ayfd_;!~Ksxda z48EKk{9&3g#)q0YPw6&ThL-^%nakToho`D7ziz)3_vCBC#l@q)PXt=*R9|LGQ{>KC z^sd$x@@kcUz4E8ybx(G^gc7mF4rC})G1ch;=!-EgoB|iY+t*zJEx%fk3bERM*8|1kJ1t3xnriJ4Hc=I#7maMXIk;k z5Ft;6`^kz)3a>3!&d*AlY14Qf$ef>jg}DblcUJ@n!|BK?XAAyd1Utt0#OzrteJ?%m zs`I2n&85!SRb>!&D+#VBjPRJ_B9f*G1%r3gI`d*#3o@t^dJD8HCdkMln|U=r$} zLE7`#bvqsjz3}ltyf2whinbC_@|HDeF9%A5f)*ETVg8;*0xRDw0}BJ}ZxvKzW|}cG zBZ>>{r*Wa9qZZE=0v=1of_>lUOgemDhL&fmR{e(c6M_Uik}4ijQugofesv(r?Fwde zI@ywWx=+b^dcRVU!snIz;{_8w#_hqInrSz)iwOFmoJHX= z?|p?W5_IW9>nm}%;VK|4(bFb;sDUrrEvkD__Y;))F!t2d^+s%FG^$o(0;@Z%9$)zgdp<9Mkeya8GE&{kd>oT(3 zD?r%ym)fODRge@))fJcJ;J!sm!>6BfmsCsILB^{xVZ69qj$_svSP(pu?Ou+nZtQJ@V-g@NyiWsZZMf7I1`>3;PO#uT>toP3Z|V;s!x0Db7RQKzWr^Y#nM zF5$ku{quH1;1mxisBm^Ed_jasm&&QH#+@Z2T27A?|HOT`pM-Gx%RhLkpkp1y?v;@H zz2%P|ik27bs!I=s17CP;#?L8%T&2($FMKFEi9Zt16fi<_CH4zs0OQP8G|fYcN`~l_ zN|~~Vyl2)xXwf+dn{&!-4i+!Z9*v2Twfg2%c{1RiXa0g(4eLB{GM{{sTv7k{MGKNd zJJFa>6R+o)fsv#Q;|ojvj_csS>GHGnR4?J|+nnAWXrtrq`fT<`>=S~LkQ>k@XBo)BDoHWb>RUI zyJ_~TtseGIw@r>n>J7u&@5PvzB}-w%c}G%&H<tb9zP_hekrJA&aH(@ImwwataFOw&93nFOWa!gi*`swX?#th82*}{#J{a6#Gci-7MG)~i zJ|mBH8Qe>>dY&*$)RTlv=2Ne<(Gl=kjuJH4Ow)|}$9dLYkMXY4-3^beV&lNbDv{#W zbij-OXt8KaOaWu3#iHIz4L&mMj3ElulscwF&M+k^CYWj0Lqp)I5Ua^sJ+=l>e}9?4 z^`RlzCbL2Gaqo?VTC<`IR_m+1u^5PVRxzAS{p?+s=w#er+Ti!FY|QJ!n`B-2f~^=$ z*mIh9I=O1K+k4p*NAuPD7;^GiCi!qh#T8ZC3j^^)4{mc+1|uIoetfp<%o~XRq5rF) z;XrH(wg*IPeC|(ahJ;ROr2Z?BqIma^dX6AUA0YnhN0+px#N+-~#e${j58lwYv_PY~ zNSw|EDq}jv;}L7Iq6x1A9LzC-ByL=X(9OFIuA35uPXSB~+YJ6VoEc1asb^n#V=q z$@NsnL{Vbil8DJqN!d6dRM>XDE@xCSpLK@)#d;dxk3n2;EC1}+HW^u7yP~k~eJ!IYnBr6$4OpK5^bA}e23dM^IrVs>C-kFLFGVQPzyQxpxeGmg zL5l{9ezOYu6sO4lcyXfY%{8Mha>JXqnyL?0 z%vKvtn<;rUo(D`V)DF2u0QwEz-g#0AJld*DQ*_}V2C@QAm#H1uTx^H98T~yrvwHbB5`+A z|5?!MgOxfg^ymh(fyv;{kyso1TBuv<#Q3A?_e-#l`*Ty z0~UFac6g(93vhZvZTgvRXoHF$GeRJgmeN2{62kJ@#SeIR_$2tvm@t2%wwpCh*s5R}sEtODU0pvq4*#1b24^JTYMc+V}sV8e(7T zU(}(Gkdo}|bvi8*S{s=iAgV0Cgc(Q|(&)wg>$ie(BPt6I)iYQ~d;G)Dv_Kv+wHg6LIXx^cpe#5+(yeJ(UAx=ZMWCW_f@#=OosVroe# zvta=wheXwb6H^0;bC58xVhdQ?gM-;$ZYr%aIPrd7(3RKJQUK>gUV&sybCxTEH@WoJY*>&kn8pR6pRft*l}s zPy}};?g+So^x**$^EL2%f4{!d#n9S-OryI%)T+WP90GX*dV`xDtgRsV8N|_r+Ft=J z0Vf_sKa`76V=~*Kd>ZH+-58C0;)R5qK>d1(bF;IL=e(W5thO^78t4N#iOVw@8qVlE zF*7j=mL2zyp*?hJ;jZp)&f8cd1|$hJdRIg-VQ3(f(dEaFK6zjfjEl`6r7ZEyS0>qeUS z-u%{^k@PId^Tl(^pI@=~saEz=5S6%=%3nRoH905R>wGZ?Sx%XUTejpo zN1Wnh1*o^uB}$u*025{&5AsFOjey}84sG^yZ*mHTqeUJq&kyHy+mr&sZ*{DgA071p z)tKVYSAJ??~8%b>*@Ml~S5?GwQ)!dwm-&p6>3~f;*kBtWwBl`&2?K=q8To z^C!k1X84=Req4Gm7=OCu$xoYA;GqUWHV`4}R8GnumtL_#M?R*P1d-SD@&Lw+GX`gm z(mg?*((}9iXl##aDTG@0mn)t!S7j*M_6ndaIP5}3L7$?aJ$-ubeXvv37l~mffc=}$ zlzy+3-tDa8PHmH>Yy}54MB?|4zJ+iUeD~`3&k@K{q@lQ051+XI5VbS~!BNn?>wH!5 zfc=$Oz(Ej0wNrn!KVc_;n_EtS+9#JxQ)@F_WYc6DD&}2oI+AB`s)W<8O`V-25Aart*NZGJ3M}=wM3Se|2mvmq2Iz0HtI#{MdWY%(N{N+ z1p&2)Y%>Ht8-a^LcqR@tj7|CXKBBy%&N?jHM8_5lQV5^NHt9?p3YLL1{7a!{H#H%{ z0E!;mK4Y&-;Vt3#jtkbpqvA(GHB0W`Ji@R>kbn}mT;FwjgQONSZFp^?dON=1s|%8R zOu`Qwt6`U>bcQ5a8C#6@Z&kf=&9;g0@Ub0AX`GknJ&+(EV$^VgUSlLt3K$lr8|!&W&MT4D zwSja3dev0p1zw{iYd$fz1_luiyAUxLl~iL44eScT4qf*mL~>=}lqP6cRFn^?*?Ucj zMKR_pJbTks!dVv)AG+=mZk4-*Rk1QICK9F+_qn}Ypl9m*%3c| zP1qi6-|(aFTa4LWji=(gg3gZ>wSTa&%Tk%uVnGmHU^j0wJgvfnoyX>lS3q=dYWwPi zpg&yosh3tc$2=09WUDjmsYDpL3a$*#yf$ED;Nip3J|zjlkDvGueH@~XX%gX@w#!mP zJ_KwgBk3gHR*n3CX&Sx$m zvPTj0T>dytFPj6a4R5@PhItGahwG}& z8LcW|!mU<&#$;fgIxn-m++VYY(Yclt!LXO-6Any`j-sN@*+TyJ@#c%CsR?GTnpJ`) z7|X|k6<7#%$ekFtmf9#_0?PJ9=Bqm}U?5rYLmbHN$m!M$J4_sEeAUA|CN?^klP5}w z-O5`$?AnMR-|>y7S{|<&&Rj*eX<$3%^oA19nb;P@sjBNmNVok6aB$#_O#BpXrJcF< zf@QP~jCW=D`4hW^8Kx!uu1hdy?+cvqziv=oOX~@AS;l(K5+mEgU{AX<#Ub( zza}u6!kyXm>T-q6Cz#?}pRNle{F&qV7~<*jqRt*CBsX&=@2I`PF$(wDENiPoh@tYU@Qc2`WwlvQ}#IyCF~IUUjBx@UDt((EK^a& z)?sRq88yjZ@~^=A%I_q9RK2jAas264`leTs*w6?}0~e{h7@K;6Go*wQ z>@h$=l^H{}yFW_PKX0gx-t~~&REdg5Pf_a9({Ir|Udp5*J@0Ore%CSj1MX4?N@vBz z82vl(nYl;;Y(fPxW!k!PeyzAiB$|@;8!p9e0qx+TQg#Dj4iJ9|Hrd$J?Ib7@qDI$;VqurnyU-TdfDrt;r+OS2h-du^4q#KW*y5 z$1$r?a|KhNrrKSY&PvsX0{yJtV`xB>-kWCUj~NhP=Ooome3ZhA6p6gMaLwXV#m&v^ z!X>7|LNx!&1H;ze_I#yw;jnMtC-$!$hVjsCe=l*D^w{pT{fIFO`u;VTXGL0PlLb|_ zLelc{4I>9neAovIV*i;hlX{3RJ7=L%s$u&`3}SsVAq0Kh#ZmY7v(jGRJ`Lm&L>NM8YSiF; z^u7ID9!lV?;i98&N4$pK6We^x94nD>-_CB4vz-~u%*+W_9CBS?(r2Cnax*8#?`{YB z2Liq29^r;j^8HzmnMdM=$u}UVZsLh)cWqU`&JNx0Y7+*y_vupI9Dkbg2{3C#wE+#) zKDraEA`Ver>{s%kxIW#r^nk=5^O z)IXLXOrS=jwT>7%zVZK8$ISc4iiG zEc)jWt*$N`(N#v$Tny;-gWXNs*w+4DSORJL2#(S8gKf{&*8zt49Vo4}kb3!361$Ui z`(T}sAh5boi+mwgpRxFoGKV?8_QEoB&`L z`Tw(1n14|)z<;q*eb*Ape?ij!o1GqiL-fa)y&a?o76l_w6c&8f5rs&K;$Zf}#=tY> z(b3V7Q%ZFk*5p%qYLp>|;7VcM2;DdhR!yv-eDF)N60`4q$vykl(N17gGt?>YnG~{7WB}fp31xuf%S;M(_LpeAG zZXH~NxONo52?`HBV`wr!od3^|Khm4<8*qtf`1ILy*feXz&y9gQ@RrG`#&&tP#|O%E zN&-qgKIe{Qq$MN>`oC4E6LYE5BtVmI@p{yH219c(?eDH6z7oxvF#y+Veh=w9=IB(m9V#^e>x^>`uL=|BSi$k-rbdN z==FHPf>tGacuMtka{Y&ChOxPDagc8DKhTtXF(Kr(Epv6tDjSH;Dy313foI;`hn;P? zBAESorb!s^s9z?XVEAL4PoiaOMjdxCLR+iJ6HZHzXWw->IUCq?4*9iO-JR*`OglnR|@9KB4Ii2>TvU51Fe1{@7gJz`P z9&bBs=``DRhdSH!hIY7MwysT^b6;RdlTK_2n_+sc(WB3s33tctbD3+qGvqasNRjY6 zV6DV+`NY5FEI=a`Pmj8c#Gk=i$GOrty_=WFe;9e`Bb!`}^m#1#A&ukmEuflVRnBfE zjBEKOrkH&nnjdCYjvyvIE46tj}vwZ)ss$ zKXwZ#%B4|w`<}_eyc2hoIdzB(0fS*ddy7_#9;56|C7~FdMh;ysY>l&8a2l#pc)DzS zb8818%}P*9QKb-NVQR%}nl>ckN6ip$(V)wGk)jBIRsFe zl0f$nJ*l72M>0@Sv9J&v_=CKLxsm*irCSD$dvxXYqLgL&A!Hu3aEqB$?s2p_+Z{YI zbAMqs6NV)6bQlm}?2lA&xAQyb_v2d$yi)>cap|8wO11t7>nKXY&pel<@xcl{~EbwLTAbxN8dJ!!Q=nRd<4w^<^n5*<_c|Zj+(yV>%J^jUM zkg!tii;)d+B<&L&n%))GuSB5vsSfV(Uv#Xk2yY=0!t@dbI}cG{9F~aVIBeMU+=P7N z=z98N0|tiO;Kxarzl<}pR9w{u;AO$>s^(W_*?)FA11;j;IDn(L$Rjqo$Q zdT)>2XCbZVcYB{-Zq;p%DpSkK>L!~ilLX-J=1=P}s$WDeQI_nwKF5;h8UMy0o5QBO z4vqJ!C9j}IQ2PGo56MjD(^|>)O*W43RWa#+nG>61Hr7Ok8kIC;Tl;W`irMmHak_V0 zkg;|-^#c(#g)qvqzG2n-9YT`X*|{e`Wd3@Ixk)8S-8lyw;$R`tEP%q#^o-gkAbm@~ zdULbj85Yz_%7R4wbQYAA&2tp6EUZ}8T584jXiaS*aCE(D$NEFSQzPloj-68B#N1xV zxo_y;m$cl{mS9&>Ygl=|D!~`!^PkWqxH7(gG6~5T7CAvop9f9NHrY={jNcF6l%15S zGk$b-vk8s=!B*B#Lo3lJR{H4i*SK`sVM(W$Wpu3Tc3MDBMe`<9!+ed>W>Ak0zNZa# z=^ruOZ72r@bEs10RqUU&T#gRHJ{B?<;;S0GL=I1o{l(*WuW_vY6;q1r zI!BKl>}QRWn3B?fk2odnaoQ4iER1W6<}w^lV#mMhdtGC1bL^hJZnvk1kPcI+=ahK# zX3zkfN0yb`f1-q+wjc|aLOG}KxUtTC+7ahkC7;Nuo2&_e*p3}&nIX%!TV}VS6Od)S zmK3DSTroDDG<`jB)bOp&Xr@8nUGR51G9qZNI2Pn57gh}qe^Z5rXVf%YbL|NT!QCQg zSHGPy?U7TW5Y0u*{qTN!r(jx1vJb7-$M2M1o+RiQzem8YI-{5+^ro+B&}_Y>9&6v` zmb2sAqS(k+GH^jPR^zLx%QZ9p3gglyNr>LB5Bl$)EOU%Ije}9XBHy}X!ri1kqM)BY zQf}oJgG?}fs22FkJ$;Z-%)HLpS5VJoGYL^MXf@~UTYlHoa7~1C@*;yC5($gyFTw0w zZfr$+B~SbPQB`3GKw4s8MuOtaJRvpFGaXOo*-Tf&`X`pJYKv-Wp9kaMXvoRhWP9gy z%`=qEqpBML+h?6^Nn6Forhy}dGAN&DX@&@U5=_fQC=k0>>&U9GM{B?R{mu5G7_t~r z55iU23o<0Pv|2sm{v&bT+RgkBoc`eGcN}bU!v9WHepX8d8(m=H_SmON;IuixJ+rv_ za2}l|Zm}K{Zm?rL34Z;{s=5wBj8PcMSNnWF>k&nxTey82RclS4UjnIx4 z-cO0!0|CY8d>cHojYqB~&0P+0Kc_ecXj@g(F27`-D@U&eCNwSgo!l(3wI@IDKh)mn z9ST{(U7>hhC&8#GHf2fts>RE1_WmwA>%TvikT&)w&&z3G#3CCRG+Lr()m4N}@_9~? zy2CQB6*WDVN3X-zpu`HbrYc29ukmW`_k8n(lIxNo!G{3O=;7h@u!G!`qZa<|>{8Qf z>vU>5m(zgrppc6Ca61XMl=z2gG^TF<-;rqq}75={tlXt-p-B24a)%qE=qXmc&0ZY=?$0lo&I2$U@1RtUKY?Mk_9ICjo!F6jvB*nON#8eK z`fCZi7Y!#ro^hJ`UU~ZN?C07?)Cyd6U+K^O%3%*G9iJT)67sC@HYW2YY$@11kJAVn zcGqmnUqY)#Mvlr*EN%Pl`?nbx=VNH@@<0F5qZJy>iB4WycnG^;!A?9{?{6W+c{xss z2>6EEAGP-c3eH+pa+Y^?admT={-o+1Aa~C;im>g_^vP*o{2QxTj#@x~LLAyt&S#Q@ zFJt$q`=c83-Oc3Q^$lOx)J4flFI3$66k0&;AGI)|Z_RMD=;M@989B3LR;(Kjd`a?wMJ0^|6=cZ-H8k{4h zIc`ixc-7nALVIsi((SmTnN1A`TO%8>k1xbG%=Gu6C? zfwW&lLlIe>r@_!zcU!@mtzM2t z0?Ob19~ixY3icH=@r~1cx?UVV6LkLWH_S98_1U@Vp~$)Q;LaBfEXq7<-cBi0iOdnq zHPD!$A(y;Od8rlNlQYu1`FW3}b;Q?UF^^rKY|+euX}oWQ$#-Kwi#tsla?p2^b6%Fb zi{I>WOjhV>U3(1M4Td4)nQZ)(jbI-WimtnNO~|?bQU?C9_>pD^q;eG5ue&# ztzL-5C-OiII^U4-K(T+?e{JFEAVY(lf!r96%ZJki{EBQIpVKbFvBrdi%}@=iZ~Di`c6%cVpl$ z;8E;6$5I2La?uvZ3l1(k2+Us%@b0K#UTz97k!Io+kZ*U(Oa4{~O}>_riEQ_+ygZsH zM-pc4?f?8kHE{7PIREE^pzG6MTu-i9>V>rWZt3;1-`V7ofS0iFy+L@m70C21NZx$Y z;eo{G&T!#&3R-QV{IjD)*V%fQzx?xLWk|Pp1G4PUJqRcQzG*t9ydy>6bV?Rj}>oUi{RrnTn!t1qYWSr~STHT*~MK&|P| znaF-|7(?knsJjh>zF= zpq`=xxzDn}iB048$xo4C#idSW@_a-N9uc$j>-DQEd8yqXwOsSp-$tB}riz!1o`fjr zrVhhC>G(`J*O`MQdF7OWcg&k!eUW-6)h7Psh`F=Cw4pvWqh*46;zXScEFt%jF4yPf z`oA+)tlJZZ)KTCwcAS*MImI0NSl77nN<4Xhpw0PwL-r;KJ3@7VMH~u-*&TqC_KWQm zt46x@Jw}Z`C)2#Jk$Dt#{#S(@;2h$d z1ffJ;cPDD(j9Ik%H$Q1W?C4t-uTv#&^2GB6`)dmQ+u!N5>71K_O}R9d?d$iZyn?wa z@e=5@0_T|=Ylnwj?#0VVSR5PT6GbL{mY11t(gADtU|H~pBaHKR@`;08ndNUd3r{yZ zRk`4AR3(i7n`-ds>MD`lAImJ5OcrtS?&sO9V?UZ+r5j8n1;!Xp-)lZjf_bZ*n>?{Q zsrND&=S+Rvo{PT(S`;ajJ}uU7Q4x@tC(TjA+YVJNm!fBFBKE>Osm@r%1No6bw@5(4 zJT>h6yC4iC0r=aFiD#HP?w5s+qBrWUu-)0HFHd?rMOPq!!xDTCs`1IYPqyz!4>76M zZi(CmVC))h3QP{>1&^I8aI6E!D7-%@bJYn4ROCE)pQ%2K4Cj2|CSy8i!z zYp(a*sgRiUx*9B7ra_vkzC_O_qE7ulQw(xQY|CDY+dU6>qphYuK9Si2?Ou2rOwujVJ9AeBH-^VV-^TninlU) zvLo_-T0t}IFQ=Gsz5rO^CFleMc+zZA3z|_umKu=eP-QI_F$KaG?Don~2zZWvY_tZG zco7pd!X^biD=Z^59zDuzT@^=kbQP35{8U~s%DpcSKQQqR-Df3c2x7%U`)A6-Lf-`} zQUF5?JFg+doiNF{!yKdkOatpP`7%f29&||az6C7Wdq-D4!CGO>B@Xa3#;$ls%=;O4WJzU?SC7)mQ9biVuEW>^} zD_QG&Cb*eh+3q6{Q2^`>%Z$iI4&-rq41k(QjxRTZrOM5S^H}awBxTduMex7@}z+S9*n~r!yK`+~s$t zE?NFa)2lixkmHW!K|qn()$03gXU&KJpo5h;~gA9@P z0MnajMNW{ywuORMA<+G#1ZMd*ng9)c@bVbh?UEHWlFzjzj|9kVXgQF;lYa3GRgxf4 z%~@#^|K3m;L^3RN5o8try##d7o@1K}m@V6Y=k?G(|?A9>Q@} zufa=?7E%v~Ii-lD3Dx{s$IpM`&S-#`*y_)jIFge8HX{zAVw2{G1{H_|`@JcMb=Xd) zD!EPpEAW6P-pN<&gO>rjN;Y-68D7_&HsPy5fc?43fc7ZRKSj&yfWuIk-cye=Ffwi3 z`<}lvHrDCS7(XFTO3}9#e%rr}Co@jG%27UBaBaplC{Uf3*EoQHbFb?O`ipnXdX ziNbNy?f*ts@`U!lDeEm0gbl*GP9bO_g59_Y1BsSv-3?1KO=3ii+1rkHSk@i)Nq4aP z`&d^jyVV*NUX%KtBhMGn?#&bn5t%jD3^Fom|GvtP4E+TOZFQ>L`@E!A*>WIxaVf*` zA?HomDnZp~jZN&udq3lFe;2>RDJ{D~YeImB0;c;WF}Y^ZvM{k&4^YYX&CeSuy_|07 zySt_pL*)Q7(I}A}78IaB@tD`*HBeH32xqZTqpp8Mn*sORA!bqPm!2d zpsR9-dnkEL;s@s?N-1mrr9bPLRh9d#LKR3!?O>=zbBGGKGx*D&Y{3|{>2)N1rCB>W zA|P-0rt5Gd!Diqm4H5Q-A7(5O#4=SXH>x> z%xhGN?-&Ji1zGH|mtEm$;pAzS%X~)$K#;3fS|x>_fU$B8gs-qSJt!kD9MNChiSFNi z%9`(0&bmM1=MPF(76u&8}B zpL1b%cyV(4ntsd}o8o&Rt-Uu7VK#nf=?l#zlHZm95^pjLVT^~p`v~4xVaA1XZRM`u zgQb}9^>HKs2$BxnqWN3w?`LXiFL*Kyn*(%@?{=)WT;x_g4ZX1GOhvxv=b0M*xfzu; zSL4M8o-6+(eszh(#B+{%?znu(%GZuAT}sf?AvcT7n#Rr9igbtj#TQkf=jXtaP)^A) zd`%I;?d9{KRP2xmwy!lZ!0}JAyUUhz)_i^#Ej1;FJ}6&7Fg-1Xr3gpFQC#=yjpnPa zihS@v6Qec0&bbc8?qfP^UYTut72^JttDBpYkbcUIl8BP#-kO1TzM9)UgGcCjH}g*& zojS= zT8G>b53FQsGP#c>FaKoyaCL^rTw$X%1$MySn?T3cKoZAyS+&AyBzm8BJt1&g(Uop;>3HW!bRyOdB z4$~wk1=8WVZl4}-%;!H%9OgMXG71^*v_w?nts(AlzC)1~gGV)v4PWuHk?q z{DY?EdbFj56cTXzI@r5Xn;pRRmVh-bpK{TG3l!!VQRPtw5L|5+hxn(8{7MM}*aD$O za74xhj_`902R%HqK~-xwyQjMHrDxjG+omOgvdJ(=#mFKKJ8cLn?tJ z%UgqlmnxY)H%D2A)A_FuZ0gP+hXGJ`I(s87ko~BQbo^N|euQ%ZFAJTT9@vZ7e~dTY z9>QZLD6|fcPX0$}WXMg*?6dxi(08b0Zy=JJrX_9W>5;H6@+n z7O7UH>ok%e6{&qabHXMH?az4+->vQ@{-f{0+#j7(Uk^r9RoQu`9R}Mw;3Jg-wYPj? z9zafgFX9`jr4>kJ7{utuOk1p=@rECoMVSQxMnwWzE!v8c9^&bpUv7R23i^;y2=4>v zuTD8*Fa|%>&PIM4sbgZ?+mBxyCdl;5XM~+#@O=0wYT(*SfrJEI^$M!QgM|*MhmD=_ z9*;uCaNpAbOcdP+4s&r~7)LJ;4>AY?f_k-^_J_L|$v;9L{uclMLI1vu8GtV!(Qqfe z2ttswQ>U!f$yBUmPL5R#c+~Fg<6?d-0OpR3n&lS{{&{wuI<#+B{k;tv7RDt2TDK0> zAT(&OlYpLl@!0%ypW58X{0jnm)3^CEK(l6)s&q|RfSmfo`K_E08;rxxrwmb&0)UcJ+J{;1ZQ1fRTP?cP zb3Pjc0IENQHh0{SUI9QjF=2|vn03rZp&Q$8|yR$<8a6LHtV$I!ig4F8V+)!w&dDF(~)(Hb#BLRf} zbY)+X%n#N*#VkJ)(OH-V0pM!$|1CH@^V{F28G=Af^>%Td{s;go6%|xeyce+{&;qi> z2{?Op>OKEXC*XWBxnt!#!1&|FDVf7>@5>(_ENAOiqGk^fK@jSAJN@w8)VoshYXkwz zzh>FFwL1O5pDX9uo^ABG0Er6#LVeWpN7($}AHmat5du1y2C^P(=wUZ&He64Zaf=OOdwZP*j1XG9IK2V@TrLTB(XIhgXwV>Q)OM&#Z{oly zSYH0ROE42=#@c3pId(pZf(f1$3=4b0mFAG1*#z)l?1)?i5(b8hEHnOCNUK5D0)Hj_}?O zfV1u9o6{!%_}oR}Z*%)@Gy=>Zdi9YkRnx@Xcu`%wR?LFCW&N#|SU3n5jM+zhJ$sZ| zzf#nlTAcps)kSLh@m5k-<)Up0<^+0e2Bcd6kOV}@urn3docboICmpD$_*k@o0s2`` z7qtAz~`F=otQj0`AjlYx|!dOf`ZYOP;Q>D{(Y55p|@{MnE5yI?}ya!ThF*f zi&=3ufDIP_X0Qxu|0RMBqN((ioPukmo6%!(D0~@+5Jm5NWCSXLVB*AI^!PK{rcp*6 zWx(un0uqRh6tvDP3)OwH2>=p0%wQ1XwMCtyyUU)z1#jMbLXDdPn-|9yMQ=zqlSG8Te-^3xFSmp| z^8`vtZj1Z-!hZJ)#5Jl@r^r!p@xU}ec<&U1tKU z6EN#?mVkygKE*-f1c3##4+p(efIWuSd0^N=$Gb6L2u?-Qe8Vfy!3SquZG-^tr4fj^ z7wmjAoJ^ng(SSg1+Vnu{^cGFv+4&-!5{U@BcC%V=#xX$+ue|)Vqy+%2;|dF3mgjFp zh!`O@e0XZh!x0FhN3WKilavrg^%bBQ__G0PAY!k91*5QMx}Zm_MU~C*hCX%T*&M{Z z4?Hlu-rXx9fF^MJ?U9{++^}q!W#>~t$oov+K)M_qaDc`5*~0u5tgb<(S75jHZPbqs z%Z0})1m3z;4eG;KZ5+JY#GM1+6acxox9ZN(b`23tFdboGJ`X{Rsem)FxOf}sOJO;w z9at>bPT=PjxKN=Wuuu%~uv$(G-b_16d0!=ctCKhpfs@e{eq(aITuPV zAk%z@40%lY*F7s?t>qC87=}?)RYh+SY6m_6hFN%>90W}FUp&qRoFdrv229x{l!xs~ zND~|14C3&b$!iw4@5(7olh+37lwnes@xh&zlP?-xzDWWA=c&?Satm_hB)vAF2~cJ~ z&={t5>kScqXL`}dk(qh+2>3~pe%G@U_ukv;r{KVP!TW+eGRsn326R(*$8F1Z9^nsc4q?6Fh z@q@LEA6)<}R8P)6MHLs4sMAOsfJ>7S04-WvA^(q@B#bsOaG)10;x_8<+;!J;bc=e2 zAmG6JPT)@w7P?NL7p8670|FzwLd*}77?{5KypEC8yoX%ycsC5EXuEdS35!?QgwoP+ zIykQzJ9NSX%Z?{U0pA3fdYtMN71p(R=gChcOc!CRk!Yu_H&b6d5Tilp>x3JdKt+|Ul|NZv6Sp-2R(QcbMg~7)HW(#hTQ-zxd zbSN+{uSkbl>dXR^r_-YzXXVHX5FEPG4GJ zBY4m}IFUiTCx$u2P5LdL0r*_H>(X%tX5Vc$Ya=3na8APX^@HKvK_*|5Cj02;GkRce zwLVItkUyTDqYl}FMjnaV|KS4QnV7F5SHeqek3DhHN$bRybEDRBUCe`pg8p7Gp_g=_ zKrcYgp63Nmewc|98kHR{E}(`Trsd}yceI$cV@E3!t!dN!65cp~Qy0e?j-swztL}~MAsFoTx*cdEG}Gku2qy%}PCHpJpKHDcW=Htc1v@@fsI2^4HE3u##9p{T z7R;#yym*6p=e7$}Wh=}1OAmrXO`muK0Bg^)^}18EIV>2te#jvY%Dq~ej}2rP58_2<*lBP}2R{*wCtg9GY+Vd1F>uT5bgReqR{J>|Z?sqsDRDKtIf z`|+Cjjkb$cFkrwf+F6*JYibuXo)>fy5H7D*gzf*3Kpa0gGN|&@;Qao0ncsrtaLUPP zr3+Ks`UBK8a6eU5zsQO9f^N;-tl2OzaV%T9CJ=sW(xk7v=Quqhp`zlGAhURVUsP~% zja|0gT-`Zc2K!SuBoTn^wqP(U!(5+EBHCb0H_pr3Ic`4?heo$*#Nni(>$>hm@ zijUw1xwp{d^5pEN7z{w4~(DK%Id1OdSuf)2%jJw6cz5+`POz+2BQTa#o0dD3gf8*92<_g(eKBaevoG)*$k_Lk2p*RL%sbj?W@t)g09^QOo5zEP5PF4rbOZtj*k zgO5%_$uO_+0TdRVlav4en47$pbn)ZGGMO|fdwc{Y6po|NFDfFNJ{71d_ z-h1k`*Iv_j@x>R_^Upu8o_p>&_3X3H>YrbE#wVK-g!r@T)9$a1}>01i3238 zMyI7nK~PwDp7t|%wFV}_Vvy`QQaEI35X)+PP2^?Ba*036s=FZX#N>o{EeyDZ6HfSA z!rmi9lZ$9ja+;t{MGFuBD+Kx`Dk{3^3_0TFoDLS_|1A&oR?lqKbZvS()ePLo$%#Cn zJ9OwEW5Z{uB}gAVT)(mf$9p--X%{SGDAAYDl`sgF|^Upu4HEY%c z@y$2i=- zg_jr5F0mcRBpuY z`TLJQ{-}ir??-@aD;ZRgLSSbR0%qEB-GU_ma%4?ACap!p`|QZ~=-E@LqmQ<-Jx!Ak zuY|$GiN6WoT&(kF9M6=*x~wNcfcFUk0^l;exTyb4o0{ey;f^puVqR~!WIE=KnI z0R;sG(u}f~di2pp^=WNqdQNcE{ja|I%9Y6zAtMMd5%428uLI|4GB&u^fHB!8bPvkJ zA;JDcGr&|J)APt9t=bOLYar7Cfw1AQVK2&oxs~&oelDQoI46@#od{vCyerTb+^g4R zIwVtEOg}ewPjl=XCe#`?ZY-*0H>v+$tloeBea-B4#^-ma^Qolu z>(^^xf_8C|?AY+NZKSmmuh%QlwQbpwR&i)hFK0vm*nycqJqJ-Md52(>k5>zyJMDz4g{x;#4{sROzyzIf%nKE8aD702jQK9 z9RhrpHF*PJ0w6EX8tq+P{ zF{t}@-g#%3jSq~CAI+vuHU0VLpE_FL^a;?@&+9yac)@ixZro8cmw#vc^p6Ml3~1tl zsX%y}C|A0p34Hc($9*XLWDRYx2?1swo|s`Ck4a$ypkc#1g9SDD`A5ji#r6i0b|9f=A9OkGLy&~4uK*~c2yPUR~Oy;S)P zOkaxaw;%2%1YQ$tnQ4Z1lB)nHD|<@*Unof!{dJa`omtw;ika6J7thbKQ_8aWO;ut@ zrEaROyY9N6AhWCdpMLsj##H`TgjTZ09(&lXo1T30vTS|pA527neUC8c-P_U*5C$-B zD$E6|`N$2zU)C^J8^|DJD35345CBWHz0|TL!$2B}^W?;^6B7Z@Px3U)>zPTHO&iq8 zUAWSAKbAN4Mw#N_WPXx)KL7mltYCf!f52>+VcxuX$Zn*XFxK2fAL#+Y06rii7GYo_ z3bhdgXah$a@w8I_fFA_(} z7PR&dpJ9H4+Hb%8<~6^&5?To+i@Ar!jd%2XZGD6U0evKCYfmPkF%uH;8Sq;AZB|!D zj5K>yuvHcafI9?MqeSP)3GTYhU(nfqzlw11t+3)s|vKp@w^WFk_=98a+lf@0F7-=x6F^l1zVf$s&=GQs>& z1OQI7GI4$!9Kk?_iT2&snmY$51FfvAxd&XjfRoJW@rU{CIsL|g`Th6bd(GEzlhf2H zFMrF|07v-}0I2H+qF`t^%Qy{FL#>BDI!ih-7f3xKNx<&ye4PzwNH zHw93k?T#wC%9NEo>)E2`=2nI+9*IjN^*HVR&p!LiYo1PsC^M#3!Q5A2;hDYws3S0f zfSv{v*Bx|_HB-Yr;@l|&`t+IU6apIr$D~^Tgf#@Q3xLB8J47=-8}W}n{s@`_{7VkK zaa7SSue|&}8DC^8hpzZ|QRo}kwUPJ|M+6R;j4}IB@0TZA~(j~Xo%x=`?Uw-*TpQI)wm|`09 zzM`TT89$w>tAEoo2+TEXBNZSIKm2e6^K;E>*RJ)Jo0Fu|WtW6911m((zUujg_E9^{ zfaWGR4{3tx)CTx@z<|Y0A;4UM@^lJ-T?A{aFGJzZ;Q5fM#J&j3SfohaY}O*>_N|#J%y^R5O;9 zJ?Z%-_K_N3A|MDzLALt{5CV)WbC?5P30kL10F((X2sy}9t-8tumHsq0}fH1o{cI@ao0_!=<@EuB)X~{>n zAvpzo`hDd%UjWz72KUIr3(b7R}WiqPyZc}S;yaa!pU4KV2?j{A)Rpw`f1Z{6w=kGk< zus+hK5#Z>RG#g!mfF{5wbB7Q}OFw~q1ix5cP$96oncxF(65Vc5fl+hq8@@%0^tZCI zy8BEH`(9^xFTiy9=bwL4!hm*KwrrU&Vc5JlwPM8z{Wl^d_WX#QB`YKqU4`%iklAPn z+HSi3-s_F(BmE$9Ku06;2>SP5=+rWIOtk>u1AlU=vZ(t`B8MJ(oAOoTpt5j`oKESV z2Wa>FA1lFCXR;EGz;xyL?<0ld_VsDhsF7CF4c&kL{n8vGKlt~{F1xJO--&=R0>B!% z_Oa)i(?@22X+Y0{o;`n*12(L7;737zs)fKeP604)-n@tni6ambfMlD4B?&t8`fg&Q zzLYi_)r$Mi3Ujtm>1XfkgJzO0uRmtSRuas$uh0Jb@2`IU{r9lf^_0vH^Y>xm;&uK# zQKE?`os7(9`qhD(9<2EY-|ETBw*7aDfJDBO5&6eq8+;tOj1D59hAKhK1MJ+;>$eb_ z_(q)C>l&y@u+(+7h2*{0&}aUHb4A43BBt zxRZT8nmH;p+`$1HK)RIVQGT+I} z244Vd+OWpkJt%uDn2&OAsssSD&V!qcgcmpZ_19mu0Kfwu$rzJFO?^9sX21sdyilbS z0>C-94`EP~*XnN!$6(j_NG#QxJ=>YJzlI9Zaq(UugJ(U}g2LSWgy8~G$)9uB}%TtpYjOUXX1m^R(9(&Ob zskwy8(n-Dm@R6;kto&5?E!acy6+uaD1OWX3!UVv5_uc2ZA&IxcfCtLFrEtcet@RY? zY5#soZm_s`cJQ+|7m%c8Vqd?!NnO*Vj*wKGOErVJg*@d;#FY4Vc;`CCg-A zSjl37%LBZeRCtiY0=kpx55tjl1Z7Gt^xb#gX(k*nV1NsAAQTqY`sS>;^P$gOxNu?6 z_Q&&2Bq9^7y%92Q0)S~gghic1nAHBdOD?&j*4N+*03Xr7eqdm4V1Lj~Y`^d z-!z+T&pr2yx+!5ocL6}(^J^q_pM2iBHPZWy;AfKg$B7R-idptz7p_>3e`*F+k zBPH1Wx|uU)*7_QJ0pKGR%FEvrjeSeAN4^szJ#mOR=6A&-00;-3d+xa|%t1lX#d1M+ zB^3>@n18K&#{R+295rf`W_o+#Po_Hjj*$lNZUTVZ05<{POd3kH0I00|%<~QBBf9|4 z6c^LXN1Foc1s5eN0HC>MvvujxMJK{UBYXsI%+a6$t-9w)X5dE&3xVqD^`g=+G1u~N zQh|5%)mH}#e@x;}rUU?K$L{yoKt1%(LlLh(`|PvRDgc@{pOo;L`l$b)RNgipgDyhb zCo2Gk2!7*(TFWQnX^%!ubvJ}P1@k3ty-Y4Fk<`_VBFR3G_nsrLyqh$~w8#C?1itv< zi&`^PycA0SFm)Kh7h(Jn&fWm2762_<-sTH{41@KqEi!SP)XZ4>MCS+05j0Ll0PH0o zuBbil#NDG2!XX-SpaC>!us}|%_-EZC#d34+miM7XTebInWc&&>{zd?BJ+-Z}ON3$x z0AP2N9xZKOoqtcg58w~a8Ddj`Wsd$N$!M=iHR|h0qp0W=y|*)Q&5qId%>GE2iUi~s z*SLX6lkMEOa}?%)aTZE6;U;ki);v6+!t`sxTxanA^xqX_*o~Ov6ou3cvDQscje%CME1^u zQbWHYu%|){7s;6O1F!K4?0ft5m}|XO?%@HArcM7(w1_w4{@&7jgJ3iF@3v0G#e7b% zuBUA>0st?f-f*@?TMGmJ<+RVuCB#M>WGOwOXs>FgVQ7sL@ir zkF339m?bdoyS6a*0W1Ll-=MYR(asO8!`kFreJ@7 zzJ2?upMLr&>IQY5P~o6|KaCCJymMY>N^&BEdcC&2=nuY$OEq5 z)qsZSi;7;86YmW<&E65gcdM9^mN{u1C>$DIGiNs7CcP#Ih33tl4_ia#bu?(8yGgAT z^h<>KO&VcvxL!1aURS`n!BYl|8K-R> zV7wTb!98Ls1Z!!&O-29^N7Nrc6OL#^T)sm37)NIUoP_Z_STu!Dbs^Pu#`#AlV)_tx zK~4csDRZ5+kmiT+Y5^Btd~wp>O*bI(J*0Y8RD9?;W5>hroa`kfAB)HRX>njZEZ2Ug z_5p=F>c+11nvv=PU}ykyN|t){ z>J|K)yu7Z$SYLR~=+SNV*TqNie*s&4QQ&m427WCQ>d{E%4W?3o{li8b83xl!x&y?gi8W`_{~j497#?B7mI8M5C? zSy`E$M(i9C5)HK>dx$kZ|47e6fMj(Ng{Z1pr)_t_ZlM@=HiXo&88A3NFk-V~`T$OI z4oG-}RNlP=q2ar6;%5QFeE>g`3QQ>fYr)WDnV&R5LJ0axVnqUI)Dc@HW@=8X#ko@u ztY*^oKa6iieZ{IrNH>vpV80ifa%e=P0jE;{aNn34d`82DjdZa`w7742lWNxM*fTvI zaMm?#N(dKqp3RF&?=@p(8_WrgT@J1ZtQ9TeY4A$6D4}i==m^yX8+ybGs{P&-kmPft z;2Oam0zCa-Pk|KmZ7Kf*n+7ME+?T?B-*87RZJJ^W;)i7-0p_;~LZ38?WO6hL>uHiDcG ztJ;2yhosw2gYCB4u7OOuX;4~P+CWlnqrG$|!LM8plW<~DYykl1?T4!GTtIAp7R!1a zsl|^x@<{OXPsMPJ+7RQv`uFc2CIB`Q?m#HmLv*PW00QNALo?;^br&W0Ao_3pK@v>KtBM5 zl5o>NHhBZt+|Jk0CFu5eHgkNTu`>krkRXdm`7HSY)@x{`ZHj+L0!mlE|9FY$gT3*I zT>umn6{ST0;PkYAM#F~ti3+|dZ6*MAeOcMJTD`YU_f0+aZtQh{=0j>Ow)&+4CTNEa zggEf0fMguT?|mV-UN9xqqrDQLj)DsWj|h-HPYA{dupPFPw$}PZqTZU<>#<>|yH{Ox zRn$#Oyu0Sbz|LnOk)E9VNY8e`zvEfI_uhMT{6EuWztITi;{_L75OyCWB_+BcD&fF% z2!K28AkM?>nJ39GXmHV)E4p0p9mec4{1; zE!TU3rv<3*V+04LjC*TAaP(jSoW?F?A2WQZOJq0b=< zd9zl@TeYfPHy{(IFDY3ir~N}>!tl&CMejXYD2EU>JO4jLKneOz0uH3`=U4+UkslCT zAvjFXU$Cv9RvmiNL6rH%b3;@S{UN~HxInPCU=@F(K}fXz0GyQZeLl<{X(OF=)>*-{ z-<2y@>Pf`eLPS8Alw_`z5Fn1gcJ11!t+(D<284haf_W1HT1giyj-j5Q%rSfqMkWbn4sKw)`u4Wk%(LHp_uXSAxbVK4Z@xLM#ubTxIfV9(KlB;Mmeb$R;K9Mp ztD50ymVy8PXab;3o6rPfFm>wGguf@erywCC=VqR(p+b@p|st#No*&IYdAnd`vqYyB!Dkc>OoENVSD(QnN7;aAdhHB91V+20IgV=pYnr;Y~YlrtLiT*khXEQTfT2=j8V? z<1Zd^cKpQw*s)_rwdIyu>PbrWzs;I8E1}P()mc>)9)Dx|&k*==_}JjIC(~|}fRHQ> z*_in#D+M&Y#cn4Ao|}HQ%O28;U`{Fe*u_g!14c`FA^y*1H%>cFO1F9X`|Y=1LJX0u z6k~G*fhq9lqmRb@nQPasRR}h7^_L2kZ9_J@imr>=KKNmf)<4bAm}ZMjN1OFYUb8)c6yypX1Ru>H@{? zglvI(PGo55iWMuwyfDt!ase@Uzw3>)26mhNSV5myYyXqrdBIGTng+ zY$kJ$!WI%;nDi5i`1?*f5hsnl@yU-*RQ0WE1Y@6b4s#2lufyuqtKRdeRd(V)dQQ+4ZuCUDbD*yrK*jw7%p zL2AgX!N7rRaPt|M0h{v4kKiLYi7dRX0xB+}7F;pNmXz97f~5N2Z)!v$5aA9T>nCZ& z&3Ofw#dqeJXKG=9ZBJqjG3|Kl-$X#c3;qS70MRlGvz_SpLtjLu0w+qthYz={2N`HM zw7h1*Ov7}A#_S<%7d~0Z22T3F1osKjQXo^`iAtXd0==M^0EDjyV>NHiT>o$py5+OPsh4Kb)urh=+p5BS|1>7r)f0h7e zo@J6o0V(Qd3&^2&qxE$69sZ@)m@ zeaQ)G%FuqQMe|4xF8TE1$Bz%%(e=Sxa1ylz;WGrWaW@sapZg>ocf5d}bv2cd>qMRE z^s&UIM-&B70jL)kO#m&xm;~{ZxF&-m_Sspf_imR~H#aKv@=faRXXdG|9=Kfn>#9kr zq{wOoZh&tB#~7V~>Ju4Z7P?m>a7oyy@3uepN3zc80`eUa>V!imFX_%UKDiS~5qg2i z@i)2c2m)yok``@X_Nl`pU2w5dFWe9U0^#+=T07{ygu3c*kRn{fy)~%ak zT~LARd%_P{iIjnFW4mvjF{TgMm1qHEK^`Wc-W~^F#Qvdt-KP|hk)($TJ`m_dCbNwQ z0Tpft0>XOa4aD04wUb%odG+l;OHg zk&aXfjOwH6Z<=4?)FfmYs-(0s_HcL-{W#TT5|}Qy{7WRjH3N02!1n{Bm2X1r$BR5LyqjAZH2_>uv!c0UJs`j`MT%QP*uoTd-4f7XS6>5v<-k0xb= zD>gww5cCk-Dlm(!s|U#w0JSv>W=$CyX#zxpXas*eJx4u$-N~w=#PUm!#6mkub*-4ccb9FI82a%sze5mw_wTDz>$2jY02(`XtS55< zlObv&!Yd>(r)ZxRjS=c1u<JIY zM!3KyO|&4sQ1Fc42Z4SFtxH0=15OkdG*Oks1*)P&Gu5`dNDb-JS*d5PPn`Mfj>3=c zR^txstD5Iq139KlnUa-#1MEakL%Jx1uMf;$AL9K+b*E9LaoW*7{WJmIer$QCdhaeg z=u*VYd-qO98?_K9C>ZP9XD15$s6d8khCn}na}V29oiS+V2%LIQcXjK80qXwK2B{UN z4pJ{)G*oST#o7kh{7mpEqGH zG)de?LPLwXCIn2+y=33NKq5O2>6rwHG&n6SeM0;NVXfCDXOshcYA{ux7k~eqwo+fs zJwEdS0Ek0z?7@2kH-gtbfyW|1{Fz?fP90^pDTyE5ehVB_|5`3I90X8ddsdn9=tr~eiFLm(=hp6et4+>(+k^R&*o6-2gDFn&{#|cco z`udB!y!$jWCpXFHcReoMwss3W;Db$?&JpOvKc-Iy_3^A@qY8l4^0%Lso-GqZ7o=Vb z5d8D(e4R8@(A0_!(4Cj0n{*OWkoxuOr+X;avm^)g>7o{#ag2I>$+>FPJu}p||GHBB z`Se_w^|dGr2(M3ZeDf?OBr7|Mde`s2&}dB5M}{Xae=s9GIK*=^w%dakO4ye-`u#@F|1D zj6srwRF7>c)q7Wth-n7=djG`|3Y2{RbO`~84p3AysCO6DtZC>}rb{uIcr@OOML+-i zvu@)}CuI8m(U)Msf(2^GkRf6H5I z3VjdZu9LHXSyRvaMMbZqWYB44-Nb2~b z2!YS$j#V3Flc!Ap7>HlszM1279f3JjTS(QjjKHU{FX3Ws@3g~9MRyiOxd zHidcQ0lTS-#vZI2nS6Qw8a@7LnNDe;{fpjoWEXeo0cJnkf?YINg`J)G_ zJ-dcRk{h&dQ>o4#d4R_Kmru~|SuYyPMsZ$(OkFDu!fiKqMhHk2@|tl|DF70S{nw-g zv}{Q&LU6qp%S~aMPZ=cUs_6&Ny={ehW9IOv4#BS%ouoEABkFS|1pvTDKq)eyDCu44 zPV~-g7Wf%E-uzuOh*Sz6{oQB{e6B$ZE#mZ}$rQ8;0EUE;I$RsZBWVXlVOF45%+E~2 zWqML^>NEhWRv;%QtfwI3zI@6c(QuS$s`>$((66)lX5QGS{(xUsT$FhMkZHi&Z!A&Y zKXjE^IrlW}Fq}F5Fm>64!TR5Mrw>;j-6f7um-kZrh(;3H&~(b7e!e!$Pjij~1vKcW zO$J|WFi#rHJQj1Zak3~aecUbp9`}qBHen(v5EKF<_vmOj1+R@10G~>Yz+aCiA2!Q+ z2=o8?_;oq~2z9=>L=9}4Gl(;e8Q`h~kUu~YQRub&oNQhIIr=a>4pEY(yc`&c0C+Sl z0)Vh!PEJ_;#kO8B!KO~*1;K}K)b8zFoq{+7zg{%iQiHu_)^tF^kz)q+)@!iGPVLlr zqYl!-pjC_T=BGfL2aflzduonO5DLAH0-|ID01xFF znf~jO2V@$FXiV6*i=6oGQGst3PjzL0x(;2sEJ8s07v5Z={R$UK_^@#!Yrv|#9lGoN zOGprbQ9A*^$xrz#?6K18<31~+}#U~OGuy$ zQ3B%Q?FHEV^dRt>`9bQ#fOa%%M%XmC_pb3~`}+i$szEITj_vF26F>;8{nw28CjLjC^G zWpz^s1g0HPnfUpAGj%diPEJG3{FDV>JM~D*A0V&gH3#(StorrnpniVrTG!9}`oSwy zmu)w<34n0|H~ZZT)|?^e9(XNv3luHUTi>g2Uo0$;T!6dNW&$8sTD7{z?gzNktBvnd zh{AjVCm+~Ny>{uah&}>127h}n3(Wx>b&ir=V*EOxNF01`EICitADlXD-=Kr9NU9_+ z7&A!y@X*!zb-I6neAapq07v%gp%za&E^5NjE$0R8SOEi4XsK;yW_$mAnB9aKY08N( zV79)O;l7%+fR-&kN}Co?RrQMqV0tU)HT+KCmkMVAog&aepiyp)YFFJ-XAFKbFTA6W z_6Z;aG8rA9pobHh*@aHLe~0?v;j7i%7oVV(UwWd(16NE`6Nc=i_TR0O+IQEEYKsbr zb}WV^?mzFe;g)*OJvf|v8=hOB?v>AA0+RDOtR^Rm!>SoAXP!L^ke|^84icD-!>IgB z7iO47-Do%7sL`Fe_kX&yfP#Y2b}isAkM_JzHo_+`P7r)BrA7JbuE_^RwiVNbLoiSV z@&trIUt4^xYAa*Q^P4o*fB`A_)sU0CK>klPpdI%6;5`gKW!UMur)FEV120DZPD+1W z|0bcs%O?)8eKrj*Q1Q*Fj~`&DfN-LjGK8ssQ?YI^zTFfkD0nt)T0nL6I{Pzv%Zm-~ zlRKR$pm~FSIJsggUw7=D>cgv#)m?<`j=`@No}~VKR8;763jtB-zgTgZKDp-#2;1$@ zwzb+^PWA|hO^P`BgX!bB2D%-yCL$>V8KgM~u(|?&o+f|52IkNxgH$F=1u_ z9fiKSaiaQp`GpcA7pBWLCt+gq$fnqSZ7@fY zcb-f^i?B%0G02t~xJFRo#YXojI^_z8)BY_8ZuIu8OVsTX2dHo7kJV1Wpl?8a{_3Vl z>Yt~?fu^;AIxYyL7GlGbvsA~;%rMPpK;54ZV0^C`Ha#dPSebGmP+h%NY7Y*#uce<8 zkfqdHuu8CA5PV?Wq_@DmCm$sKfss0Iz~l~~5zuIqyn&3g8B4>d|JM_<)bewVRP8FG zbt}TrNJbm+{rWObFghAGyh&8{RVj4}@>xYi7u(nMZ7(*rPw~@q0qL7z%+SAc8@2F+ z{_3f7ho~QJI3bWfaGVwdI0}DRF}0s+cOApZRU=QzEpv zV2mJ)A)4kjQrpOBdA#@t9zOF>-Ef2)0vv@}BbYxkM35$fNkSN4}h8-Ff*#gS-``*lZz8T(3XL+5i-!J*#pSCme(tCREx#xT*8^OSK z0iypQ7XA_TIxKwKEO>3Od1>I4rdM5rdrchi8n71P*`EP}lZT##UW9l($Rr?3fb;Vf zz$*xJ>9X3_0Ki>9r%s&9Xg$+IkZJ<*0M-of+GZlO9dZ^0$8MG39)7?@-A(=A{^lSW zjM!)6Uu1O=!&tr$0YkHq;$`QnP z?U~r(kCzYNbs#GanKt<#tiEhau`V0oGAvt9sk*yZ!k2hbL1FT}rG zM<|twe9uZG@>b!* zl7<)>XLTy_YmY8^-yDZpX1#27Pkr9(XdGs?jep#%e(M3VbToYap19F`JoGwq{l({) zDqLyW@9Y1<9!tMNUqZJg@!5MrcR&pg4Op2l-5)y`@ThPz^c3_a#J&OM4)P@!uwjVi zp?>58PMID(jQja;cEGu*{~C&H!{fp25Vvx6F_C95V9wyu9yR9vt9qHcF2BG`#b4wv zUK(f)PkqMhN5K&nF!Km4(_S$9VhIbqWy(l%5MRHh{y{Tk_|0bO)3;jJlq_Dlr*BWw z=lpJ_S1@8uU9=dmI6vVz=t8J8&1Y~4#G8}R&_bvs{?35*WHcce56yB7a7JDs!gM#L_;9)o0wbumFWaRQ!f+%>2204*aENP+a5vet50e1!BAK zBM@&Fa>XSmmN-Oot1kf{YNYrGh<`{#fzc0Qbs^8o)N!cQz>tPmx4^sv&**$DF2GT3 z{UH;8fq`~dJSy=0MbuY>!~ka37;tA`=M1vF=p&B;9Xn1(Mc-jxf`IGuGY`OLBnp@T z&^-`$2lF7-7jQvPo1jz{{MPXIuY(pq??618S46#bF8y_3u7U4!Ib;oU^*$!hx{P%P zbPmp?tHmZ$TDrg&Ct=q(@dFTdkfI%N9>lXV#iZI`Xjl?@3gR}K-(Q#6x9$s_pu3@W zAs$aW0s$)zd22yCv9}?PB+G?eK@-mKi>+i{IsqUMU}Kf@c}Xq>Y9TTL13`C)3CV3x zjNJ~Mh**3?XQ5muL!OLDz#G#l6|Ssg-oyEfG^i1x-Sa5K{6(C6$(`XqkX@Ux;^KL* z`#PF(0O;0jz58?VSR_Qd#=Qk*&V=;|n<0-NzzRmX74c}m96>hv5t>X{**7T<7p<+` zh)PoD_J;KsLI@X3jImCEm54DCrEFck0U91&l<7__PF~g3n^QUp1cQ5!P`t+dd5H1= zLU_O#`;S2OVu>n4W={eKkZ=|O>j^S-Yrtd32y7 zM&$5OT9joj2$Yt7o%Ali!ykaRJ3@#!xVOl>I%Pe<5y&G)FbHC2VrGm=X(*aZ5a8EV zRk2aZ_E$XP^GrWXA@ZFvh+AhCSN#h`1p>}UU{TQ+sBKM4i;6x&mEfTyZvxih`{=i5 zz2A-5dWb^gJ9O505VDU2Q8@zk9dK$9ZwuPWz;uE&XE2%8;kvqQFbHuBSlLY0Cri&y zh=So&$Rh~YWyW+dYKtpF_6p2M%2u4lqa`J@S-0LFDq($gU(=E3)s3B1>e;nieEW4KWO3Un*J@zidM>_N9odV{emTgtCoP#!kjI%*=iK z`kj04x%cn;&ppo5DW~+z_xUcL&-=4HFS75w)CnSo!G%wc`f;hlyVdzQo#KfqU&GK7 zOstQk$nNj?#*@OhrPf>`;a#UurO)CndLvR+jtHG#yOIB7{d=dB8Z+BtLyhfU+Q6Lx z#ZCD8?bwqv?nL;Pfw4t5-tDT*8oXxZmxgmhYrN_A8is0b9lrFPnF;dghowmBDYhI= zIS}h(&&AvK4p9`k=B#WCv9c7duIDq?M3Y}!9I0%YnPf+jNgM&w!7{SeY*MdT1K>r* zF6ou8e;iJFd+qMi3p~%$Kc_8-+qr#tmtt9c{+{D~o(@^6(Q^0Pcg|I_M~*yX!nEyC zCPtDl2ol{Z0E&C>sR3V8`RQDImLSh|j8!(SirFfZ1vBq!FRi72#S|oU=aC3j=FzR1 zKZ|th&=)ofBk^i#+B#5!+e-n8u~2Brch2gH46vjdkN3HHQisrQ$+ku) z=0(i@2YgC1wr@*xfoE`;NW6U_=-s8{flJhUk4R@a-R@0O=wsuNwkl2xF`DlFwA>-v zVThOKQGIFS{maK8rriS`0vnMBX2o>04mxWfO7Kk9krH{};?*~5XLA1!F@Wvk$JU3%TpTQ0(jbZhh)24A4ptGr*v(OsA$dm@NH!7g zaJ`1&ooSgmLMzx!V8AzAp6QsK5!17cuWZBGOdw3HdqOVvwXsz6$#t z8P;8+mG~<5IcXiSEe6aLj29NJ1pW<-U+Bmsxbp~}L9vHkelLkXVtAw(G#thZO4a8# z5@5~A-?0vjzMDC>JN?t+H7kvvv4>0rx82SSY z{Q-vl07HL(p+CUTA7JPYF!To)`U4F80fznnLw|swKfur*VCWAp^amLF0}TBEhW-FU ze}JJsz|bFH=npXT2N?PT4E+Iy{s2RNfT2IY&>vvv4>0rx82SSY{Q-vl07HL(p+CUT zA7JPYF!To)`U4F80fznnLw|swKfur*VCWAp^amLF0}TBEhW-FUe}JJsz|bFH=npXT z2N?PT4E+Iy{s2RNfT2IY&>vvv4>0rx82SSY{Q-vl|04|jNmURC{UzDj(v*|^Fgrk~ z=R9v_atQ>20AE5ttSrDEl+Yn6@J$cTn;6=KW-m?zXNq2KWLfnQzL%F9*8t;7;uZX1 z#+1Y>v;6i`fs^DYGsJI|iSsSKCl}Wg`qJ#0kME)xiAZ-Ned{-is28i}(5| z$hzj0^*uW1$v@9awUjRhTNa+==v4WC`pY7vNAdO9)Oh;tDI28=X9I$YG_3a08ZJjD z`xg8(4oVauGiwQ> zO4;I$PoG7$RwX&aBN7#VAc9ha6j|64JcSZU)D=K$@yr(qj-!03x7MDG z9?2GGy)`$ns~$)&lz(B;pIeq+@WUXGN;^j~&7Yde_;F;dhG0~0RLj5rflm^cG5eRN z&@r|rQm;*-HSBbb7o5U}wm6a2gy!ZnAN}KWG(}2RSFkbo3dlIo&G8edodI2+ZDW?r zw&s64lY0N*Oq#k;OYT$4Q#U?`vaoRLahI=*`EIY8=a-hMGwGXl&U|2^je9vBr406$ z6g(yPJZNm0+w9$cZjJrt{`bkxi@I7DWofv$$RnMdUUKbqcP|0|oxApyI`8*dP0>`X z`6`vjJy(^xzt0fBtLe7B+G{4puuJk{-ccK6KRpz=re8-!w(UF&>f){Mv@xCF27cO3 zLdeoFeB>@?d_lomYl8OzeHI$E6QvFufuRvlSVllwHRb zYIx@9*|V9dbB01{^Sh#!N{RI55)q5UugH|5hDasy8-&(`ncf2PfSSd?Q zT&bGB&pXyvvXsMTG<31ncLrjx@dohUA7n+|+|Y&b9~+0~Xj>%?SM(Yg8o~)3*8OKB ze5uniM#{ouuP4F@ouz8?A@BlDFEEcpdPDFvY|_f0hXtb7p1soMYO?WL4*f=DI}3Z+X&|DrV_L^LQ!8nDI~o8*BxUfX2SsKavpzRLf7vCcM|Q`zmtNkVo%B~ zlNOkOYo@Gn)nVtl!<D=#68S z1+(XmF8yew%cAXt(cE2yKP20M?E)BsFacXz!{s|H2!&8iF3oGupFe*Z3&k7YHt24( zd>oxe1A&KpAp{3?s#u!fR@4MPoo(S39+)$6a+1NCug!Pph?3+EmAhVPQ@b^f)1Mvn zdieOrk<&LG47{zFUjI5=^zbCIn0L8f>|}@c*yiO`ly-%T8jH~IK+GjZT$6$`q#GWXI?yj3#3v8(dy6PcdZ*LxPf z^^`mH)cPcFmD8wShnwmL%Kx#kzmc9XQp9L2=qN7r&7Ai&j>;R1ni|a;D~PwT4m=Us zved2*wT@F@qUs~UVmyUl9S{S#w0|w=lanE7YofBq@2PrwFDJ$7gBNN7$@%J?-|O_j zxFs8%H3h135-{E~(BL232r#MOR%x3oV=!qr>aBL2W!+9hoI&oIDxhnRy)80Z+u?1`+!f@g~uM9HYCzMN!ItzkYqe^J97<2C6IbWM}N*%O?&ZwQ{)jEV&~}i~?BvhW)thE~BA(c~q_w z^7@I{*{3BH6|2hwbIjojsz1en!D<1YbpAfGZTtehGUdK|{F5j3lG?ig zk8&e3wvUABv^P zV7bFvz7gtIFn*`?GuUPM^W%G zukXlC|Fd_GS$0Bre(m4&U!4LrD^F+NxjN`y-Ei>%?-JzCIt<5r?fM~*mq@+PiB{g= zWyyE%^vIKFHU-y4DCO7*9QUe8+bbv?AAQcJq&-@Hh@#awyNft+A|yHI(bIMARTKl{ zRL(#AIK0MA!C5R7! zA@6T+u6<(xH@H)Hf7>EXd-cj(ZJithijA^)QH$}ow0tA2?#E@RybSZqC?zF-?wOtm zwnh(jSVW4<#4>XFxMJ?Yu(0{_2#!Rtkkb@$kf3Tn>lH^>b4$lgyT)^9tCFkBr9WCf zf9f#J&E1Q!tbdc|Hai58@Fk7d(zPn897j{(63NTbzgRG#+;9UTFd#mbzXS5^{(YaA zrQsXEdiJcmn76r4_<&0|!m%lYTSFIQ?K>D9 zHC#UHfyZnRD#jp$rLVvKu&O&+?G)i8n3$Ot+EMe`PM0sEnKd;TOc@I(R<3MIZqma# zoAjhd4CC!K=|EGB74|g1ikbkLG99qh``0CTErV|ydxG!@&}X^QftL(8u)l$y+-tvj zKhG0w)`zeWB{XdX`GC)-BM01cj?IssOu4U(&rh6W)#S)Wo#|Q_5G)Cj^@cRNe#vRxhzV-FX@x+aeI{; ze6zmOycXFB%#vFBs-yScErVwoJRhN?;QrImCOkH7GGh6!U(4EE-iRqE2=Sn;D>b!>4VN#w8wai3!lrd|<_?ZT>khy|wcDCX9F9Xv09AA7jK)X@RGUj!3YIsik^n9iuE0$?H=_}@!sBVQV7ntIBN?z zH%F&g#R4uXvF=tB-{Xr`bz>_N%cqLVBZRM_Q{1hVV7NI~*oV^>mB*vqaqD8zkTElZ z_m@I(9(+Kw^bigQb&DZD``58-vRu|mTtp2DqhaL78!U)PNfE=QZ7qKP>e%v~6%pNn zRd1^ERb!8=F?0&xi*TgySe{irdo88yzLxvF8#j2#wXILBD#5w)xFVkBl9k)!iQQ0E zrO&ooV4c%%c(jljhkH^dAZ@hUoaDl0>o)iLTk;E0dAhsI)va8vlEU^B>UTJ7%$`B4AfrGJM;2Mq&`<%I8cpo>h>2&$42z66exaeZIC{mlVuEkk` z{S_mU9^tj@;x%p5+1R$Qpmf}ujoNtXbfV##8~Vu!IfGj}A;3?I9>b;)IW0$sz2u&S z&fpg*ma}7$6uT&rClK%XMx4RJQ*;m8!=Y?UrtL{E1(nq~U#DV;a>J znjjvEkclRdPtS!>1_aw!6jtL$lD6cw*5*5>IrE@=-i@^>Dzqtit6Ti#=E9(DahU@H zGz^unnaKn@Npyx&2BKom+~#6#g4S;Y4QlCHMOhXv*)f{jI$o%L5IWUGpf!4#0{;Bv zO)Iay!=2Q-+!`Kwj_v`kpsn<~HUsjH9tWv6xQrOB)-+mj<1Giw>v72jbL@yJ_i8s3 z+Dd`P;z>L+b{SX?(f`(?Krn}X^X{sU?0oBv@;C&0LjEBe{{w57s4hM}$akK_MyB9d zd`F|#(5bLL#-u%v;RiGLpYyW9jdlUmm|@+VmDSFG_yjl9pzYq>-$Gi|3o)AcEM$Zt zha$&t!9d-DXm#uI>(@~R#nJl8D{QRd_b*-l?$!YLC2Nqe${8klnW~~8CPBG$@+mO0 z#rg`MpBjHRc#+&Gr%^xFVC6j`0fN17fulu^pyg^d2lfIayrGcDvTcF0$>A!pa;son zq}BE7$^;qfu+qGO4_dAvKi^;UnBWRVxrc)pbG)QvcPWV2`Vhv*NDD9zTR)muqA;2w?Xc8)n_YgaY!{7=;oadSket+G3{6 zmMt^C_ilGZUl5@bA=u$68Gl@~QFpHM?xO#bPkj{-u~mmEIv1r#uPLu8UUED9K_|Dm zR`98hpExJ1j)-k96@jk=&~sF#-KtylU3rRYDY|A>j3Z3A6-`}yk4Ot8A4e^VUs_v} z4SM+gkVOixydjRD_b31AH46nVC#nM{Vt*P(As)I09HQQ2)gw*N%*phjck3s4dP*vm z3QWAqiF-=Cb@a%P5MhhlJu8CkYn@+K1K4i+`rVglk)R6~v|{}e-2%7(XQD&C)YzCF zQe0TL(chQp`9R2%jH%l74Bl`f*sj=fDnd|1tdhX1s!#ssJ*w&z8(iu^RvTC5f}C(S zxp|}{Cf5suj${gO!!c}tH52_;1Au0(`JG93l5X}<<5UD)z`@1PwY4vJKlF=jbaGI+Zd-eZ=>}R)W`Fx;HwYB zZ<8;gL!gaZL-Fx9jYBrC9hZ9tEiG${HQNekd4(miZ3KULu^~FTQm+oj!Nx;*LrsG4 z@1(B`>2gv8{dqZT<@cvhz)J!2uTYt>uxHES{4L5mnec5Cg2hR0q`%TL>7q;CwCrB! zINtYy6Qh!XKk`G%W}$8Nml{-8C5VLcX61UXACpTRw%u){g|&POBt@I=$K5>3NImr8 zW>@mLtTX2>KjR&riUj4Y@B<@cTig`MkcX9)(z&H4+JX;H1Ky_9AAvDbJS2QIuirvY zfoR?&AD@u6aGhuWprioe{9k{NdnYC7J_L&_A-T0Y2n=|eb#li2QHBGjrvUg9K7c+s zRFY;?JZEPzO~|s(P@*wWDVn_HX$y+-+Mu4aHW5mZK@-Fm9Xbp|hwU&C)JNThfTj39 ze<~hjHF1WzR-4o5WZYfG0F+EJxa|A7t3r9q{RC)h6ZaM^E z2!W)VzJMcT#_Wf``xk%Ow_^$-*0+KlC*1~D=bjQ(`$+Jy2!T24N{Zk8{L~tVTsF~B zbpg)@WbAJCPjaE~At(~It4O_MzO(#=x&+hkQV@fd7B6ESVo=;sHimPP7SS2?{x(P` zyti?z?av=mDeYbFz$SCUsJ4;YqWYEUI)Q_aSJ#Tp2Ck>nV52IrBvEf~FnMZkwZd_G;f=wuL}+y>bke)hfF2MebCgq8^84yWI`AsgA{AUl<|p^+Nu?j&2ejZj|s3U zoSqZ@K)zJaMM;pnX>SuG`9LU$Ij-qZ8iJ|iXW>}e#_!g_!8U^%zok4T((d1V<1tsf zTus|^TBRR?xBKJk6+yetqUn2PTkfu8^9l!CS+!t+hj@K}k^K!QbeE4M4@=0L#~2CAF(rPyDl37$Gd#RNOMhZ&w3f49jRI&-hLLz~{NCpie# zABR}mA$do2?MG9UY(yi$nTnNymfSxdesv$6{E4#^5N}tp0d|5-=6{O++J(#vkhDBV zE2j`ka!R7$dFEs=q)rRpwn%hwf3~JF7#8WvislGN6EACD+Kd^=Vupz~a6DILaCA(3 zq4dvPY8{q|*hoj&45*PpGNL~n3OyR&e6g{Y({}e>Tveqz7pHD_)gpVWEBN?v4gSMd zKnR6x{pzSBhXMph_g7T(d;Ygjx@C=BD08QYngYl~sv5L^seBM})%qhZC4%+Gwjmx{ zHVr_kIf1!Le@^FhsdQnTjJwYDnOLOD6;aF`-#h*jH}Sm8uehE7!QjePX^LHQ2_=g}y@oLL)p10~d=wl79 zl=+_m$vxVGzcq^UIWCe4+Rh%2(=bGnPId#&>2;n82v0=$96xDo8&9PsayGGAYL&2L zOE@0q&AyXz(tkY1+GSiE*>qTi!~twpDC>sGi9%?b5=b?oA>aJRJl@@nE_Cf0g=rzA zCGwJfXi%p@5>hI!Ug-s=giHP5)^`k@QChz__V?Vahq%g<;!_-XlOd=4bZ_%5UIAGG z{_`&SbG;kH>8&43_0}04zIxF3aZ5g#j?Hw&#jC}H2BuEc^FZ>G=|2N;;oSwtGw< z%Dux#XZKg?Pb*z~kW0SgK6HCw1cPI^m$|+~A#Ne#LBc)dEN);MfB%#Bo10)ucb^PL zETaARhjfB2J5u@?3*%0NinGGx1qDSkNLmr(jOj^Lvo9Y7Ctm?kG~PLq8D`(O_UkIaa#?zG1DF495(j3 z;i^E}J580ryee~Q2-(9?;>Xi}G=<8??(UXKhu)oW@dg4d?kabv8l&+`=pWxBpQ9N! z@5qrVr@nv|7H*Se^b|oa&k663W?RE*lxtg`)paz{Q*?hU3jdAe*_-o@XNK5=ih$`8 zQW~kVcG?kEMUs@|-iei0y(#yLA|$>B>JCS>a&n6(CqJRqUs&F=MLjLd4`9cF&buKV%n$SB zYTY_)(im4o->G6CxrTHOHRSgz4}zz9pn?jpg~(CD)hcF>&q7`wp4`iCXb@WrchR!c zps7f@3SFFgGDp?A<^FMP*DPC7wfRF@5D>loXMiF2SJo-rQ}NeL7miE2LPqB=0iJnv zCnS1g3_;ll(-9Za-qa!)=|6mlD;=pgJzS+;+0fDOF=~&yVXf!8e?$A|qQ0?c;LfiE zdJEA?9f@&i`5x#?%G0CC0eNy(^6+h5@(k+8L@EW{mvalbaD!}IT8p>0(}9Xx9U_O6 zLm4}#Sz-Z4kwxne+XmD_9*mxlJI_76l0j!LxD2WlT54-Md2jhlwSIhLRv^``O=(V~ z!NO*wFeOh{hJ}?A7TGfZoX@|T8}NTIKJsn?&pGc!1wHj9bKPbB`Ae5&T0$tq*ln^3 zIG!mfJ?(>q(Z7t1mD?}W&B>u?v+`A{{%tL9_31TlwO7isHu_7({L5~4vK$%CV4<8O zR5(9O@F9xFs@)1Pel?cHwYNSivb%KC&S8a_HakA<)xM;}G!eP}=CsGcoUP>c=%fm6 z%L%c$Gu)PGWnjy>r!R;x-($x2H|n3M1v%cw@9p$Q(=ezvfu*3#tSsW7Gy7OHBdy?} zM`i;%+<^VRUC1;5*|=~6AUknre3_3h^6Z~5Rt_;| zsBE?eu-%Tai-oHDt1iFmtri)5xwPEG*<#1DuqClP*pmM_>n0grICgt=ZOFX6(Oqod z4JVn>YKGgOMSOG@|FXVBn>DuO-YWrieR>mdun1aq<(mGK45_l|fg|UhJ#}~E zhLPf(wKKN~C?Ah5R%2Y%T*$&Ynjwqunl5qau z2KznBTAsJiHcQ;_>M{Lem7S1BkiTvXD53Sd5IO`ZB!b)GNM8cPQw9spa$dWB5jewg zCmJCXgM9dahOR-Ewiq6H*u6Mw=juiGG|MAM>iQ*Tv*(JZx%5V>xm+$^UNH#Sv?DC3 z5f`F^O~pB?+%#a)Ig>Xxruxi1xAc`0uE~So2p!m$Lm=I+QTeP2=j=veO!T$gRV<9+ znI)P3`ZBR5t8QM>(Xo!FY5^$?j1ucBZd=VYlg_zlX}7r&$}L(M zo~3IHA5chSveNDgoWDz4x*%aSR+*DOma}oAt)Rf%pnhbLLwRw1dlG-Dg;SJbD8A4% z^Lxsm&z8QR0R}#Y+ci?uaf{f400mP9`lIhMdpz})0TSGwm^zPMa|Av4PvrxpsL{f@ zP(#%qunG|jXQy+bKozA_p61C-Y}?e55>TU*ZAW1}z{OD{{TU9RBmqRQi3RG+Zm1I8 zh4>uG)|_z!Qc9vJ`U0aVxJ|R%^j2thRHHt&9opI*rR2^!rCSZYs{qb@?>;Zvwh~KMLc(v3+uzSot4nyFsjy( z-^?ET7KbO{6x#1!it)gh78n1iURfiv2DNxEmPDen14z6x?Y$31+8goQokuSNOZTsY z-#2BQCNG2pL2saq6Rvn_`2G6|X5({k>ltnE&T0LqWEkXtq-1Pl~- zi8ztl--U(~cTAi?7t8^tycFu4K_e~tK&Q(VGCHG_uYjfzn0i`l6n-nJiRUVMJ+BV* zS%@e|LI3{!B>@=U__*f&_*4df)P#~@!pXiB&1rs)r|F(xGmpQD7ig0p61s{(2!*(&qt$wQxZgeuUrX>w@$Ikm+^$^1TbJ#Sid|7)*62PbC%81&ZIh!mY2qc?Q(KC5P*745h<;!QVbz)65L;XJt-bQJto4glruWL4wt zS)-6Rz8Xt1*P%IlmA0P;Zt`C5mkD2B#O))o>cueMzCYaWoUy?+03F|$EV zR4eb?ELJ@}RU)7^Ai&$xuQYlpRY}0kriSzN>mFn07p9p@Q9r?o(@|Dh>>++`904?c zu85#5VUi>G(bVqu+c#r^85PB$yaf&*gy9Sv7^-Zw-&(7=pn8Yt54WI7A-M~HNicB) z@%=B8psLz99VvCqSoshxB>{oCa#q_jvQ;VH**-)+E*wLEs9aBZJWm7%duAX0W=W7vqh#^zm^U05XUfhpqTDrG1(4UzFC0Ld~)j zgpMfz1|I)E4SZ3)PXQ2B`=<)-bnHb`)kC*|Qt>NTie=6NyEb8!0XY74y|8S~HS)!X zkd+ZssXH!FOBZii`uY#&`;Uj=jqX2h-o#7TKa6jo!&aFz96);_&!*Vk^u)A&`TSXm zw2o#UkOzf8{Ti4_5jK+L`n@ur^)u3u36Zqnb_^Ida>|?VZNFc_W9zOoHXd8%A!Pc0 zj89lmf!W}mq!|8)A}H%@lufuP5CVymo6{4@ZeQ_gha9;az~b_zr-9IR!Ox&s5QIiI zUaLW9EZ_ssE-OGDNv~Pf1{Nvb;-sP-eu_+goU@z|m8&g*luQi+hfXs5ZnUK;mXT&h zGIYg(sm2s3gK(@g3HSoTn|v|qA0vjF6mn7HP^!)!tHAjZHEg%7vdp&uAUnNBnX@v? zP&%Fe>J|UbpXtV8EA6NHsIys>0lZ!&g{$ldK7+vaRY737dj4iuG=$Nk@sCu|<+}mQ;Q)K`*0p*fe&o8N0V3 zG-x+lKG+KW8HgS%@j=%-$pW4tO78uqgf^E3cHg&C*HqW=Z4TSB^ypxHC53TTPQ}&7 zf*_g%`$Avj&HMP+?#69p97lX}^Zk6|M3`rL%$8(B+_u|5HO&%UiikNR--;=*m59#O z9Wf}ge<=ktrgSuztY(fX6eZt?@hmuuz%cbd9=ZJ8J3_KoZ9zBKC#I3GTBW)Y^;s9$ zgyiq~%E|$ns+;(HVl%Qmqx(K54t5Q9AAeR*c!YZ7Q!TT~FtnJGgI0R_ILAOF zV-xyPug4)xUj+Ya9{%eWxAEn#N*W~0VZw}&t-JJ4URAdNBW%CX$D^ImPRJJoN**++ z2S%$Ep{9NU-N2&&)#3d|QTQ)>AHA7e?T5+fI+w3aVkF^*GBNm5IPi?ggb1>hTS&`* zTynKV?#mqbn_QWLNMm)X?OB7Ul?2X&jqriF2o#&_H>)Mn3-CMk!_(MZt9-^C1N`vC zUgM{VynE%01mm_o+0T##ndX#~n^MEf_+O2PKkIocc`GnN+f?bZlU@k}gv1MKYKp8t zryXZ!0*$NVIFmi<1J>o=EY2e)z+piFXJr5+8#0ScB*J~Z;lJNMZ6d+DmcV}KP=9pp z{Da|WV~H))_H;t9>0MOfy;4@PO!O81ljTsP@kCH!K5?hAWtcF9%Q!kTSlHv4D6B=g zOR9e(Ncr8nAN=gv^jj)=&e8-8hwkIybP-Nk54Ro6oJ%yk*0lbeN~-4$O{ zlA4!kHwkmhY}HfjLfPgkS=iYPah*E#N9uvsB4WUBwWiy`5%6Qu3ZUdvW>Uuf{ALzi zY_0aQe%GN;JV1z^YDC@uLWKO%`P)gu$buAhub*bIXYmB+XJ$d6tTVz?p=oO*rj2sR z*lH;+vN0d$EU0+unAMqc4^MrsJ5AXQio{%0c=k-$xUrs}9Ek+9$ZB`2pz%-&#)5In z)s=<1d+IbS7V)%538XY)P+UqqY$PVDg;e1~M~%p|ZlEnP1J#ja6*iq*j_vN7*Im8M zCS+3&T{oKHvq>h!q+37&$p0X; zfB9^;IcXz=X(BCB7p*$fjFR7Leq{A3x0NNgZO!hu{Kwc@Rc@dV%`xBxdS3|w)HWcp zHj}7~$c5paY|V487q^^A4wZW>H~J&N3EXW=9ou=;d0p~XlRPq=fvLX=O#Q>Zp@h#z zKb~NJGeu_(nC{uSkR>(Zk%mnuf>4TDxbJaE=igvwzm;c* zvE{VCLwbJk+1!ftA^uh&*mj0+6A95>7u2oMtPkiqRq8Ox*3aKxqP-R^AE`mS*DN%LCiCIvq}UWnc8 z2j9->+}+CI=A9B~=!@#bO~tImN`{_xJ67v$1HHIGFYnFy^QTv8ysc_%B^q{E-d@OZ z1y^$d(pdX;nc>yr!hQWZ3}~uk-VlgY?=k_J6;k58qiSU{+B+Er_NR+LatvE z02D%pKWH$kH&u5yQ+tFUqZ#a+Ne`!m16?H0SGd_2`ebS8mehBckgfq4sj7>eds2&< z52r^M!_(aU5Nv;*_>L2*!2Q^mT}Z8{GM-z=QSZ+L@)gl7QBlU+NzNGL^ieUgCSLYf z-iqz}&!XT)#%Bq2)<~2ef7}&NYxwq^OpTwCguIaj-Q$@KKqzDN&@FnIYMSd)J{+}M;C&Q@?@-P>&2Q6Jr5$8_T> z)xXVM$}YVqVhnSpZ2`qzEkekWmM~@O#`TBMMS7&;x?d~c!{iugU6c?Nm8LUDFlX%* z^&-y}#JBsSa>QCxUw>x28UfLEoU?1OqDbr8!y~uQ`dZbu)~1ooKY>0BrC0Cv?sqc1 zkEC>6mNj~B4z@b1JQ<;7_Y4t3&mJ7qH@2Ok2vXG9{rh8`K(-aLk=~~CPPihg7irqpmjkD2{rO`Lh2gi0rmTWD z)UoE;4)(3bKF@fGe4O1iTa1~XU%}69i3I>+1?ZIfXLY@>nKst3#qACrhOBlTalkV% z9!7zwTYuKmVv8pIf%g}XpMS|lETlm zYer)6q#&S>mH6pokT?S5tBSyE<001S|Bg8X`F+Zrxz!A)YqW$*z}n-Z9(Z%_8P*AE z1T~pU0m{k_DCt{Dc-ZDd>g!Dj0x3==z=%OKHHlL4x0vEi_8nKCu4aAyoDaBp!IYFr z%*T)FhVHWPm(v>|Aetz*Uz@}@s>i4}tf$Q-5TgJd&p*pd&Qq2T7kV+j!JKi%jk_H} z5Gl|Vb7YtTZ2-mrMwS~{OiN^$r(c12uz6V-v!7Ge-Cb-kV!)ol*}I*90>Zv61S-S- z&U(+Q#NxWuG`-&r0rV?C*D~^jdwJ30XU}w5-2$%cx1wb0VT)dnFaee=3;3+uNw|)< zgvl*z!11bjU2o6w;!s2k(X2B()8aKD`m%C<3{UR$i^VUrlr$6a?rq73R7yixz?nVNVlj|SS~VhGAW zNjYjiIb`hY+!)Aq6BZFprGECh^X84LQ8nwUV|f8E$NQLPpPoTwWbPT2zg52le*a0E z>uRu^X;hk0P6hnV+Mq#bx5wt{px);{R9M@riE5xFFP_oLsVulEa_zbpc$%w0SXkKF+8XqXE(>Q)-_27M z2z(udp25`7H66fP6Za(irdnH`XSku*#6wA7$Jw@J0ib0$uy|Gfot#0Ge`KUy+^&{x zu5WR1sCYJgJjY8ZUI3BHje3ghC9=PMeas+uV}vkN)$4!QBa$x1QwG6Oe&3+A0M%GH zZOfdc+{GvGuqTZ>p2!?uo)%u!`3Z0~R|WQ4@Xa(8KpXou!EEzdvNMs94%Ax z&ZaEJ3lwZ)M-Qg7$0z3e^W#Tx3@PTMzYW8{+1{Q7O{GNh@7;}qRJPGk+goi6PUW_} zQ#!kgZ|Zb*em-5@o=NNUO4DVzQt!`9+hR~h+C0YI-d_E2U%BH*nhqpOJs_czOPk|@ zxw+K1(WAVm6J+E$Tw8|Y8zs-%hZdFw7H-4Z&3W!&h9`ws^Q3Vh13plC>%(eRnOt?vfasklN zrr<6fdhOb3&>PgeiUw%~2p+Q}zZN;ku+v(R=f94R$CE;4jf};k{U;bbeohp{%7Zdb@t1lmSyi zSja{fztvCyZh51>jkY<4h3@9+hR@gDTXADmr_OGytc<_ouT3Jaj6eok(c!GFOrJNe zxYA@aXtcr<53ouvZ&kf~7xj`!EjbdGwzWLcpQ%aIpobd6dhA;4fChO7l|YgcHZ8B} zri80<<~g8y@!wSCQOLfD4xbdoIYE8d_{gP zn&N76Y&?eTv;ye-euYi)@!y=yOgoR z18Vrlri~aktsv^|Mf=7HgrGZ&y5nG{!};M7&@|B(M^~#b$Z#I^Yo_%=Pw$m^Fg@6sIw|Bq3;XuX6lzs+&U|Su6IBZ2V40g%>KYn^hO6AU0U<6p zUb9~aAtS%%<~myqMQnb%x!M6&#s}<`r8AA9-Td$I+S#`XF{1qbx<(C4$@84&#G#;_H^Gp2kqs)Ne0%wS3VA}S6DPwU-ILjRN;k+wO zJA4`~FJ+WYuP$so$VQ!IcxYrE`h|S9a*TD~1^eF13p)@(Y1x zU!zM($g}_CP96d>laLRQ^ZL%n7oMMg~P0STT9P+ z)H|LKG6!;3d~gHR4hW9~Fx-7Pi_I^o2LWA7G0WIS;~O`vz-hglB4_+S3zw?1&fJD9 z^!I(qWwKn+NVaT)HLX}F_mw-nVL@WyYO4##cfe*4c%n}9g9p6k##8(LbJo_e+S}n8ao+=Im56Mm z%BTQ zupOa|ef>u*kh*bHUXcZ5ThBN@Nho=G@@m{kbR*D3mk+0OjUkQEx>~oZ@6HPIoC8{r zYx(3v5SX|Q2mwgp2C~2n{Pqu*Bfky!xN;Qh;2DAY($?0&ofr}S7 z5QxUpFYG&XK)v3^63tWYSFyNW3Mq;Dnha{P{p<`nmnzCOoR{}Z4G{{exUyRFNf6&U zWwlgzvZ|Hp%c`~-FwuBu620Y0!n8`&1q!ioStWzCH|K}g$_Ss#g>`Awz7f%pS zF>avZL%8bUS}NO~C?YNaFLLYv$H2^7oA1;q zI+y>J5P{K(+VHKG2T@lu-FNF_KuuGf5bLZ+aq8+TYmsWbE>m31H_kmOx_Re!7CyR- zPVRg3=4L-jQ-53eq6CQ&Vk}@DLgb|NABsKylmpmIHMuv6GmZhTsIe87&NFX_nosd6 znhXLFK35H>%M_^7F9L!q(ntXFvKy%0t1w&x=)~x5f6{<0F{I5@0{T(f1IUENj=6G# zAW54w%aTrRI%rGpiLY%K&wgH6XLC6{4EJt$yQ+YPmZ9 znXjtGw0EDLF9C9$4n!uQ=V}q>fnB1}1>te`1WU3@zzu#J{R~+RKk1Um5T8$f(+GH| z{Ejv?CdVi%%P$sY8xSn~$Xws`;rfbI>+1<)LCF+*DS~wMwGXD&m%qXmH*dBH#s|Of z7}T-PWR^^=26f#7CPKOw!qdj~2E=4J1w$sz&!1ooo_CtKH-#p=yL+>mo=c(RK59&J zqVe9jD;PboW%Fw)970*=P@7npPLxQPXu1dG&#~K0y8#|vk(IwC@M<~4LO@fImFhS% zT=8(SQZm4smta%_yvd>xvdGCZp0Ond(eBcEtm+9)f&)4o5NZWv;QT0g zA~1MqRq!t$u47_V13AG8240%RZfpVXV_{2yjv-X+12tE(p5<^tRzH$7(yKUzo@@jW zQ^m9~fSjHfa<*B6tX=}ybUG=E0SSN6J4rRylJkl1kxI40&aCHB$2m5}MYmr%D)8w6 zTM{rQHNd+?Llj5Q3%!4Sobj{;DG5Z2Y@hJ$Nt1v%gVHB1Mx*#&OvIetsM%-d46l8Uz`_7Im1ZWVVIKmBmN(QR( zfR|N!YI+ZARVQk?XyO@XFbv~W%$nguTcnuD}V;9o^O}2mVrcrhfE03?%YXJ z=q)kF0Is1Zx3)-KM?t%_&*OYq^9zJ(>GX5mN0b8{WW&z>brnvAx!<-@t>C`PWKK#Hk2-&i@0)Y{VD z7wwyF)=&-7*q;<2CK1pFoI##&$Df`ewiY%5%(W58A~sr~o`qiP7R2ISZ4}jA%sOKj z6m$~kCN^x>e6)zOEGuh+RMm4j>L-RQeQ&32O>yV5MAEX`=+s*tSD((%Odo?5wr1Dt zjLvqSCQ&xerKeDy7?#;5DCr=7K0a4$4yCU20v-76=!iV&)xu&PHo<){b*uw08{NtX z%UTvf>gKCmchG|I(1x6>OkaO zKqbJ3xALAog$lX^g5$I&R)A3@nk4HbI4V0m5Y^->&+C08WlVZ%r57FgsiJ<8o!T4Vl-)8G%D#Uh?&ic5??If&I{YuqTJgsF(i+pKEAnm20zF7s>IID57sg_K&6Lo zF0xU>;ty}Thy6NamhK0b+Qp*S?{XXc8*EASTV?9))V+I>%!^IBRWa8TYfX|ZfC7=% z{ib{BMHVOR%T>xV*!SPVo_{B|$}Q~)cyQ=`4bemZaO9hPmyITq925>k!*-Y?ey>l4 zuNW5VY>nStU3}Bu@$s2_JGK4#jZ_d?p^_+KM%e>iA;KB1>W)le{NMdTR9~?^AFxSe z+szluvHQrxny@ooM>H5#zKz6lklMm{p6SM)U4llF28G`8pW8-!F!_G~Qx`MI zRHT0Qn-~cWUtz(iV|oT{+rq-i+Lvkbg^#V_bn?Xvefsv1uJGlC8Ls!AKQlFLMfK}$ zVa{cwzYUK-u1vOg&VI=uA(2|xu_YFCjzM=po7^(#kNgda0p9#kcZv zKV?pw9S7oxl)qtk(i8JjKw&F7F9j9waWHGw=vP@)DAwkr81;+3@y^4h|;Ji1|cEc4JsxGl2R(E=pfxH-Jmom3?+kfzjM*= z@AuyCt>0SjpZCXGZ|1sGbnZR-p0oGa`|SFRuk1kv0TU0d<9;p`@82+`z`IG52U)X` zb$5sxOV~9~77h7VSc&4>-1>)omMY_tlWF^_14g}Of_l10u8Kf^eITT-M(Lu2yjQbp z-%Grp^=L)8rZ*VM|t?;kfCOCA0B>3R!mXVOj{X|l*a^4jQqMUu(`X0dUTQBjz?z_(p`R_N~N5< z)Z&tQ4C|TkRW{E3aKaH!MUmRyt{;wW=7ZI%cV*;<&$AluuqiH0M?gFhws@T7x4rXv zj<&Smf_sMe85#|MM-CKoLcJ~vi6WDZK|HL1T5#2-4T9&~-6#u)HO3n~-ts~u#OV8n z8)z^w>8QXTWQ~o5Y8BShodcIZ{I}F%D{U#~NA+8cnl_m+nskG`rNLrSM*b_SrukRM z(?4-1E3-K+__v!oRgxRbv`Th>Ji${!vgpJ!L&sl@nMMgoyIJJsQ2vZ6g>#Xy4X|H> zC*#L{b>$B9G^4qkLRqduOpdpJ=cK(DPeyKK~%&sN^o679Q%=yY&iAFCghSU-7{deFC%|48ya*6&+$8D*Y|;e3C-?f&kT z&HY*HoJ&p3!77Olj>@P3_XzKKa_pnyIg)O8qoS&+g@yLCBsDodUq<7f7cV&h^?5bD zU2anF@o`R2ctra5oJwzOS$qMK+9uhc#xV_13BH2B_zv^+cbNnY$aYlgDtuF%6whu5 zT)21_nWJ)6VY}$ky?b={j`W^-$1@%e#b%|>mWKtH7Fy93vyO267y$VRB2a(Cg1q}2 znZZm5S{a(}c=^;79N+2rF_kQ!JecByV2*mSYa`$}u%{jt8?Zure*{=jHF^_kWGN{r zbo=%z(`{+w*!Y??C9`3OoZK3_szihLE`66~bHWV7Yau^;$L|Rh5sD$4Q-oPdG|MR- z3vw*me({cX7h^}Yph&vQlx5xQ)bq$!FP?&$!#mu*InE$D;%|!(t0x%;3Fi!3j3{!O zX9VxMzBY>#pWkwX=gA89gVz{Kmul=tJYD?rG-0 zPe7;%!0{7O-L)9#KCZCZsb1?}Boy-VXAV$@mz=jrFO>bfdhQ%Ac)u!@viw5#aq9|C zaeTH!F$XJj32Y8ut)D+{;gyp^&ApOjjj>}`E`m0L$ZJ^s``<1L=)FcI`S|loR)~Pt zLql_rC-5zW?AbgPbPTVnE3BPG8?dlGM9$AU+F6W=&(^_acg};xA0E{nR3%Vriqg?t z{e%6z+TZiD>RAThtQxtSh5pFKRt@Rs2xm+Wi(k3P_sNSM6$mt-1u!T=k1W52-yXzC7@SomxodxV-oVL0fmpno~_e#Md>VswjIOd~S zp^YM_6j+rSLD5YlX$~h}0IoIPSzqMs#AERR2w8*JpziKe-fr$^R_}#D4mikX&CDbl z^_~|gDqrOm85#^f*`c^Xvk4Lf`#Vwt_Nl7={J32hG%|2h?%oMmPz%XNZR)jk9-N<` zxmW7_Q70LSftRF~WL)VqQ~(oIlu-;_i=5FsOC9J)Cw1*BJwfT_g-F}D7wd`|w;aZQ z1O=c@@F$-dw!TqlRf$D<-$s~~++@Jlon3x1TImx~O^(IUu7d<;DC8*3Sr&;`>1 zD92DtgSpb}mGEg_nmI-0QuAR^K33Q|OZ_*?!7LBRv69B6$%9uu-Jv=v;~0v4-9x!| zO!mo>?PwjN3#%fb<&+?yGkjs;0DJz)XWy%fj1OPFd`b9v{86BR?gx;l)I&0r53SA* zpmz^AEl2%#?h>A>hERYvk6I^*i@#4Ax+BYtCiI7^PTP7dlSUb(&*1_4uh{ZS<}k}~ z6Mxj|@)j~PyFEBFv$k&d56)v~EHCdgyNbW!&Gcf{K+&pAl3mMf>HcnzU?lH`hk)Z) zU~^~Y6$WyuSf&`PRK}2L(592#bzh^)eftCRNma9P$?kPhcXuCX9l-T-8dGFOIXf$= zcD`HI)<6~2K$d6fHR;>HyvN?spFhe_$Htee_xB`wt8mq-i!Pl64l)b0Mfps7bHT)W(-=~xY^29T*Q zw*;O&&qt1wd^sn2`FZ>SiF~q{N`N@KBv+$KBW>LtY3FP}SR(FjldTxx6) zcqTXo%YYXGNj%p!;u2FLRtH#ElG4CVI3X{3=2i(aH7Z`!wBO=ut1qd9gjLv&m1i4> z_Km<<@7%KW`4b#UV?&iH{Tpk>;0q)Aru_$?dBF{^Tr+7?4_Kw=XO@aaL@4rkB%!zlG3d5%!D$tRrY^j{crXE1P zNZgjXW?#i`(TEMmk6$-2Ne#-({1WCr?rl@bC82k&Ortd5pgmck9;`Mf83q<%bo||b z#V2@j*yj&Si*q`{!Ja-s!d+V&gU|QXGc$i8J0pI&K?6a@MHb~cSBw|EzR8QtV)Ivb ze}Z(1Ke@<EFsO!sld1w%XKZdeEKivJ%Z)(1S9C9 z(M#f-@2t?3?D4~)$hMw>QVQh4{n$xLz^LsZ=J`urhwW?x>@I()-6o`B_IB0=tko+7 zyHJ6n)bFbb78VLROP3udcE>eoxd|xh7ueTY)7M8kVq4x@tDS2$iFx{WB3Hq_c~XiYBhLl{#93C3 zLcvK8*kL4T1KT<;;b}lI&N_hjF!i+5C&hxe<(h`Co>Mna!vMUGWIANR(K z9iO`1$v@lDa!KvnxvROZ?Oqo{fyzG(eS()h(h+Phs0O+4M)(b)> zPm&xM?mJALTMoU6N2bTmIWqnpyI&KCAS{334q59y5KQ$YiDI-S$EplyJ>XESV1`7z zuTbU}zRO!sclL*3MLp1Cvm_$hgk*!%p4rNeX!3H3^&h&c@-_47_fK}^d@;T`o6mDB zC(k}wP{f|H@;ZSpGv`r+{LHJ5f7vqkU+LRp(Pb0JO+gLEGzuSyd=WZ%>Xa=AYD5@~QL{_mVb_pXkDw!; z?|Ayc1G;d*m(&2AP~7AgIH0@U*f^$Y(C+)SIg!-Cp(EIUbloDOU*58177;WVLf3f? z>F9bg=bMGW=rsctY0FauOw(E_*-slMCMl0@kC{_HaN>{9fs@ z8_2sX2^uy88nHkkR*&0t1e3}72cAB5p#fTvEC<|($E}6RqCWAp)G5xqblXlDt?Re;xwFocdk|H0=yC@x zQuu=1F1rr~*ck7$%TCcy?$fgK1n-UKbLZ|1I1LmW!`D9qfqrmwz*ac!+>ZZvdmwmZ z<~#^iwk63R%fnoQe_dFRE^cnG;$-w=N}HQgs{6=%?_!^6r^EfS?+<3A3x+epIntWbZvi) zaidim1m*qiFh{lV^$@c16NHrx6?Co236B?)>os1ccoFiA!or>dx0(TxG&Cti*^%=V z8aRbdI~H!~oRyl-Qd7~zSD@9E{)nS%?!d}cwVk+XgL|W0(V1EZQX%o5a>Bx_Loj(y zpRcj@D!(woyg;P}_opDgWHSN# zm@kd&E^j;e{Vw`Ep12pd9H8fwA<$30QwQ5Afr$nTVg z9++K~`*1q3^?{R~Oe9mNV|V`rPNCK}KeNv>{6YA=2Bp2J^fzKqA`h9`G^2|erg5*u zV4gSw?+!MIwDDr7lyRCQ?!^lVB)6}a)ZHJO7&0KIr2JOCnk7A zKt3q?LJ7XaX^cxTpgr5jbX`GK$tL1fO=0dJW!YZ-j5N*w&BHvhD%+*Iq#|3D_&U{Hr%)Ozq5cwH6)SGfwNrcd*#EQKR=`w5qu&)MnLXmR+qnD>5SO8B|73d zG}K_9Y?ZV6;?eD$A3q%2(UH1gTaQRP0(e4eJc>2*7)~`_owQG%GOY_Euc1X@g$}8W z0!qTvwX{^bmBoAZY6>3o`$9|~wQy-<(GhsCJkHwt%_az~!TlQ~UPb`Ze!YNJMa)e; zl`GDBR!4MeJ}XB(bSHIy|F*UFEPv->fcr|u^HZ19GPK#0%Vtu1z}6y!!Gx=$m6+XJ z)69gS5B6;&9Ro)E7Cn#Mo^s?M-K^2evn7?2liWO#E3{|Nd0%kmO=+;ft>!!4dX17A z5iMZ2L}j^c%H53Gf~s|W`KM8Sn*}&oYq1wFqlbzdZYC`th%O?pUl`NLPrBz<{A z+NcsX@+3rlPxXMas@)+cnL@#10Rm^S#qi&d{8@ZpKt^X9`*2Lm{&v$*r_c_S15UTZ z4A5J}XM<#_d3S3?73n=c6PJ{PY9h@|)8r8pMCKj%%&RGwdi!dgy!)dB(J_!Q;oYHNr^mZ4D0U0Kanm@2kS z*9Q+i#5sI>!awe``s5zos@m}beoX3#!cqkjsQvwDQ%f>f=6*`-cNc$}q3i@%QB`3X zRUC%W?JI9YO%Rfca=R!^oQ35|$xVbus!6r~9?$t@Us`!fhm209H_B3AfBi&Pw`Y{* z?qF3}Qb=0q_^yTAYx22O^FWZ*OI*I*9_W8%l#a@sbyL^~>r@*@rt6OGU&LdrPP`Ap zoarDt0`EVOhk1f#Pz6x*&TcjyJ3OmodAe%_S3Xkb!STUYzJI=@q3g-FYv(tTl>OwT zc0-R^$qYM_nj!uD@Jqmgqp;d8Q~_>y#Qe@XQeUPi?-CtUV7EERoM^xn3WTmnk#wQX z+D_zNRTLxF8U~`iU!-^2F&`0-=|og{_>5Q@UHoXd018mZjml>qeoQd(Ei$O@iCCqe zcGY!%`uXC=8IL~IP6rS3M(^c({3%D4yJS;FuyxG|dhnp2VrWyrOg#OOzq9u&S9O1w z-h!P>QRVKR7H8l{R82$1Suyq_`o@O+cNXb?2OV_lijvO@sLb^ks)Q{L;YdvanqrTT z&d$!-_*J(wH`jpR3Mj)cGou6%w!|~Hc`Um4AFk~jk+kL&{$^1%c+J2|YRhNRL+M(4 z%03_7UOp=ev^TY5diHFWY`ZhXs=1?Ohw}aVX|;Rrw`$V2#?u``-lA8Q83TMf!N-TIRB3glLny1j@S3tSd0 zlPa=Abebc)qsF{ZST@S{Lm0Dz;WGDm9*f!j{G*;+=JF4u1|3|tL8`plv9CSdDaa0f z)6bQTzC9g7WycB}4gHH{Ey$FP8RiVlk~rT-S#BvMxX>Tw0i+f91ZxKd6hKy0P}D;*m&rfp zxT$vss`ts$+h2&7k7vYSz`p4GqEr9;D=w^%G(JD6c+%Du^Wu7Un5XalvBBeRs-MQf zFzG|8VpLo>QXpk65+LL~2r`SmYO&p{33>|L-I|)3Y1IKBzFza=<89tSGbjGl*&bxD zQWx;AY}r(vApr4G0}gss!(MqMCeAe%biv;1n_P#Nx9hfOVk(?=syK+^D4pTZ#Lhz-X z4ha;n`eUefWiy3kAo42cmAu%rr<~)*=g7*RVf3#V1^q|RL~5`GbQ-az4t4w;NNR{% zyW5pPzFAI)p6h$ZHth1{SXv`CXHmqg2y#p652xFm=m&XOowG4z%taWlRtQ``Xnt4mQpRC3qc$2;8OVYj$zUd5!NUfYYzdb<`xQTYux+yjBo;Ck%9J9eOU41t(BP&u8ow`j|Nkd zZq=I^sWYcRt)p=8=pF6541J)y1tff&hGIeTlrh*YqLQzP8fOd zQ&Qi!Z}&6TIs_E!t5n};Pzn9~@q@5#=ML+QXw|+v&BfdnX1UxGq1+QTN{ttgkI@aoKtET8 zB`=BTT{sju5Ce@90;S8@Rc4|(mVEoChUtF3dd1nv9}0rQ*iN;yq&2)peOYH*4{3Gc z>Lacqr*Cd-po1Adj(Xj1S!!2&NxXD@(j;9>1(`Qy=)}hSjLUDM0g*Lwf`Skg&iOv0)d?2uu6u zdCm;IP)SnVzZm+w2aS4Ixg|l^qPI!=$y)nqi)nns1&7siLEtw!nwx`lF`vmieh*XW zdA_%6r0%>Ms`QA;^}BVCU)?O?VMUMbE>o!0dz@qQYxGL8;BSVqmyZnQX;vo;_FN0B zXMXvS{rXi6oPq+mdAp>4{y37UwCiXBwms~&D$WLyKS8TGv95&I4Myq6bxg6gtTaC9 z$49B`r6aMQeKF}An|{o}?oLWH4_c^euLQzL4nT#F3P^pg@RG#WzfJiPx@Gg$A(PS# zoA$NS!S}VMWP0jrgC0qov-AVT^gTtB^C`j2&G|N*=Q~S_?>EIdeEs&BaZXOgPlR1S z;4l>I4vxEQpbVEO#sTx21=T28wo$&8OqWs;}WL(&7$qDeNm zA1wSDizmDi5kDOckepM-(gB4ho z>)Z(GA=_&KVpqGd-YcKB$}qi%=#iVeC4Ls4b$ygCGPbAjyPC{Wmm2HK2&tz}b5wP{ zJ)+;OS+cy?=M?w2i=u+_-BJ`yt(XglIk5)Dfer#zMGz%OA}KK8S7vk!K#W$tL*;Qg z(GbG9zpPO=Z(Q}RGrdZXky9QYx~xcBmFO;NHueZ8e9|1MGHvcen$`5Knl;ALBokTa zrLZ~PD(q+0?QgBVN8i4?ua=7nJSmcl6c!E}|20^yBc^8o_W(<)1^feAtg@bl^ek?q zIV%u4?)EX8>MxMGSVPZF8})8vn1J4UC$^<(frpvx%Ew#e+f^El4zvArryT^8PqQ1I zKW~rKY}6tIlvz8wd4q&^9>Tp=KmJ7O@Thn2>24E5fj}?AgiW2}#cJajoU&>D%sgyhWD;kw0o+1HTh|UP{^3)NEydk*(iJUu$7`D?S z#os;>40XPpquj^}P=JGy{8^M0%!pBHn@M0E!q-obY91aHULopS!}iO*R-x$`)}#ha zqP#6*CihEzaA)G0v$qX;@Jo}FYCWu)K~5EAo?s-%B3CP!4r8+l1W9MX@l3ChN_sEg z$FfN8Bq&hu?d=!(@2TGOGczIh=7WZ>FCcEZSP>@P?)ZJB#h~EeiSb`c4RnA#5BFe> ze=TB87|^kmlhN(=x1E}J7f}#w!%Z=~`$KGS<8d)r;kVf!C8PIlget=uHDQafx)b5A z@@R@u#3;~QG^b4S;Dl^!(f1MuCOD`GXA!`jk}Tc$x@W>BX&`GqE`VvW^`Wk)2-y9WaodtH|{H*Hl; z+vjVUIjZl}*OAHL8yAu_v(WhgK;DoL!Lsy zmk|c&-E%Q#Z70ai8W=>#e@Vd5KNDpaD1co#z;9p+63Bs0Q53&anrsu{A;;kFSHZoh zB+}{0)44i?Qnwkin+kQj=WXL4^j+`fOr1^|I=@n)S%@OM>3Z1s686|hrHrRl)t5CklY)|77E5z zE_I2dXt(&b&6o14HOoPOY)>4bwnJmEq|~ z`|+(aZDW#}-sR(O)R3O8EFgZ{fN@xGq@!Qwx=BaiI z1#wUS8+A8%u&Ig*=uiq+NGtGW`^8A!7_Nm^bih)7Tug_9m zP}E>E=*>idTr~Oqa+5P*!&WSs?o8M?(^VU3{F5*J zOxNS-zSLy~tU`Nx*%z~lmd2*})ck#apND?WyE14LK%g;e{O-ZY*8HpbBB2HwNy4wV zJYQ?xxkGVHP%wwhQ*P-2!~bn~BTo^MOQ{~Rn+p0yF*Pq_w4oU6v!0Hb^6$>|Sveu@ z*s6jRhoZatWM)!Z+-V1uU*rUX$5L^3ZtvE(C-d%ttR7}>BhY7Xd+#CRBT5lU19WMZ zy&@cO27cUtSpn6?#tzkc_b|P3(^br~!M2Rxh!zz~j{O^v5Q~bxX1pX-+hTYNaWqyq zSRfURd|1ixsr?A;X%b^C>6Pzd_i3c^r7;sn&p&y}D=10oCO`gSgNt-;1tqw5Y<1I2 zqJB{N`}op^wbNqGZsUw40rYDLRvF_W zKTX1rv9$7K`bCw=#v)MS{@&M5(9C&pWoELIR_Rde?={DacLl|(x273)>eaN)!zM`SMoOiaQ%`f=Z z6>2#pKWWf&3waKutMe^p{1@j&ARrRp_$b1`ki#p1#{^8AdAkbh3q@!0JkGJBz9Q_a zUydFR)T}{T8fj;@e1CG+cSFRlTa%Trb!U2n(EN3N;?t>il6ewk^3Ru@4VO?8@i=b8 zp0|&RR5p@h)qPQ#BB1BjjZfi6LJlarb!*2`m%f$z1kF&%YAc>xps?TD6Kt$kLwdL# ze_fVz3h5V=3;*c?$Q3weA`4#L~e2Q`mzlrbjYk zI$=uIecojKCEXiLFudoUdAc3g;Sj7vCDypuU3+h~a+-1bQpe`Z{AEH5=T#o*RxS^b z3czWORsqh?>e3HH`(2+CullB)2dgvK^GNd?Hxy=(o#q(?D_+`k=Hf6R>o`7GP&kr(C%b}ec=!)3&MhygBET;#j z;+wu_b$tNTgEY`zK500Td#;5DQ5=ap>aZaX%(0*iT>S|fw?rG2iAB+ts_x)Bp8MG= zw$F=wi@tfhO>-p z&#Gy@bPyTgc^$v)E^}hO>V{KYrk7MZKfj1c^ z_t&H6xVp`^=7c(Re=~(Sesb0L(A3#bYO%z2H9AEnr(d)->*pYKghid*2Qu4LHFsXc+S3 zNovh(&`5v}=)7n~P^@}FO;#_a^;aE%rf{~5vWtyZ(gSRVvr$(4>-3%z(<`?17p@B1 zhI|A#MYX@0E~xc5iP8j(paih3%BOP+&5?F%Z^Yd@GSO6Xfs3MhuJ;@)xeMNTZFbJ4 znrD+21Pqz|8R#tdr8@2vtYjX`s-$UdTiH<9v-6Ol?5U#eveQ?mo>bd*_*|VJ?47Ie$8KnH|DG3htGVwT z_!Mk>v3t zZSP=5nL8sMW=;%wukIZKz9gRqjShMb(UHprn6;$@8CI*R`FpaN@9B5B@okNA0Qlv15Bx$2NqO#L@`AacL>!*wVuwr5FL#wkP$zk)2;4r(sj^ zZ&`bOF|ltF!YfyrYVJ!qMVMn05y92H)){p{`9^W>wus+G32*1=kpxdn#sbh+4i;w0Cy?QQ`wmNvQz&fzzV6WZA~SJyGiq1i%N3V-Z1^>CTUg=sk>|19b1g_ zHHjSOm%rJ(Q9)rm@}<<~4znHOtAS3JpnI-9LGf3eb8XmRB0<^&39!IGpEdR-<=IJ0 zvDF=>H9C4>Ejh7S_0T1{-z2G^a%GIeoGHmoG9xkAW>{KmPwbN*K3Kh4`M%(S)lHg& za3z#r-6&ds>PGglxs5(JhvJRp(xVW4QQEKvACMuK{E9nn_cyye4fP}o6i{V21!fZV zXYRE*TNznJ*)L0VhK01ExEn>)L_a&SI40?B73x_Cb1@e1-@fg{i?0v$%@vbqQk`n@ z0f+s75fYFI&FCqYZ65<-3VM{VYYfy$Y`~JSoGg)!9&2S%2(>0v1)j0e-Aq=o<;0$e zMFmXwMyzmn_M_j`oc)HgsM~^T;Vh3hvTCpC$w}o)WOF3;bKuZ==A*uln<}5>ThAGv zDUwy8JIRt9tMiV2J}2(59ZjjC?boknFf}Zt@b_PEA}slchHW74hwD_z*Cd=W4C|r0 zO%m2bp)WAMbA31oA-eQSX7Wszaj3jV!)Lo!o>q7glcQnQUsOf>atSmp`K8*5!fro! zV^nW`=Aza!3_xVu@J@Rdw@`hcOt`n;%fHXZ9!_=j@0JTAq11~7X>)Tan&Z{wZfjym zgIGzE?LB+v`KzgYTKkwxSbYTJ6vKr z=T0bJ{;1DA7H~vw(Un4PX@J2^GOOhW7YW<3G0rPzL?S}oMe`64@3U>%SY#sAHL{&g zYExR?^Dgo&43$+Rg9&EIpy4w}e@}_^AF#7vjZHrCJs^Jv4PL(=5 zzR$!fx#AAarlbaJLC>D?<*poYV)RBmu}xqHR+|a%;=vb9Jr9yH*(2L6J<)M8QS7rR zzW&bk-YIgX(ab1RBV{_;aQialy|B-%?`JQ#q&dh^S85pb}YjkDmL^&!x~*O`gkcEZd!F2FG6sOzusn4yXrR zzU-O~GO;w?f~36DAobr3Ov&u};Ne5y5DSb%YO9!mYQlRo%S1zL&rUKdYI3KOW;vQH ztOg#lf|K_$xv2BrUO~TnDHsypp;bQ{2=0zGhb0W$%bqR3KY7yjnAtjYl3E;ZVsuid zqaml0F714MT!A?kNU`M^CI@}-P}gqUClyU?M*RHSvL8lk#TNTS>d7Q`FGd|OD@O+!9ADu3 z%~say?8^*WUpy9?*lI`Eadhq+!a=-<0L)nWq{GCn z4OeV$?~OE&q#QbLiuKyEJFA}`F2A%W0iYi1#cU4^C79L|*JD~j*KAd}dni{lwbOM` zr)H4%5A?!!cOQZBk4nB0a!_!71Elh^&sD_V);#t1cF+hLW^M4QKMf{sFc@983ygfO zA8ye@0%8{cOiP8Qmpn~}DLyv#lhI7&&PDvRAsCfr>g>N6P9TdOk*d>f5UtZ@FuD;L z5oT~Sw%>02hqNq6C8As_!R~o~|DK%Q?vsVHas;@IfT_A*!G&p$)iz*0U=E<<)=Vod zHH&5KD%&H-3W-{y(@&CYyPrE}1&N|a9{j!O;vpx3#bK0vnelPcU{eTm%yNEQs;YP@ z<5z#+e0oi9Z%+E#TMD6|M;MDfsVKQkl@QzEc>m+pghU#)@~}s0X>=@)sB=((_gJ1( zR6OFjfFp5}w>f`4G1p5}nPG-1h;IeYsAp!(<|D#NKNi(>N%mu0=LwKxit6ht zPfJIWPET8*to%JNRIT8Pe`781&142|O11JBVa|zHFaZ|d6-vH~WEQ&QY;2+tpuJ~s zaQgVK8^4b)ys)>2zn}0wSmy*wWB`?l6D=nX1(gWd1O|G_D{@yFAYVLtfq_I-rd;_h z!c+b?YH%o5Ps(Bn8|fxq)4MSh==<`ypW>0ZuME1W+r$LMr#idC z;-wn0xw7s&*||n9yHAuIBx*;F6oN)Oj+7-3P&Cw5=N>qg!;>LK1>Du}{kBgYvW-%- zc{|eL)q~STNWOhD1kjQj^fwV-+bTIKDm?yv?G4Dh{rmN%D3Q8s%FJj5ja*jxdm{7~ z$}e{s5O5GPn+nRxN|Kx88a!p*z)gEN%&YZDC0i4+R^j{EEn&Cit#hT>PKsF4?y~}@ zuY=(DfwwAXcooIgK%@aChf%g{kw8B&)sHn#C+8XD8$ZeT@}*O7HN5+w2!DQP9WsvX z?xBK&0ZPwn6nkn9G6&f$$5DEejgtw9SdvyzW3T^u1WxSK?p8g{ha#v40oDf32Yb~< z%o#94(Hf%Znl)o;<7F&y@isppah$8Z56aj{qkiFHhHP-@B8xpqf6|J4wGerhpjM)NP}fvbynzy_xtEX zsqe@X@^QAWt%_80zJ`Av4%;a(UEw z8K1wBqDKal%^{O@AGdo{#2i!r{E(@bgjb3z)TnAQP-o&0473Z4dzJLxEl2jhw*YmL z(!ifA%0*xHChi@3W@c+>bo5y52S}09y-MKkmo-%XQX_zPorF5Wrb2bBXrm0Dtp)-g zys85Ns^sLUf!yRaMTb+_1fXkSNLAU48uYeBy`AE8KSUK29NsM6+spet*=J$=bTHSvpza+ zhV|dx*?|2=o6A$)CkBk>VKn&blk!3$BNSm_O#jrF9S8-%kEv%L*lW~PqgZ!4t3QJ7 z?lD(Q4mvRT%Lz0<=^e6~=$Rfe*9Jc0`$ZdCPP~V2!sPQGn|z`KHfGI$LkJT%zJo`) zfQ|--lpZb?eE5=59qqb0?y? z!-{~2<_@O;64Bg=XzoNbcOsfQ5zU>5=1xR&C!)C%(cFiKXzoNbcOsfQ5zU>5=1xR& zC!)C%(cFn>?nE?qBAPo9&7Fwm4m~BJxf9XciD>RbGRb zGXzoNbcOsfQ5zU>5=1xR&C!)C%(cFn>?nE?qBAPo9 z&7FwmPDFDjqPY{%+=*!JL^O9InmgQRl!)d|L~|#ixf9Xc<%wwSL^O9InmZBAorva6 zL~|#ixf9XciD>RbGXzoNbcOsfQ5zU>5=1xR&C!)C% z(cFn>?nE?qBAPo9&7FwmPDFDjqPY{%+=*!JL^O9InmYiNj3lDD6Vcp>Xzu@D?1^aZ zL^O9InmZBAorva6L~|#ixf9XciD>RbGRbG#(K*dW%#f#d>NE0fB_)bQ@r@T0fttGz}0iDY@jriFA$;tdS8_Rrj z`Q60}`F;wL@{-Bh9=Ei|3~rAV7?_LXn_D(vZn3@lc69%y(#EmYr%yj9cF4`_R2Foc z2@uQT4VqD`E+1Gs#l!di^G`;_+s+FK&CvdV%Aiz&w`8AhgT-BC$EutAcgpyt`B!YG z0!A+k*-~&Ec=aWwRdereL`5Ec_!vnc_+o%R##{|h4Y;rx@aGv)91;T;{o(&<7KOC3 zyS|VcIxVp{ZM*UjLMIfV2hZlS#UAW=_LjVCoOYWE2tZ3sJwVeP-mW2cloVnI*Woxq zXglbC4)jZv2C@Oy;kso|e(G$M_SYFwn)Y9DT~TLR4`MhQda$?m#_PxCxO4ng#pl*m z$uAs-OPt~V1%fJ6D4@finqRU*fg4}`5iG|<3K91I8&Kld4Q1Qiz{&XRN{3OgrNkoi zwa3!#bJ8xG6G3@DBjcToaS%f~FQCe!j%<|^R zlXJ$Vrnf1go;*R=b!VQL3}sRtdG}IM1!G$1ueC7P_1x4PH~nI9=;Hga)MSOet(kyJ zt*^X3Qf_-8m;I@sY_udbFN=&GpP6iB!-)gcqGf4DZk+-M*g z=yD7q5WZkybC=+?yCy;b!52JN8C74v%EozFO7Y= zJJ)MceWG1{o7%I6u1+kc zOm{Doe5w-It@(+(ry?dtH~!ULHK2O$iTc4t@%5I}#xS=1v!|_go$P)O4>iOdxfK2Q z_>Gvv#4f%C)ne}TMD14hX+_EQLr!1gfaRgc8lKWaF))XTg?1YM6O7L+|H~IFA4s#k z9B5n8*RqcM61x>BJDYj5A@Pil=I!}+b}Q3kG3)CFF0)ydmDD>m~^xIRl+g`Pt9Cl4*_IF6IPW*y6 zh7H(SsZSO4e>1VZcx!K)t+DkbHye|{X%>EdQVkcE&xD!lWE5cmVmXNtCU;<_5^6l8 z1dseTa>gB);~=cceN3no397)<0zH$+5+s7LMN1ayt&>|C2F9u_ii#Rq@C%tb_ZKu-|Jx_`EE!B8yo_1$6c7}7$W}1(r|ouU9%(|a zb|gIG!Y3tWM?F416S=jr#5(h8MSlY65rCM+qM;o&x7jo_TB=Jo(!fb5(6KrY!u`JB7(0_YchGHZj?Y{++8+<O{?Bz@AS z;quKrTaP8Y@M^R;Tmn8eQV8Pt^;dv1{AP|^ZHSh#jE?H z_d~6lzaY4vPPTooiFjtrl~qx@)&%m5q9U^fyD&al(z-SKNMR|M?Umq{`+4k8{F=Y8 zgCD#i4k!ALIG9=Ns4cwdWmx1jw#xYEofj1ERQ*2I`K#-tR_W3l^Ntk~KEiWejIzRE_xxxG4JqQU@&OMHK72Q!qgTO* z|F+s^55UqwabPjQQbyac*uV3t%bd)bT55Wrp#RFP+tFpz%P=xop*+-jl5HKu4MBbr z@s!&(i==f&Zusi!B@|uA++O)S6l0H*8wSi_%nGj+He4L$6l3*eIf#NcU@nt!qOdB505#QQa2{5V5YT)^SdGJmQ>)2X&==WUz^e;Kk zY|vNlfBVjsQ7esPl3tDuo>ZCj6!WiN?sogT<&D|;)l8h@2p*XdoM2AAh}`~Ko>}^I zt4`rlL({8Y-cC=CJ~;B)Kvp$tdp=po{K2uE5+O_qPnor_UN>p&QF~Z$GL+%h|0)Zl zI1tDSn1d`ZwGg%mmYu)aPKX?)OyVJst$%Cam;06Tn)0pvJB-_bnA4|OLy+8j({1JY z7XDuG1l?#^0x9;^=8D^Vf5V-@G-<~RE%EQJxAb;rHKX&ixkt+1nDlj~-y+!bb#^Ko z9B@l$oH=vdR5#|2O6tnNK~eQ6!|@C5Ye-F(Wkg0=TIUHR`pp9yv4?`7pPqu50DS20!6^$!*jqX1jtg6%v-2;>V zy$(%KPVjIOI#@$tjAmZF-tERQ8D3x#eEaR`b8h-QZ1MtPNVFCu&vU#-zOl&GGULrG zxv|NLgYkKZ%d1n#Kku$YzL5q?_c->Sgwwkmj{W98iT~gHJc}n;iQTHynF|~n_I)OM zu)7qgpX7gPH%xMY%^r$;n?}0Vp8WRX#boyv@~cMfgxXuJ>}T#}{?Q%Tty!#2!DqQ+ zof4C*SwbjAjz>ppMfz9t><;Rk`Hzx&V2TJ_uz+Oj!E7W4_GM2rJY`+`K#~GEYk<{s zD8GL`&Q9|`?F~|9jfwj6$J?b{?hzH;Dip`{?$W!hmFL#MUxJQq!PIA1JOJQrLlC}UgOZ^Mk@<@*`$|I}xs*`!hVMr?nOs{>5CCJZ>^D_B$P9K7Wl zSHWC;87#xu!%eF>9`FVa3Xt`t9u~IGKd``l=-WgZ<;<>;zRR{Rk(~%NjL{WcWE{eQ zEw~85bK1XV32!u$^3J~12+5fiPGj8G7OlHWFQ5{LJfdu|Rk(wwNT0ZFd}#wE?mQk4 zg&?gGKBvnqVJ(i1nVb}j4h}AFZ4{VL9XD3j&?p%@93R+=sejd@u{YRJ15ejLfxdNr z<$pE`%;9@5hkvO4ZUJlg3CjlJ9WOI6Yo|mDnLgNCp6V~~Gbq8`r{;$0{3j>^^_FMG z+LLc>-80r;cJf4 zxXi&l`%gNX$+1ABPW+p=ul#=n=NYh~52uPJ{vWCKhom+@ffk3PKC5p#!!4$fzMcU1 z*V@8@H`}mU&%&+iDBl9xE$Az2lJuK;Iup)u27doEZpBT0W6M43jEdb<%??sRzSp#s zWZ#M1haM@^+hZY-LQ(9HP&dpitTqr<+7{?cp603lhyRv6_h2tXxKyELZ~B@fX0#?q z5;gfe{t_yK?39#X0!~kjn}?J_D(2f$naKYBOB&kR@svB{98g9xdh-j{ySaH5r2mOJ z^w0hIwzdFf_3#QuE2ihq4=~X&2dd1>7Ea1Hf-zmF9Vq3NPb;l`j)N2x_y57kn}9?4 zw|(Q+7#UI4M3j+Alq{8KVWuLLR4OUirA0+5S!WDUw$e%^OH*0Pn(PKy+7YsZv1G~8 zSjRBidtUUr@8|#g-{&}<`#t73{Km}qb)DbybAHaxxm?4dT@p&^{==7!P^CE{@fYu) zE|b6_-$O7;7XkTZ0Qan|e-Cu;TEI0hsu8^Ksqdddjgdpjs}SOnR|!uN4;+c2j#RHd zv!|#hf|j79*h|`(`*smXq(B~dsm!Ag$kV%CTg`0BbTp32n>-@J6xBgahZ6YjE*<3! ziOQyo^_U-hAi_pMF3(^L28i@#RzXr4FE%N~d)LEAviW3DV2$|end=nsE7AEiz8{vK z7cnjG4?Fba%x*(BZRGGnlnYP@UW3{O6cY7fD275^j)k0u5kg){V~GuL`A$a{Axl7G z044ywaQRd)QuWh{i{uMONS)&2&$d>7z+@zxxwf+?c0r7++UEBTp34*4Vf;-(L)eQj8`pIp>KB&MKhb-Ic& zlF)o1iGt`WfG8R@a5K;l8ceV$=zn(bY)wzg`JY%HkxU2l;`;Xcl z$XQ7h+&>C9MVvOHutCte2PCX2;Xim)cLIB?l%Pi#wLQzh#78B4iJpG;SZ7Gy%vyi{ zaQ}sY&AcIxCp*Uy4z^oLL?~N?Y%Sg4A~jnow|Rh=X;-EnV8TIuiTK4{`k7#P%2r0! zYE<8Cvd$v7yxc*O^JRp4zwuaV8ZX#RVy`Bj_wFsneDfBbO+ypk@|d|k1PeoU!2*!s z4>gCeE+y>nlb7!EbVs7}T(ymE21|h+r!GMc{I3h(z|Z-i&_gL@i5-GI;4R4!O(7)o z2@avXqzy6rT*v+yhKn?Ziw1ZvN9*=Gzy)-g(j8cl7#r3oj!fFo=~T# z1vh0se!j%wY}%IeCpLWYQrW1m^Yzz*AFcOO*|x2~uvv2$skI>Qi#t_3yG;A+H&f~> zr<>wk|=lN3ANh^wM{>G zCypIAP;toAhvbu+Uw64wys0G4tcWDLzUq|I&8LBJ3pJlAEQ*?ye5HlzDb-3yLR?h& zmm=U<l%#2GFCbRSwh`#gS>|WQ!W!X6 zA3dVWm_%J)h!<@AxxMnqS>6NH4J%Y5D%G$7`>+~H&{R|Rji3}_kZGD(T`kJtmGF{pyiY;k8BW>?V z0m;d!N0EvvpFXvbu*c_Zy2QHN*f5y@kf9mv`?#ChzlKD%>qqdHa-aBoZ9EgGwrL?@ zlcKiqGUfe_sEzhjU^(gEmhU(E-@iI|=uzK#l}`Myf1hGK8=IIkU{F^(nsv^(b$aDw zZ0~3DTK})&p6_q2SUWpLDcERpX{0&8^Wo)*1Ab%`KV7%(jbMl<#j5EU<31 zI61ReGOT^2x1z**gAC;y9Ph%{kTdj6t8Koo>cZX(-Ux*OmiQP=UU)5J5?FEGow=wx z%TYx*OTom0AVNt=iCTiT3?$9~#%pnlkYdNb$e-FkJKb}ridQx(T9MqR@t_&eI{jfi z_0=nr1J9D)T{P3j`5&ySt6Q_@_S5yu35rObU}h)(@u~wQy-~`MPtv0UOXyBcISyq3 zJ{4)!)&VA5S&%cCwLCv+U~Cx(EvwD=zRrCHcqkUd>~?^4cHFcaJyrN|{+%X#m`RUO1?2lcqna z5?y`3w&a$dt}kn$LuF{32yOBsE-u4%0MqFjAogW#oNwZ2*`~Vwvq$TXX_IC(GOzv4 z4I&(RKq{H-cdelxdSmnEc-pZGO)2p;9(=NeMZ^*wcisBvH8;gb)i0-sjHpQ+UD}4h z!B`n{^FzTWE=H)T=9RTMlpDk==lt2+Bu8s-rEkAf^irHtX!>R)Rl*SoevjN82dnJ} zatLP{GUvSSCo2$%TNtHYz)C4u3t=%5(Q|+UF$C48B!(q^as)o)+99L#LulQFHZi^* z4auhqa>nrAj68el8`OLIwL?5tDW1iS0T-Uvnu{!60y zuNwYN+s@S!0bUkUJnpy&KxigR*t-i$86CiQRo~IbJXc2x)plGOY-pQ@TfB{^ZpEUU z$o=EhWqT;A<|x00z2Ut0!u-XUp=XG+BW?S zH(7N9H#E4?1?XQFTAcA8)M?k)`?Fnth03xHEG(Fu^~$c2FbkYJ6A1_vWO7c|Vv&tO zVBM@DmbhGqQi>wv&^$u!s-a{!Er)b#A%ZBPNT}Q_MJNZLyFe|mI4|Y<`@}g8=3z5d zy#-UMvn8eekj2N6PL`-uH=U;VXN#$d^BbL=c)8+j?BQxdmgXo@t81XdXiK)VJQEy? zJ=aN5Ke)9wOu0TPc6JR})+UKlYT%o^-3I#TDoWjaZDcis zzA+l>XnPKb;R#xHa3e5Em__ivlFII z-yRp_772;9t>h5t-^br2CC@xpRb@SEnf6$1(~R41_|s+6=hfVjnb%wECCtQ74y%Q} z-oNbXLVeTlpS|p!zR;7{pg;>;osY9Bz)>QX>Rf-le@e7FV8Jv9zIAmGxJgHq_9#Je z!|KT?#Va57XI>vkuY7mO8d%*IVJY|-ys_m|^(Jc@l?S}^?1&FWvAoC)ep1`7Ukfp$ zk@3RbJg)||#H5ANXc<{ke+|#NVNkQy^oWA)IzI0idDgoAl> zcI5^{6v2EPihGm*(iiS+n`CJ*98%M+!^OlS?Oq`&h#pKMIMNuY5J8dciexVoNcTfdk|?uQCf=?t{3esQ_5&+P+(}(4>1@xUe1L$ z>5nC)0>lE=#BXX?ht$zT7pPnxmB-!E(Ropk<#Z^7$@DNxq4C~r(Q%EH*V(_un>dYi zH~q?&l94$ZR#2cB78ywhx#mY^uL(i}o1K5LW!k7aE_WKJ>2Yx6_AhG>1cf{ar1K42 zgM&MA1@T(*@rPtVU*zB3Gx~pdCo#-4K>TsUlLiB=_lgKdYBg>3`4P)UER$z8p}AMj zM#*iPl)D$OvopF+tbWL^B*i&tR8~cdgHWmcn3?o)P11x3n=@kpm9*IQi#M9{#@z1> z6Nhd2ksAiXH#I!gPD$Q3#qni_opT|ZH#D}dm*N(MRIW2RTCZtTK%m`PWcE2~J)gV@ zaAs+mypXf~$9c~8KVjj}zTPC+YxdX9D`NA~l6nK!mkP5n^g(&{ex70nP=zvpChbxL zQR+vj49IL7ira10XCMITnv)U^n0+i6&7Y{@-$8r3YI1_!y?l~MB5WR)v8&*UM@t}$oEAj~5kkmmFjUfMZVJLc!}gsWgsmMy?1R3L8S{Vo_@O5h zTUUHsV)5Zb*fM6 z=xJ|G%$Nz6)1>=k<;~zEI+|`cE5L&n%jP}!1tsFP5Xd#1jQ@Tby$4Yc_(3aB>C|7> zqIpWyqxFJ-L8r>&3#QKaJTp8JplVa-D4CsylgmVL(9uc6oy+ znR8}(ZVWzDiwchSuitQT+!XDn?qv>5r%wY^N4q`|2pkIZA>OKL#;teAl#-n6CpcR@ z@MIP|>Ry)ZF}-f=^H5`tMa(KO=j0_ak5Y}mQpY=Hya@+{(&~6U+FqrOrm6qTJ~;UM z(!UV>fHsA|f1Yz!jLK#^1d#wvLLb6tSF`tTE=81~r->%pxun!KVxiKBanH#A_L7Nt zHi9jEtnJM8el&pji3@~zPiTA`oi)}U3=MjtW=#$Z7!4N*=rdukjh^5K|1-(FB z_afprZ^9Az;Gz43#P91~U^6KDYgxTraPY?eZqpnldH;mnmH)!G4+-;v0tp6z@Gn^R zy5P0^v@qhpX{QzeYtbss|4?uIk-!P6uss~HEoC>%ZOZ*@daC0#w6=CgqDkXuK&zNoSv>tG;s?k@`q3Hc(=%N#}7~U5@j-4%_k%QC|-mN=|z1I7iGs z7qk5&XY|463Q^rOnGnTMX+`9w_)^|fB6}kDmC=17!K=5h)ucu~pwBx2r6^CO18li%Qt_`m)R|LbD(S4pRm zt$oufo!CD+t`_c_Mn+SF*_60pr3`2D#O9n-ljUv|mC6JCM{R7@N|7))3U6o$BtnPw z0@;dS$+ghakdd_rRXeI7Gt@*|eSLU9Dum4C`4+Jp+y|4Lp`@KaW{siFFAlEUAH<*e z92!PEBo02IdMlnV zKU#m4`u5sWus@JLqmZ(`0y~lz8s~RCsQD);DI)3*mYjm4w6@j+$1_~BIJMcAFse+Yu0At5Z^GYrk{L?>h6Q#N;x}1WEkV6w3;j*`K)FiMy+&pj)B= zhKH%b+^Mp%w>#tgmQZwfa)i5Mg6=wE33C!>m*eDp)|W;U%}(gSD02bzWzu2wC)zee zZv86vew%QtsMy_5Ou0`P8#ZH0Vd%bu=k*zp(1FHc;(4o0O1n)({LZs>O7_RdcMo zoqA9zopD~K#wK_FY9ERp5UvAb@r8XpXcHs^fpq6C$m*X`KJ)VXV7ZK=O0vP z83!lP=1v4HZRa0`uoNL_=Er`xkVRCun6~-J3dGr&7Z==$blM);{oXH>N<9OGR-Dez zCWroS8j?JJ3^6Tlcw^4xF04asJT{P!erIdndk1t60hRqN=}qFCQ@JS*4jwTz{Upv= zyf`B4`}b8wwG*=`mBwLs>+AzXmwPPl4VtiHXTDq+>t1oH;#7pyh7MlU6m^K-krvzb zqJ$L2ier0(xLW6*5EMr&La+J7iZ7#bq&~oeU2^v=#r1N?#u3Hr`i3X*afigGpGt?z zZZb4HIVY8n$$v%V3N$K{!EC7dyzS;<9LfSQDC4NdP`*PD{eK+;4n2mHf=0m|O4Fos z)$DlSu*+ep>)FP2ZJe``Or*AU)Y|O$_1D6)REk^J2L ze%=68*XYzwTcfO~*#-Fn$~taT`E75HvM|{tJiO8L_ToAM|8bSPTmMb3?k3&WOMN`j zrS{>+3pK~X`lC49u2j5COaxuMfH(2u;W(YYJ??~x%56Dy4*R4nKWXnY(uu9*%s5Z< zSqNf@2{Y_p-aY&q$I5X$&1m`Eh7YsnJUbt%$lS)0gygIBXhQmHIoJ+sQOeY7^S1wk z7Rhoz%fqxRZ$xzi>BMGTzS-&7Qii5F7WYSLC!KyWI}KP&82P0^h{r<%y*%#FEqsaD zWKl(W{mbbg8ZdOHe@gzznTh<@7ZZfN4e+(c)|1^v_K&5ury8YbPF>Y(Z&4{Ixq8dH zd&{G4te^OGwa(oZ&ol!{Xul0caf^|w{p(=-BF;%YP>$o!9*co3xpTIB*!|Bn)}eg- zFnJEqzgEttSs@B4g_S=Yn?wIN%y#~=6w1*~Dbc2-Soh$WVDMwSL*`E-#~xRs1aXeL zyDeB%17cTl=tr?qxq*@rEhl$6?G5{jr0dY}oC_9=>wk`s_dVnDzR_YcXtriOxJl56 zOQFz_*3ogSfL`v4(Ky!>nhk8a9ic&EWB1LZ$feM8eGN)#Eyv5s!d#>8puAcvXW&R? zF8eNCvk~XZ=h8W?)rL(Ea#OG2N2MnR61RlJ*StR<&NM)t`DV3dbTvUVxhb3;Y;pmD}%Y3tff|*nP!PL2J!2jfpa_74<1!&B> zaxk&K#UHjS*JFuSCx)nbp07t4wq|Dc9uxRtnIs>W#3BBi?x+-Na(=I(}Q$8)dIS1e^*jXTPtPd^t|BM@C$sIZ%t5RGTmY-jA#zCN4`BNunzhbY5> zIAh*~W&RPB(ivcV|9r+t52Pifq5 z6Yl-VsrIPgq{WDV+=?r~migmqk{t4{Kf3n#(oJ9axaWtSkYv@WOeWciIsP!zl{Bz? zY@#Mj!pDPA7|hT|4`<>@Z+8TcX4Xh4-4V$vVRz!!4oql+2Wyowyr-a?#x<}HG~>n( z6jp|#!%+NYN7#HSK7@wKhq+$oKaqt}iaRUT4PynbgyB8Yi;wGb zGWJiFD!SXwgpQjmc+Z=88Eq>f$85@Wel(7aBDiwz(=b>|quTz)WaB%Mw7l!D_D=a5 zjNYeRum~m?jPJWQf+Gu2d=(JReL|Cil)8&EVi!bX0*{5rm(_ z$N#?i;9%;U>i&=3u)VVwamhV*k#l2U<~dz$Pxt`H%*3#G#-U3ix65r+ue;MGDn;1~ z6kEQE+vQ%Tc~tw1?v!C-b~tm`S3hr9Hyr2Mz#?Y3z1TLWDs!#V*|~N7x^lr;GNi*i79&G7t zYC<0y3i>pg#s;em0VQ}Rr|Pi2zNW7wVQ)I>>z8<(M+By_OuwB1DtTINSZy}+RF=BA7`tzheKdR}?lJvG* z(`))IBP&4LT-hc9qCx0R)2>g(5^mbu~Ptz!N1ztLns91Uw_&Q${2Zk&w9;B@kgGT>Cg@6})u# zr7$8$*@eCfPY?mbF2MfW!1eTq>S-xFuSnfK2-Jlsi)uVL_TzrQz1G89pTXGjC7A zm_zk#?nW67h0zs3C@}P$Y@)e@<{=1CxOj_TfXcC7yY*LoB_D|l*?1kD2SH6Ww$`My41V#CP*$|5dj<2H(MW}IB9tWvD= zx8=RFQ(aAs7O)83s9l{@wMS->=tR&oG4T;$d$D^Rg+!ZhzBS0@ehZ_Y;|t2CF#2?Z zlh>p4t~PX)^T^U6-d(e$^cI2$W9Y^EaQ5P+rrpzWxeBC3bR4g6abaMYoa2`|)D3}4 z;9BpR{FjT?-UD_k%{j~JzdB1B_4-O9c=OkPJ(O^6^C*j=`3{MfmycayLvTHLa=)jw z7K|*t-zpnsPFa z>5?S9-erez(eP~8Q`m~U2w_&#+=b5)Ye@Ah=j(!jGjYp z!~ggPlH$nhf)keB8KbjaK1|=k&7vpLk;~&BLtzY+eKvI{?I?l$tR{YrSx1Xl{Kat7I!6`=h(1d408L7gwUvX7Op=$xl&*Okhz8IqEv3z#Z2&d7* zi2W&Oo#3s>T|$(JgUd9A8QJfyXI&QaS|lU;aW+BVXLx2vC*z zh&oYk7b2|tuSlGOsxu1JPGO8v+kCM7gUF=Y5SWxzf|SeX2mX0ayNdcQcmc&Hyk!qF zE@|>`xUv*QB70uGbE8L`+h@C9~c1eBd4Vq2z@`_8H zoJc`U{T|ll`K*tVnca`JCj^&bI+>zI>_MMn!eYHalU=Ul)kS1meebqaRy1BH0cnor zy;=-tjJUm3$T2)qc?Db{Vhrv=-SE$qIfl=>YxznfP+`u5`S-%Lm*z;q)n5)oBwYTR z8=040eATiQzOkqYM@~m-olo=2FP!(L=h5io-l)0mXX%fKrfh0PG}cubev@;MC;`PDK|UYX%DS zc)us_>}8j_((L%-Yhu*cvR0;Fy_91*)&$rPU)DSK9dK_n|1^bJ66AEFmI}1p`NN~+ z1(=)HC$Ho3OkVGBOXGgURUO_LLTlh4^<{7w8Y2X=c@D4k!w zz@gevH?(;HGBHsyS(_W!H0^qIVrkdxRO42}*E~-g5e;9srxOeJF1YNlZ@U=2MSfA0 zoZ}pu{`IV|tN`(Y1hPngvJ(M%ynE;0U)_Sn*vv+I4z7_-FC z@~7J^+CAcwkzY|HGNag{cXs-ctFqqNJKMU28*|gM;?|~#@3nt$kfsSRmVEWfy_`&# z1S0Y{q`!~z`%YNppR<1?zKQ<|dY9H?dcUj6q)L>{iH!0)`0_})Xj3{q_2ugGOSS=+ zYtrk#5^Q-^y)0ob<+gtY(KaU_hS-$#6vaVr?rmB$mai+@U zH1U_J>H?Hg;`%T9YAmwPuBXv#)1zl!9r-lnzjn~llDs}8rAmZNGPAW_srY2;@wXca zP4ozNWp^OgeD^v%WlydqOx+NyI459~?Kxg56tEi{9X9e`E?ndmm^^v|et?^k{<;|fii*FCeu{wp zfIk`hAOGaf>G!LUW=$m@q{aiu`zd$+G=JB?j}etjI(bByF1v)wPKAwea|@6I$9^@v zJ~>!OHy5PX4NpvG$EhiF7Kw3~D&$RPVQITD!#))2mpA$QMM`Ro6qEg;py(K<>Fd!z zeTnUa`2L>>xQd)$Z&M z-c-ZU9h*we6;;?VV!Dr;6_?1Q5zc>K=Qdi_A9nL*88snc-|%}z{MQUWUfS5vo+U3T z_sT=GzD1EhM1nytWuYOQHC7NifJQwWPq!S31p(q}N#yQgczX9X`p=`l0DiAVPe=k$ z4uPo-Jf&PLN?W@P{hy)a5V_GtcZb2{nk_{oqXAD{PW$-+5gTsfW?m-wyOl2l&qL6Q zKclm5X6YthCB=&sRx2JAzxK>zqqWbQtQy+l%gmY@gHwy~X~X_mIp+DiB7Z_o&e1?F z`S)>)4dk)*CsfkJp@&0QAL6AwY?BrE{mY6S zDQ48PKWu0&Bvv*gr;GRy`K?W@;pJp{3&j~*CSx%y0k_hG%WbMX} zv)b~0>NO2q0(_q{MlJ^<5_4jzJPZy8q=p!VqFq(KNr19jkm8BjEnzbkGD;q(vRoP~ zi7Zz}D$v|O8J$-v`Mc3W9sPC|(W7VBUS^8ccC5a9&n?a^eaveWyC*d@TEoirG7w`Y z(f#);AV!(FGh)YfCr~~a;9-Rp5;}9{o$Lo@0vSNWW~ApO+cu0W)zRq}Ws~Y_-yG*~ z`p#2HJzPlF-nKtF*2l8GU9*piFr->--n>J#jZe;=@UtuWoY6$c4K;MgJLhbvYIx7Q|DuJWHJ=iKc7fU29{=N?HqFPLcEbyoeWW}3`liaK z81-TFp~VV_REUouiR*lC|A-Wm?^lW$ESh+zuT;n@5!tRFk#NB4q%UuRzZ%RhYK~sC zWf??Fh9n{qJP&##5Qm1v3XIYh)amc0|KHv+-Lk`=R7hS|-guc8pD6S7t3pt5@`EAK zMXAaAVy-_OpWStpyC95{wX?Ki^=Kl&+y*zW%V*MTDmyOO=Z_h)KU;U-TMe33jCV>F z&R2{xnrJ%tyIvzD<=|2#``kb$w=$Z>i4`VEc>Y=I(B>8=&FnW-!39S~zA5K2Hf^Uh zDL6S1B$FEsc1^2PFjdN!DzAL`ILA1GLU4bL#i~74n%Si_W*qgd`)nR|y%hmP+aZQcZYhX0LluV1C zc$0tfw&ELMfw40;D8-cgku5oP{+4f5bB8g!TZCiG1QMN-kfob5J802(@@%rUV*>^A zsl?BU7AJXW!v@XZ@n3fKw1{uZ-Se8qCZ~-C0!mH9L&(4P*!z`y`qUG+SS8KR)}|;; zS1QEO@WK#Vd0?s{erG7dBe(CHIdd9r3v~qI2%)}yy^9mmbS%1SO5XQ-UhTQf!%{+y z>Q$={L00)UoxkKMp*T+g1#l3Vsu74pr|9;kzlTKhoYRh!0W@P$cL)$En!{GBpbmlX zLAubAP6DBZK?)A54zX!A z;^yW(MvRU)`7$1#JDUtQANkU6g)JW4bjDXn-JFpZunn7|0c<>R2)of8$~C!k@3A)+sD1iglf~jj!A=FyzyBK!;RN zz`3I1p0aIZvwMZ)r;cOkuZ&unq8A@Yql;5cc`Loe6|}Z~4&w5*zU7l0emJL=CenuhrAP$&j)>&;?v1I@}H}Nq3a(GF*XUlYO224 z_g0k6_4}kY9a2@b+vw!UM+3XGSZ}n;RH4L>L{%jb`2e{>!U=V7lO)o86Sckul@S8O zSP6upie}M*JBWlXiWSfKa%&u5VYm$by4GEglD86Kr|r#m&-T0GaKn&`L+jhzAUd|T z$ZN1%w9!a5dE(pAvoWuR39fv<#8_{ds%E+jv;Weib`>kzcvdmxoXc^hz(}C?6H({jjcfR=R9@wJR-SWWv6sf4V!Yku~;7vDx^}F{VJBP<(=lllbKF zPXF?KuR5e2Jz5KCW>astW96S-QNuKBsj&%pwaRk8#~BgnS>t;4H*O8}|I}n48ON5) zj>VAPUEz{K%CnlJVY5NCD*UACuxWmwgy3tRaBU&S*TMfTE0y3dpvojZmIM)%zkKe< zP+FkuScO)Zi2WV_Qty>HjzNzT84sVUG6Ug!v?D*a+nweja@yTPTxqs%@zST+(C|KXxn-ZH#%#Z2#%?9%!~ z+A5U;EL{-J)u9@K{vNk`_h{bGUP?tvP}D|>0GKU`LCaA9%EmdF0Y)FTgC4<3B>pif zHFy4VdWZ;|1(3`VfSBW#8q09PvT-3z&2-70YJ$cv?k<<|bes7UBi+ic3-2>5TF&Eq z{N-QXQSouAM_j=NGf*icqi{{+tAQeo5HZw`CyouaOpv?WF&!l{Yd@VuSl4FSyMIQ- zs1@<}W^B%c7*Z3wi(sJOIbv!zV%lwmKWt{Da*%VV4vLX$Dop|l$~j_Cw;<2KTTGD3vVdet z@hU`X=?LmuqAEaIUl^G|TXuFpf9QFDza>Nj5j$}KA{qTq^LFrKo#m@B{LP}3(jiCO ze%-SOo0<3-#6=de{b|`->|F&cEL%;ES;VdR{@c2oTZ7GWq!W@G#Oh!SIZ)Swqb8iW zpOms~-?c6kL$5~%6j!fXZyfUUX_#SzqDN?CBpuF7O75N-l&ix-V^^{4;7VN^<<_S5 z#N+f;LQ|KPGHLYYJi|P)0Vj)*=+f>Y5rZwgN6m4MzRaCC!PMM*Q_TEmf~rsFRT5&9 z*Ajie#;f5@*zw*WzSO0CCfw)hVleC%>`pQ~>Gv!@#0yt91fSz+fGJa7%s;6K|7oR=0C z=Zm9^--+9L^uo~k@~N7hS2<`necN&LYjk4Xd8Mq(ba9{h()0BlQfr@GE<~A^JsL9; zl3B(Iw~x}23!4;1F!O$gAcf)ykr0H_FhuOs+dxFkBL#Z#dVmB-|Hv|{{0G63*up-nipl634Up`(JLEbc!!Uf-EPMwbk&1g-S~5+Td?H5x4vMFUi{MEHzO!TO}{gGs@$6 z-56Iu1_#rYniZ-ga=rH4IaZ&b<{}@gbW!W^4zKKi`u+*3DE;{si8fcQrKysL0C?`8 zFan;;04Vd&i@babDLr9tj0gkl^;RNzdyvwSRR{}}LZvIfw?Uwf0Po#_NT)moVc$VI z#o;^<&FDgm`l zm=*pqOrs2QUD^#zOv27NJ4-czIC!k@^Uibp<65}eKRVl+H`8Bv^pO6VG)7+i`2^`h zJIprg>H;qh4GkHk#nohGy*=mBvD%8(arm6~(GP#xX{+vk+zFd?h?%*L&C7a*=rqAr zi&=WgmSEdFZ9(F?D?4LK_rH3`^)ik)9%V&^`;AxNKL$jLC_f2{&4C2I^ z_06|Tuh~xh2o!!2K;mnNSQ$sQ{PBhn%{u{DQ5rn4Uny3R}5;3j;tfb3ami5rNENagxljUA7TJj!Nvt=or{4~*R1A2i=$eAv<>4@pD4VZKy`Y_Eizp6ekHC6R!Y z2nA5(X^ebWB>*>UK}38oM1pk%D9VW7ZUhE9nWJ}*yT&j$LDkaTkhyk+5lS`yHOH=) z#Ylw0NxH;_4ZABRe~&nnB2KQ`8fGZ`%yZ{hAsPIVAx%A{FT?EhkGxGAe%L7QHyJXj z#rBrr@air!?sYdlcRDOFj%@k*RPMpoHpQ0B*rF!?cEvp1ORcMIynnCBbRRlC_NR~$ znoB4Di~<~*HuHHY=Ba^PFT7_b2J)6{m=(@HH1wWw43C&cCShkzV<{xR)emk26_ee# ztoPqK3^Q`fS6rsZZ##B-MQU2nk(THRYf{b?lARH`g+4vp5*M28>-1pY=d@s55aY|5 zs^3D6&mn+6ngdw1SXl>D^6xxS?J-xqFQUU0p3BzG(^+8Ksi3*59WqI(Xxl{w4 zt}pEZ=(sI^U${b(hUKZBf%(DL;v>=m+bG+hK^>N@-WV2+nbb>ZxS*5=!Ng;3a^uYpX{0y75R{O%{wXy znp2z-zt^p;mTx(d*QUSHA{?Cd2aLm#Z^}zBcfBDUr3qnOM588GwnPkUj|VijgW z*rx4imYZhyMW#@s?Q3G-OU2nzCP6{ zA4(v9j?$Ek-f~Hbnc)69!>2#SqvC3f{@OHXzI9DcnOnB*e*2bVljldn_?#v94GpEk z_Z~?b#u<#1PxTm+TcSl;?d%rI#T!g4)A7xl9gk}4jY{>JI96@^_A-fhM?vA-Frz1Y z6VGUJpw4#qy@zvjW-|--Uc+8X1$+VYQHY{@c0qnSnGnw!U{=xMF|GCY)B#F3~=ZPFCfDRdQVgHBGA_a>DWvwUg!>w=9Ad;MKlvY>u3OSwxIEz403{7Pu1BXGNBR~y;Um=_i zYk{+&rHsnv%RucL;8!O63RxjzG#o*OccM)=$~pKoL-R1QclA=(X>q10rKZ-v2=rC= zk5jHPLqFa~r&^>h60R|U%Q!jJR6l;bVJ+jQnCR^zH!6Jv(U}<{3NQxVMf2BTnE$F@1P9*%2S0|Y z_n9Qza14DPD01z=voJv|EeD?hsZ+F8Axi+-qCW+MD7c`-2-?^C{$1$a-qjW9e%uT8 zuCr&wS2Kw{Q=YJf0F{qv*DO}iEc+Ci$T%hR!J(nb2=e5rOfuKIYnnpyowY$Z|pdsVa<6Sj(JZhk1z%pfDK9QR@6GxX&A< zX#>Z45U1&FQ$G=nOnsLoH7^KERf#H01DhWqvv%WXOn-dyCB zJ?dk(tZgj$(Z`{&>48k*<)iCZ^(wlX=Dfs6w# zEkWm`@t?uT2H<4k=DBHU>3A^()44K}&l{nhtqG#OT)k3*#<>EG=QCT7a7BGfPkp$N zH8Vi;F!bv*+Y9L`MRbC<^Z|CisH_HLUHA@*4btgF zXv+5F*8MM!xC%EhT!A>7?d)oB?{BIpEJ`?Ju|=+8gG6(*P%MclRTqTV72Cg+Q+GgB zXRXmZi^7-W1%RZo>r$LEQ5nh51C~LWWl^7r&RZx67M=$SOH0tE%!xc%RpjBrXKL%# z@s2)>j8z_{`Sm`s5TRl0Kt*<^+&Scbfb6FT^pu$%0sHSpwc?#Rxy&WwkYBfof7`8l^)wp&?x}9WMz=tT>@&$p3$Q-* z6`FoCC+k|h0|&}=e&fSSgw~JG70e+0s+g!>21Ec)lzen=fkJ`;>mpuIU7OVsh9kT}fE!=(?#iRhVRRN;Q(KU@?N)LgUng@vMOQuqSx+2 zKSIO_oWokjKeW{Jd}G{02g<2UJpnb22rQPmu4`^h8J@20EE~-0X<&`P&Vr6dn#d`o zjGdiXpKWB{yfI!#qt~o>C#Qzyt7;Td8GxBN4Ti)^ppnrxH#TGjfO>BNhDVkNfMZHI z=(~Wr##BiYb-?R2eg%#u14<;5TAhWDjyVx^A_~ z0XfY8MM{yFA$-9>7VIhA|A?XHOzD6TA~TQn7q5gon*zwuA5DjdwTg-@H%My@@f(S0HbW`9%!vEb4 zEPP05ICUfM{fF}it6O%huuo^MY%rcQdGAE_ncEpS6?VUF+vOV>y63YAhhQmgvu?Uf z8sV@+xW3jk22UT%xpcic=H$i}L}oG}vWCDfY6ga~d;OR>JCq*Q_5K!^b86$(6X=l9 z%C7`&ua`-F=zQJ`W<&Q@!MDJon}Ki>tSgeS=k{#c04F9!Ye#x=wVZkl8XgWekkZG6 zs2k?i3{;^nBuHugTbv=tHuN(hXhu}p0{xJDPyz@AdZvO%)ApKCR#41^wlgm)%5dV6 z%u|Pb|B{u1|Fj%2r zTi$q-@_;|RB$n>SCzYLexFxY?-RcKT?DW^Kjo=)l(+IW*qAKX~pbRT^_y#T&`r$iW z<5{b9TI- z^L0867$P)`OM=B-*iz%`=;(C6`{^RRMQ=ubbS_eUzBA2eOM^1AIa>V=nm~&U?htsM zV^s>S!jYsfS(JGS zGiJvlrEF+?sJBGevm)M|e$Xyh@OdP1!zmS&L$T@wqd^bNfgc2E^x;!E&X4xKI#SM_ z@Jdjs1!5k-%-4Gwr0k#qVE8O!I~vPghf1PwVzl6 zo9d5rTg@|H9lBIC`;>^(pW(7ZXW^&qG0{ z!}5 zymC0s_R7aKAu(FSInE?revvawi zldF^`Iyd}ApMm(ga$^BH`Z~OM&JXc684Lr5B>&?u3$w0;@DRMf7fN!Gp{&bt8Ob8)fP{8_&9*pcK~e+TV8uS z34Q6o$DE_UX<>z@$BA82?4r?f(Mq%#M@R3nNev?DXSMx#`QV@wnc#Q-LQvlA0RS#OFtza*=e4TdZY+QF=10iLU5b`9Y9Sz8z6(Y);w}?s@bY!{Es9d!3Hx zJvn7NP!d?O7$R&B68r;=Fhe{>2}lE|O9-Q6vkEbsBMd57!9(ND=msJId_sk_38*5B z5!I67GlC+BN4tBJZ~>~uURxI)zu26yGaOH_0dt;HOUREhbYEQsi{#my>kM+?=qz_? zu+c=_^6lQ&R{2LfSFlD*RdO%=R5{@?-FCGG?wp=oNQ)!bvdU#EaCl!OjtkslT|UHz zmAKWClmCaaH;;$%{~E?GO`)VANhwAuiL!=L%w%hoJ+c=`6lKY7MzWTuNcJUJDzwPH zXDNlL?8RuYG|0Zs@;le)^Szhnd;ebd^Y918xSVs|XFunC4%2G%YlTmdxTGYXW$PuI zpZP%}t&#}RpTfNws+qUL!+9^@@)m_bJv0qo^e17%op%1a?81MTfnO7i?J4BH=R23EMTB^GhBLhzW`!UyDU4;A6BQv zs;tx5!{eb|r+2onYGT=Y+Gj;u1mu8P2iXeX5av106{XbY`53wyq0Ge3JKsID=iYJ7 zO@%^ryHB<)t&>qZYlmtIbEgh&p3sV=W8H%V{ZPoNoLD3 zO+k^NziZ=y@xaU(Y1HFnyz0$ajrnXqyfrvl=fapfC!~{T@P($8}K4AOS(ll=H8$^DIS| zW-bflQAFzVo;s8RBW-tY)4TBg{_fgwZ6>PNRTqEp)wq;-^m!e-v;C01#(t!$^>Oj| zHt1HI@Mge;Z*lMWzN^K|^%zJy#z0-Q=|f0(l`HpTxSma&Kfpqg2a%0nu>$am71V$K zAun(}Fb#^(Z6s>>A2xzg9~dY2fXe3-etC7_)^pNb!Q9)#TvU7>6oUU5et5jxU;(cI zp@FMUbPT=h_>&v!I-Nr=d}%8w#5 zlZ>974eJjH-iS4eP7G%Yx4d%-reV&?2yVH*xV)-07q|*}-iuQTq_3z8Wb6 zm+T?fMg-vuM|dFf#Hu89LHZ1JX@JqhR}m&q+C%Nh+6U52;L-qssZf6Pxcz-(l~Ua4 z@DO3|| zzcH*XFui0Ea$wv%il|a3C8nbq!x(Z_Vmp=1OHS56LS^4L`OdUsX8lrl; z87&G8f{$xL!Q8GUB|e}8f^st zZ6Z~~Tq>@9EnMq^92tXweN1)DB-v9l!;_GGyopT^R{6tY+3JyU_ppa;Px1A~6Ur>N zwyTrFwG0Z*$ArB!etCb%(h=~-QNVy{d|Uaig%tm%Y2WqGx}IC<;KE&~Fo&a(M_|#3 zfj{gae96dSf#^H8)%wxaE!LX@>MTAgD7-!}n=p9)R9=i}e0W^Wi9!jfk_!%@q}tf_ zHQKMgx$KQi39m~RcZ%7aQ%qFfbk8W5@^OB0w#Kh&>z_Dzk3&Pr%ib!&t;dKwa;`&t zZx_qGj-Ydj2oCGvP(;4M9K}YY8i`U^k31`I$N?QULXWIvx)S_ugf&+mW2hvk{Xs`HL>p)na*%pG zC>FLp$ib24uQj^zBiRUC%S$kXQ7XL>@_wY+pF?rM!LsQY86Ts49i(K#{w_Ir#Tvo3 z!ZiOtZ;hlruS1y!7zctVC3aH(%FS1CN%ZPxMgw|4QxR+;zMTmLN>>h#z6dla_k8)z z9d(-C%>_Kp_+{uC2+axndc%PP$B``P-^|iHaYE;DzOlRen}soFqNH7G@_t(NIM&Pe zPsz|~459om2&BdwK9Y)h7;!h?)$kWqSPThzK%V8Q7;IXa-2}c|{u}q0?7Y5HZZ0}g z54(GINv&KMrk+KwspiN6TqM|uvKnRmYJ|{>&YBQWV277^6#j)pHfs^d3Kr?WX5aJ% zZFWkNwZ`%tAgmb*H}`;;B6JidH>da$vUKCHengOWcAKh60#v8V#P=R*ZQJ5kapUV% zC6xVnXUKTs@&4K%t~K>#zF4HfXMVG#nVFfXrH##FU?CaA{=8L{s-F|wq(yWe(UvSm z(-~fbHwF7Pk5C>&$KSu6`FWG?%JuKI+$Xx}(!0NNq4h zB=w-CU;o{NY@)F4dSvAPz*hW>9~MIw=OrWzLEI5kQFIT3(An=^4NF8C;#2?S_I{J#BGXlMGXeQgl=oHFV2Az_<$- zEV6~SZL$$3Ek~c;iWI!aw{jeD;hml3MaYM$s)S^$?`1#p(Si{zW`_5Qe3*qC_4I%47Zk5WPH3J3_~#aj?pF~;Ezd%DmZpGsZ60d8(-=y?kPZ^bYDDb?vE zgKk)iF(2gkIN4Aoyeol13GrjDuCO|Gn@aWh@-%1hHE`1z;3jFB?@x>wc?kctGb=cF zo5r%r$%ILtN7|=rSSIc!8%Ys074S*QL(wHD$|AO?RwkWCj>GI7&M|5L(=_q!Xa&chuhq6J!WwJn2A60_0r=@!v>CAx9K2`#+!z(7$b zmAV|Uft@kJAXiRJ{Z7xYO`pUTTgiw$daJ!O&J5jomj}&Qaw<=>CaajC)=}d579`vU zDW*Psra>MtI@@tB9Zr0lka(&;yE6P(W(s#FLf-9;g5Cs2z{&sIpq8EVCHU zvq(2+;~%+lbj0joK^Hohihy!NH57UusfI7PEs65+gd8y{)}DvR**qskpT}SSp&{$>$NW1719c0k=wr?|*Bxdj z{ebz7X{kr^jU$DRg+(YXwP_HC*}^r+0hM`9$=Uhio>863z#!qTUcoH+-?s86o-?uH z46L9YMVc)XTVc5{TgMU9(-S8;I{U(7oEHq)x9EKe%kei+LuMTaGwVUmqo1iVpd7@v zuSZJm{@?$mJFm?%%_-E%cT)4-h*a6A+`Z)lMr9_Mo#d!3{g?vRw&OUL$yHnB?Lr4W z#BxcD$_XLhFntMMBH=Q+peb?6g?9!zvJ_6sAgRS~!X7@1u?wS0S-y_ojUQ-_Wj1A?i3O zg2GA8ac@Y8r*SSV8X8`Kf(v6ux@!N9?pon~`VJqzWro4b=wfkpj+d^X;Wx3S56-+} zMY;{R^r3m|^|BO%)Vjddvl!r0TGDQQ(cjwNJ~%G_(bbPb>Qj`ez_sPa_OZ1779{)2 zimvA|245jca>eZJ{V^w?h(QMax;k~B{$c3=3uQuhF+HdmA(X5*bIA321U%kMdSEve5z8y3?;J4KT>rmo6~!k0-a&|&*qx$7C*_P;zCYYy zOSlS6xX(1(I2hUa&U!^3q{>18;bW1`w>vucMX8uwQ_tSGN1WSjYWfhA?GwlWR+=z( zc-v@`)#uVHk8(tr`y(Dgk1sr3J6MV^$VZqQ$MKP)7w2R#+B5SeJN)cb*Yz`*MIRjp zuTJlOu%JdKObMY!I3Nw05f~hhAj8l?uLdOQ=l}CB2PB%!AIx#Qvi~G{Zv<_W)H5}e zaAC63O5e|~8B+X--Fuojn*x$ui(ODfK0?tFwQahuK%sYP042P?b?)~;{A8l5Ae=ZLbuaT^vW6z-Qt_VH$7}fdJXx;0tsBrlY`e@CvA10?eP9Y;R~8c_Im&AyjZ;6;4?c4 z;*%ip=K3--M6v8E3d-P zzUD>T4480MlSGXjJ>}BvVG2_dwBqLP8y4~^8Ef>6+#?};(|%60#(ImV;8#mhF{?-m zQ;W=85_ry@ zz$f(pixRr#fYl~Y``c|0aTt`3exmvU@FiS74}RjoEr`nk#|5r-?MspI|{olaLL-f={M1Mvurvy-bp z`mWG!#xO>{I+cypsbf`k-4tG$N9%YJd8$jpJK>(pa#-qo3Q?7#Fhx#(F_p&+X)ekb z2yE%|8Q*`Cn5+&)02h{xEv|3D&S-1jhRuoSyv+M1#A|n#Ab`2HI9rEn8k+yW7{SX? zWc^O-{A3c&UKWKFQC?*8%Xu4KWc?OO{iz$*BpiQiYdsRxiy|!U0rTM{fgUyx(ZPJh ztrw8vu=`|X`kIRXhVTkrPz0WttU;o7u#m4fpeFyv6Yo{I5Wb!B2ZBf2cnYyLo!3RRE+1+P2ZwW8Ld-z+_xU zb0n$rR$XkVd3k3v=w4?`A-P$=x$GVN@DB`4u;lkgB9;h=2Xb?tq1G`xK7P4=3EMjz zGNb87K%(%BD5?k}s#SJS!Zqknj~ngr(WlwtotRviQ_Y9%(ze>PDEjxM-)cmn>bD@D zi7Mc>o>-5(4`)k-HA%=Y2u&0a2X=?q!ZFW4*o<7<$RdU~>{dcbBiTfD!6qe$^3UC2 z6QOs$MF?>qMBt0A+Xhba9~*=d96&jTZLasc-Yux(gcY*lDw-Q|?t-Im-_Y{%<(%OR zUM*FTv8rR{uFVRfooHS}gILqmwXo(h&ie;;aXKXV%H_|@MdM;|X!E*&h1)&O<;CHzMfqLcBf0hcHG!IKDMK0V(A^i_-3 zg&(T-earI%G7}-Ph&W(u$08C5L?8`B07epO3StNwY!HZ=qal{O54IHWS7R{?5)Oj4 zy#Yx@*B`Oi3{^fsH`zpB>|yj5ALL)kubA5? zX`HvI`wv2luX@pD_+{JC2_AW5848!k)s0rZ?)K& zqbVVl72aX{3%k@7M99!Dz&k^6c5>Q$9-r?|KWSPvR+zx)uN@qhg9?xR6<-St2>h2w zc>5^o*Lfq@s7HdJR{J@Vth!-=@O>&oZ5hwmdx18eV$&jbSdht)akq-kK+})7-9D_O z)BNIHndP`KvK8Wm0%0PUK`~ zy5{XVfvN>f-WmVgyu8h*zU^}Pu)iKU+a;i+prNfD(dFo^k{ivP zX~Ax4ru5~iJv7qx*#MO<7=JKNwYZ)kj-}6^-(R42?xSMXk+`QrrWOPG+>h8Rv|k=H zvvd6N)^JVelD~%v*GKVbp`RR6s+l1<2Q<8Zd)8n`Hc0BPIz+c?O%*$fILcssfH=FA z*-4w$BM@2K2Lp3nfF`&SS;9(VSnL-)4A}`81U5pRgt_5C788!usF_NI4O=@Py_r6Z zaqYq0q9Bn_N-Ur4FcZy|%rDpX9gwu*1;G*R#Cb9qnP-wD(yMCrUx9>bCPa?-**1e8 z%}SI_M`j}Be!trpx#{S*6}}qlK$J6;z#)nFU1YyMt|@%y@m6m`umVBJ_)P~E6b|;; zdcS>+QS+nwe(EXJ`n80N6D=J_96o)1Rr`EXg}2HM)Jd%gTuWM*z*KB=3{J?8vpD^0 z{*L5Fx#>!;`b38o*|M$a=!{x65ochNGqOk>YgXTigGOkhqxyI%m<=*jkiA2V&R;i7 zqZbWS{)a!JVTQ2153Gnwzq2&EYK6NP3yt{jzzxdBe=V*0goUQj*1KNl=y@~VV6sy%RQg4`?mY$Y&^cKCmV!GvU zI(cdMapWx`@yU~?5G0!0zVdskVu|Kts3TT9^redZ@$ssgA|jkaEB&k#mUJs22L|_Y zc0jHOl0)W~oMevFVM8NN*u`(XBqh7&QGnR^Jn2+(6-)-ya=pKpxg&l36m}nmz)B{Z zgaPW4tckYbmMEWSEPlYhSdZ*w8LJYY8EfEcswe8S@(f5btL4y$4Pv%m;I`zW?;`y` z=Q$FhtJM5bhd##boa$|gk*ts2Yfv|YXL{WmPL9s7pu;xGqo4%38Q zhN104kn?+}M4bNUb&GxwO<=bFYO;9N-!vbk5eOQPG(aU;-;DSm9PkoWD z0LzU5nK=0mt&VR%9h>xp&D2$>fB^a@V4064N+jexI+dTWiK1qKK$MF89_07Vs?Y~) zM4hG2*zQ+;M@QSA#de(!5vn^SU9qcH@j@Nvy%8=NnwEl^Ox;L+m6Wjm!ujtU3vlWB zL8>4#^-GEUAEXTrKB*8{trG2k zCLphmqn7~&1_ngG3baAk{atAk?Ke)wXIz26MIg@K588LCMAF>{z6VQ22M4oH##YfZ z>nhND0dF@PKE1k%-AYL*E*py=_ut}5y>d$$d_T>|Y-I|)XxOKOgXLqY!J*;B1p?by zXK?{s`zn?cM@a)4M}PZlc?|_IH2h6AmWF0h5JDxKr=H6xpBmKH7Ln*kSDjc<35&Hb z7xMiW|4xBKVHNLCRlg-pSWfG|@cjoACHNPmhSP4n4d&)stW0AzC{^ybDJ;0~eoqRd z#}2A3aL*p)5Za{`A-Ma;9M7@zg9?y4;+=(D!w`5-hIJKXAfrG_3($zJqLKXiyoSPjt#F(7nTY&ICRV^bB)$=1Wc#3~ zd^Z?vaPkbAn+LsJL%TZ;Bs{lzjaSf#^uX{Q%}@L#Bo>2ZZLm$Wk4m zC?X#(Kso@NJ`Ig-RM(JL0KbNKz!p3`UcJJewv38YZn*k!rE+l)rh}Yb7?+lohHJO` zf6%5|1<;EYM=vh%l%Y2+e=W09VjeyWf8C;d%2VF%BhTYwk(+-uM4WOQ?Rv@TU_Y`e z%HHCEnD)jv|2__^@V<26N#Y9z>$rBjHf@sI7N`{)1Vcf=eH} zGd~^JBAWgV9B?$5NXiCp6vX-6W|XA9;D@7yCPdG8g~#@z$Sj7liR@-^UmlulgcTd; zSxp%wEymvCvNSS^L9Klq1ReV%;AUy2z_$l*9N#=*DjTisC%U9ERXMeLe-)z8Y!u%r1 zBXH~0GcplkBlG(hQI#=+QU47?@mV{6+s~E+xYt?_&A9suzqy>!Gc%LC$yjFal$+zM zr|CljW)}Iv7k)nUzkIb+kEqwjux>Pr^H*6OD@yTP_#T4RU)BV&>G^uY4L417^*|G* zyp_`#S%cGUusnF%V2UxmpV{R5sVyOqf~l}?QBYaWt9Riq^QV)6nd|SM^W}B_H!q3r zRoFu0JlVr_1!`6kA*-EU)^CRRo=x;O$3(V~u~B#x^UnoYW+qqNsV~wSJho zF^@+<`sc_g?Pohg)0wx~L~4L~*{Jx{-$Efa0Sf`0dc4p>XNG$?Pxg zAGtonoea0;xO8U=Y2LY!{f@*5>!;9{6(-+uP@PAwlS}a9P5fh_$5$novuo|r!%mqq7Glb<43JwF(IIC^oI%g|LYWP|V z391enGen3YV074 z(`T#RPO^RPo?~jQBM$cqe|45>`ifo5sb!RV^jzM9-fLYa+H(!>#IWLHB!G-`43UAhi?#F7glH_tEkPK;!hkeo<=k zOcd-drfBzYxj;3ezB&NIa?E{2(42^h_YB+uHv&oY`1WmL`e%KQmw{0in@FgN;k7ZY zqR>d3&`$q}=KS#BJ{3*!{1BGc7rL|7=*hwY!fU(?L6?lUE9t5TThK@F+XH*VU6U8n z*Df9LIO2Jz{ox?bw8765rdhM0eDgv+#cob+D+Shk&&X(KXl}z0Zo(Q2e%P2nNMw0g zyqe&d|8HL7*NTAc$6wy9>BcP$VtJqE=JH2GL_oWMe=WxFV^0%yOkxy8s#=}|;XlS%SY`+f8aU%RM z7@UwOgNCr8>1q*dsU*~w(uKqqT-CymE}$|2PQ|}@bH*U1CU4M=&#lHuerg1~hs%ro zJhK5uD0wQ&W*5fFFH2A`1RkHz{;9^suw!}4VfIwBJ+$TgoE+W|oPV(i^fQY{WLOgE z?y{Z}*1WzE_A&wq*;rk`C1fE&o)J}{^)sdQuFpe5j=0+&?d5A0I%CJiDa<4ES#YC4 zXJ9o9zJx%>j}0t`dHAkYMIQ${yn7+*pv7`{z$HemqZwX;h9MkR9ShyvV39@6uuNcR zxI8;)3+i8>9x{q8kb{@k{mIzJpa$u#XU&3qi)n{X`i#CPF*(c>-jd$9~Im@{+(IzS!;KmR9xLIC9b z$1PS(7NGoFfl8NJIViLk3me~!65^~9_B1me6%cn^X)8MWYpJ@A{LJ)#(#%HfS(SP* z47ELD#nKkL>v=lI(3<2+eHm9g1ldi!ZZ*XOz~-R74(%bD(5cj-Swx{e%-G4WEWJ#m0}4?hyNe<5S{(+dW;o+e}lyF>YaBhIk7BIy?AU?%>VkZ)VbL&fgYQcn8V2R*47RD9!$gLl3!-& zV`-9g-W)%&vBO>+b{#DV5`W5R2VUwiRck5OMj?+xlM3%Q5Ufa7%-l!)EsY88D?yVX z;q;bo_B2EeJST+B`j^+f?L9Z!(Dk5x#OAZvwXwAq7S)X*zX>M;zYeDRHuM&*O_%8flavnGKv)YKgVPO;yUW3msa%~1rkX*1rFNK% z{HDcrQ_L^=iX_;kGyLTYOqI{UerU5_%xc`3Qy(~B#H`Y%AV;cAW)l$mG=L1b8Mzfn z(O=PY{19`1q?oKo3d(MO6$V5dn?68nd5~2AXGvT3n@8uw#;lOYM2`%UgN5||>)C?uU!ui>e`D}4XM;?pfL3-DJfGY~%wxCo->3kpZZxHirMg?i5 zxD3POZ=s2N!b?~L8Y+*1k0P*3@moekKXkD0Om@t^h325zk5wlUy}Mxd%wo^3w6sL}m|wje272!}0vLnKgPZy2 zJ2=J98X0-4#mgM?BefQGbm&0M_)I!iXYqR&<$nP@RABB?EWf)Rt?!dU(Fha1ma+su zci-QG_&0CNHc@T`q7yw@(|)9MCX2hMAp7i@^7{LidP^>heOxQS9zxx9PQ|9&bD{Km zy0P|~iTPHjjMSq>PrM4v2aiIe2FsT<4ULS#Qb+33eEx`t(SDTu=-$>Ukl*K3;yJHl ze49VgO1eW;W%o{1&x*N5$+{0k4AbpJOsDi5=OoU`^LYm-itC|^_YN!-=h95W&qzOq z)d4Wi3HK@JC@EysMqYkMvq7PUwO8tI>V#!Upqb$~E3^n_`Ru%Uz+woq>expiyuSq2eAnPCm1z9$~_Iw6(!LN7xw%lk(BLAS`ckeVmM=L~k=bdu4!vt`z{=IQk+JyQf z6l}tZ`n*q}6^)N@OX2h*nWJbIdU|2u*;^{)`9J4&1{cin{q*;K?m31X!#19JIk%Y5 z{ya>AI&YKLe3=-TWis97`Lc0C!tMT)6D5mb;61P2B&Rab+1|`wKS!<-X>R6%Sr(b; z4;;G$r}@CD%0i-~ccp6qf~GoFXbAQb{A8$A0E+0Lg|b3JQBf+m&2E2t0zg5t7Qw?& z#+*5DRg1Ce;FVbI`ulscZm$^&nKSGLLR2vfKXv%?B}+_U)L$kj8GTcja+|EccnBQD z8a;Ei=Q)P+D=1$0aod$T^+8$y?qj^@f)20xxv$RBkdO>CG&&n9RJ47cl<eL)Z^>@RH6lP1xA|gKM_xJArwz4eUCrX7R**jtQp0vvb zK`#6~@KXI;uXoVDRWb2SI*fVxh&D8On#e3I{|Ka{0B~2UWOevw1Jb&hllGle@e1@@ z?t4~eVO)q^vln%XUiqcoP2k(mV`1(>J^$m$Z-FqS@x=&}<w_X9@~o_7tv|OWVuq8C>xm-SzkIAb$eISV9049xp)D z#`1~a6kS5tL^iRLR=tHoWw~~6sBnzGpvy&kJ+dYk@Z|lA=NT)x@hv6&N*x`c6fEAP zTz1a(-fYOKyN<*y*5L!hO4Dood#}w+Cn&k!+A>LZ4&~BO)flb#@=){(X6bB-3#%T zH7kP1_zS9e9YR_F7KH4^Th#SIJz4bJT)?RV#6-HcAnVcC;;$Pa+N61L(C@*VGzm9a zmgG4UY*2r5PTlwE73}I$e=0RJ{Hxi*<8Z|(WqRezYK0$RBSf(V`izO})XdJh7`(DR z1Zofm)=3swNxY)dD?OMBwK&nOz8Jq-adzpdq=9(c!m&8#N7HwhjO;3$Z6`N035T#+ zZRu=fFTkG&9j-+tP&>UU3{qk@&>v3E8KDh_wowi4*Nt>sXrN@z)U2rnn7Ae#D`3&wXv9LurK7yW)fvVvo2a z70+-`HYH#MMI3xOLZ!Z43!b_?E-dnju3PQFbbHe^Hk}aKpa1eF^8s};@YcSsVq2jW zBg+xLR*X8%Q8Iw3wgP{p(7il(1kNN(%G^~+Y2{3iVZpz^^05B}b|(O9fwtqJz}8xW zegI403$a5gB$QY!4TLSIa{|t~W>$i(Qa@#}>Ff_YpAH`Y&!t7QDpwh3yfBvdUV>$B zTF>xhZ7&U(7^yM#_@-}u?Q=~nM_|K}rRbNV)?=e5B;Jk_yP4be-2F;a;-x8EwY{&1 zoK}SWn*?8^xI=?Z7pd~(w2sCQEPDCDrRd^7&6PdW1)=l>9XX(zL*j0{J<(kb^ zdd&$E{wkxodgt_{Xx{o(U)5Jz5MMDCiQeH8Y&#d?9~H1w98Tv_OioLthh98U+J~hV;UN3fn%zfDBnY&_uXHy4o7(d@=YW*XsH%qZ5Rg) z5Dr;X8YJydx1VJGS5ywV7^6egvoBq-jii#*-A2!)0{&WQi~1pV&#a zLxjZcDpRj?*NIv-nruo7+o+dc}tweW*uMB zv|g4$zk12v(U%aqw5(Yk!4HL3X{g!nX|4`<>P(@8p_jWKIDWgG`5D3hH8dT@DzbxZ zfLbA{Z+<{ZOcz=Y(9bCOzs%DJPS1sU;InGjto>JP51w^6qhwLM*K+8qRo}^d@t*OV zhmqnn;G8~GS=e@O7K4e}<+|s#CN^TDy6IP3Wo-KM0>hc-c^d^1-Cme1b#3`hWIRx! zonDx==_}wB{YqgjL!N-;*)0fmUPy9m$%XJ`GWWf@TfJ;msvu`^Ra~h1uDkv z^LoJy35mkZN#;Kv#yYq z)on||lMX9dHP zYO#V$uE)*q@|nN3S@BHg9Xz{lyH7YLJ*xT>MF2{Pp`sR-?Z~Tjv}P!;8io`{tF{>+Ux`dG7wlUFvFb-Fvng3UivohO`BR z2c3Oc@TMc80_$+@t$yKIpYb<5QtR}07(OH@zP_2dS*pXOWO&So=Oy;x)A>SO=_%{$ zYejC~ys%nLPNe|bGrjmR%2mwO&VMM8GQH?o&ZrT;)ben#((g#6{F1kN%E9lLv^`Fh zyP|QFIpV?^bS`FZ!`rT0Yh-L+zZ{FLcpID9zaVQ{dY@So>*Y}+HIW_#vx71Fjzv>tV@T3F&wds&GO=H5u@-W1<) zc(zE~i`i$L_}H)mM1$bFBb zhJ64Ub{&PENv3k=BLE12I@rD!DY1sW2v}3nb2B}Bi(JV04&9eSf*tUU zul1klFY-KqRo`On8cz?m$23J=l9{zK*s$O*@MCwhpI=dk`MQIZ-0QEbZJ+cY)Q=yhC(%Vn}UjC>4+bQ4HyS`QqcqtYn?B3F*W?c`f8An?C;1@%uJv_-ykTV~gCQ z{WTx%u2p%(xI=a&5>I4LV6JyBH*%-p{1(%7D}Om%q<;HJ!Fj*^b6YJi=Ju>$Px-kt zrK%e;Zj3P8`u)sdT+pB8AB$;eK9T}_XXle5UanOo3s`#Nzf~}v>oQbx_C=?CryNQ- zJ%G7UX*)h+sPnW#x=q?b!=$&&VDU47!~czQQqqX}>q|@-^z7+9o8#pM_QTm*3CZgt z6Ohc81HNZTK*$Q>92gS%Bb+Ru=lqHU)F2`(k+4RhZaiX1j*qu7^`#+$%g#rFHgs&X z$ar`O?_)Kbu4>YKg`x-ChDuF7ab(<(##0sviOYYC zUi##P0o0E_Fe}|ITMXE}?XrkT$tg`w8BZ@ObCJDd6iK1Qkr~eZS3&w;RK=P;R91dt z^|WKRe?^X!PEt}+EazsT_ye)L_OufZDtSy_^b4f@z>>#|h|JkE2>fUFB3tgU5h6Yy zx_Rpn{cV>(@DcJ|;Ba$xlFLOTs^=h!dvDkxxt9j2mV5u@K?PAH_mvM>f{rau4mE`H&#J?kcl`s{5Ve zz~y~UPSKN@OBd6mll*M$^Vi6$N8rgiCug7Z#h1#w*nt;Rq)2~UfiLs@7 z6f!Yyk2-rB+0##O1Uab;k;!~1Dtl)h16!*U(7JSmIfc&=wT9!QQv|#jj+F$8a)4BFEj752W-%T0lTfE?9A!?YgIfKU#6Xp<;+t(G&9_Q=Te!if34O^%ysEDHB$FDyW#rb zCTp_gK&O0FpNDhk6AVMzdFdIMtUVOubYx_L+}OvQBPVm+;MS%ERGodTKW%W^KssV` z``82J{j)@sQX9_h3+ty^h(ZA2-Lle=uDnLsw@SQ6x* z2{@baHs#OnbkyNind>%CV>)*qQ>!YOPbarEah|pCFus(%fMIAT?QVY%AKxSIZbGFQ zR^B2LMCg~UTPMg5HKNDXY$=Q^E#2j6pA_O+?+AD*`N7Lyn3vj(3vFcJ$0M7)q64_Ch(%s*UAzwpJvbWpQ#7ovKvci(@tf6 zM*CE(N^*;&uqu9UOVUy-iVhTbja^Rji5226&6*Oa|HRA?5Cc6cRYJU-)}Ese-h|6V0ev_r0E5$(}HeI}BRB`RY)b&48H7IVqY%pPldJ1d>+j^$@A zj&|91=YRR-RGkuvcDUh_)t6wJyp+&}$w_%4xa22O?x#O~A}M-FgGy}+j;kX()_~%sxKbA$f zlk}e{wX{MhVvMW)@Vx#|rtqnVkpK}ayU)HyTfM(HxvGUQ8j{oI=}UjoB4Td;e7gL( zu@C2c<7Y~+f8n2Bx~d4i)ZxT%j`pSC{B{Fa*dT-J8MtFM!jW!o}YO}XDo(#BBptp{&LpCOsA77 zF@i&#hpps_iPT5~;|$f5o=2v!5=*3Pxz0`xACM|f*BI$FiORF;Y08xbT0S|FNWreZ zY~hJ|?Wtm@508+v{-RHcMft?xGYen))&K6TVdSLv2j0i1k~v`qXWDxUlHaHOt}syJ zvYQLKk<`JbJRjw8OZED&e@-l;yUu+sWRAPSeSR^mL_RavZgw_!YWh>`NCf{xyh@yD zN>i-qT-J>fvWgZtuyt)bgvRAAylMQT!UA1>uY$^(Q&b&&fvmif{6ulXI8&i`mF4fc zpExGcKVYh7eqRcrF0>q!jbUOpBI3~b4A@kEK;h}MO=^m<01W^$aT9%>OnK$bp|>mafpt#o|Bh#@qt-_L=|&t z+4au&e5_VJ6*ZJG(p$}AOcOxe=5#n{XFnTxO1`X8`Y!l@eQhf%L z)D3o|0sMdO`W2$8u#-AkkZMmkI{X=pa5pvH~WQz_v@|%A}cIiSbA)Bo z2kDOiKjg;>uy|A$$~R(2Clgsjwaq_Mk*4L7wLwRSav>?&+f{-|N7zLyNRa&Ge zh#NInXMuu4@Y;$PrnCM#s?Ji&!3WY~i6k;{a*FWcpu0D<5&btMmvj6K9QC(>{X1@S z<+@1xq3J&Ik9u`57Q;V#^U5r@Km94c@DipkkZXaZf3hBl^k0PVYsKk9P}W#+TOm}9 zer(X%ZlA~zh(A6MAYwejX2jS1>%G#g%~Ibsf<&qIH7CsX>I$XgaOp#=8@3f`lVib* zRJX_@*%wjA0OTm?5~BM7RWa}}J94_+g+LwGpU)CJ!s(W|K|3k7C)zY6Jhln+dAq&Y zR_Ykt!QQ&R9+-MgONq#+NNZ_%pT?`IKK}%swGllGQPK$@Nv(xhJhAHF~M+JJcfsk?P|vfuzgX7_A-TU^dFa6Ia9E=Ila zhULGmGiX`rnz;VPp6cq?X*mX1Z2M%~!nJkq{PXJ(!N$MOKeq)raQ80;ufdzz&f;~1 zboR}G7_5w$!Q<_#9p3m*N28CXmfQ{`=(IA|y;*ufTAXZsG?}9-G(P#$0LU67i`cK< zia6M!G>#8uBiOJ)P56O)0REXeJ4tXW{6MO8%XBz&#NCVl{vche;+aZ$Q2Mh`i6Tb`b`-)mY z3iA5eLJX8L3ZFC_BtOMB@}r(s##bVhiu-rpb5&sKbM`u#vpT4K=8ZDye*Xkf< zAwfT7>%aY8{8@|P{@9G<;}O?+-c{_GjcY57uCaE@T|YZRYyB&av=Z7v<{6cP2WovJ zth~MKP{z2r6*-C8SonwzVoPoNO99}Ke_qy2@yJk3SkiR36Ih+z7NY+_ALZ%dgo=SQ zuW4bd!dE!K*3s_H5M||3{kMGv0w6Sq=NfiWVH5h)>UZmrt1;;5k?sNL5*YNw+21EB zRNRU1NWvwDv(%Y30eva%JGpD&==e;FCrWf*b0e?qaHVZq;tG-b}{-0RD?2#&K^Q`_FEx!*aUvKJ*&?F#u*L9$tg%%U6of^@g& z3H6Ze-E`l}{Zodn*i}hM+gPD{gAX!1a#+l;(bWmX(pqDMTnGycctICM5&ezZJ{^?= z!2uC+(vqix-_v1UYxnG;`DYL=3#dgv%(Yy_^YrJTWE3wF47y^SAB!L0Z&!#9-$awk z^ougI4sp(8+&!I(Iv}QT=>~9SX-vr%;+~Ha|;s5x1l(o?(2Oq-E+COa&k&$#a zm%y%y5a#^xgj6=_;$uJgBaLj&L4s`fI#6#n;8{>o1Tq+`nE(G1CVTayoGUW!Ht@74 zAELuK2X%+%fz?gq+8{Hv$1u79wB&Xk!S9z8(Z`VChTeZU+<%?+7CU`vRGVv#U|@A$ z&TE4a1DkEDy?vM-_Sde7L(bY$6s&p+nG^q*rS4cU4m|H)NBqy36B;XX$_y0P1HPTh zd1APSGT(WgY;XAu;b75xA(;DfYLS%F2~iNdxlgAx7fGQ-qT z3D9ctX+4aOtd zU%{OEt?h39@Tn~4X`Q4LQz7cevK@=uaG4o}#X%nZ6I1};;k)YTHI6ZflmE}t(?Vt9{ zaQEDE&b@o?J@Dq@R!Q56YhO>H>#ik;cFE z!>so%XMXv{ooA#*rj&WJs*}>}ubE@%ADgDw z4d6gg5yr*Qr;9O}kg@Xm z`g+d08yg#uevy-q5EDa0T3V{AsuqhSDhlJ{F)?v#KtN+q(6+j|y3wOY`}j;JZKI;1 z!ozO^l#Cd0hx~~ugTTP1=H_O`=-5OD2RnmJmgphRvQm z8z3Ek?#`2dwNAWJ85!AOFuZzk%d)nk4s6`l-rnBY+S*mWhXm}e%m*k3$hutg(v6ye zsEFZs_8^`)K-=b;06yKAdiO|XYqizY4R^Jr)P{xKa^dnA8gh$I&`06gh4U8CYs0a? z?;a|71?(&Pg{K z-+EwNdh%q@XPK)%e@lBTcAB&8x#;NDrJaG(lTIEhewcV{rSW0N-Hz{{=<13XbMI8%j^NWX&fGG@h1kpMe1qbm z7cSn^_Rn{Zbc`GxeX&rP9}={?cz0RJY}34%O-~!gT^LimYyVg8oSt>YGU4*{&e*v5 zFSa$uJ2mGfolkl(_SCM)i!08jNAKEs=c!cv%jr88UKnegllYs_hjyL17<)1~u_bxt z#^l)KXN-qx@;iQeTyI*jI<$F4f+4CdKJxEt+1f>hwQJagbCu4&-8mNhyD(WJ$770a%* zEcxZrCqAp0aO~q||4(jAUElGbd$?x8jf^NM@?BpI=|@a&I^SMEC)_JKteQg6La#jyEL3qQOb#6q~{-s;!EC6g?(~FDHQz z#i|$%S5?(09Ig`0AO{gmTU1pQXX8FZqF3v!+XVV?z{h5;bZ{M<=fG`>16Tylrd!1t zTFmsJrnPt(;lVjx37ja~7Xy178f_Jg5$FG|2+r z%zdTzAaEXKMIF%5g)Q6)I)rdgC51>pF8~_>b{G~&&)03H7+`bLgK2hp^tcp8P;IC$ zo=C!>+kozf$w4PLFc(;)As^VqHY@7eTWPMY99k+b0Cp9#k=Zc^tklzArZ2uh8pv)T z=svxR*qSFLOJyTG7{yi=2DR|t8z*o_f8#hC)B(oXp{fInL!|!3Vdei%#))(pM^md_ zv3m^UzvZZ^Hj;rF)OwGBcBt7Zdqs5{$hU~HMoq+7^-Pb2HXc#c>c?AX0y>Zx$bAYa z@z62Zf*K*F(qR?PTOkj1tc%WrO*lX+FadH=cb^VHlT9!`aDYbOl3dh}M?W(*S;ZzA zg?Tuv+gvKJNeDVqqC5|Mz$^r&2|7;)a1##eq#m>&k~l&>YJF@7b|j~nLZ{8`G{|BI zCoRhBZOFPCsY$>Bq|9gqmP)6X9A+CbdI=u+3Z>3%9$&%D=qs}$9d_*X6?FN^?UG(! zL3YP!6TZ8fs1#^sBD%*}oaAqpghQ}wn%UzqKGI{v^}RWAL1APUmM*dd3!>f9I7KBn zE(p8Og6McPAN-d&gxE($DiwQj1Vf}%n@L=*(X1!TFCy53bph=kJw} z$_xRI)892j3JfvUO@0!ewxMt3GhntSd;v``V2C%tRY`dh9^n=Egu^vB!+{i)u4DIqUV zh6LKR+##7R26jK5@8Q0t$Wn;C?*zM&OMg~QHHA^JD~toB0Yz7>j6&`JKSe33qDvn> za-AJ)oHdwjTw_ucD`-^CDegsaJNJtxYw#$Fo>WfwK{IVEU@#A+;2ceL%O=pZVLPCW zQaxQ-Xeq|JaakI{X4w?crO42VH1sN>{-=sWx)nL9wo^INqll_Txbc39&?Y-jPuF@N z5^`i_^P>?0ZK&-oMM3%H0M%WT*; Date: Wed, 30 Sep 2020 20:25:55 +0200 Subject: [PATCH 123/136] Remove "RC3" --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index fb709df0d..73ebc497a 100644 --- a/src/version.h +++ b/src/version.h @@ -12,4 +12,4 @@ #define MODVERSION 48 // Define this as a prerelease version suffix -#define BETAVERSION "RC3" +// #define BETAVERSION "RC1" From d612fb5c99e7d93ea68f8d15d970e7206297ac3b Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 1 Oct 2020 16:50:31 -0700 Subject: [PATCH 124/136] Add wads from the -file parameter after netvars are registered This is so netvars registered by any lua scripts will load in the correct order. --- src/d_main.c | 35 ++++++++++++++++++++--------------- src/w_wad.c | 7 ++----- src/w_wad.h | 2 +- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 760962a2c..83a863463 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -99,6 +99,7 @@ UINT8 window_notinfocus = false; // DEMO LOOP // static char *startupwadfiles[MAX_WADFILES]; +static char *startuppwads[MAX_WADFILES]; boolean devparm = false; // started game with -devparm @@ -977,12 +978,12 @@ void D_StartTitle(void) // // D_AddFile // -static void D_AddFile(const char *file) +static void D_AddFile(char **list, const char *file) { size_t pnumwadfiles; char *newfile; - for (pnumwadfiles = 0; startupwadfiles[pnumwadfiles]; pnumwadfiles++) + for (pnumwadfiles = 0; list[pnumwadfiles]; pnumwadfiles++) ; newfile = malloc(strlen(file) + 1); @@ -992,16 +993,16 @@ static void D_AddFile(const char *file) } strcpy(newfile, file); - startupwadfiles[pnumwadfiles] = newfile; + list[pnumwadfiles] = newfile; } -static inline void D_CleanFile(void) +static inline void D_CleanFile(char **list) { size_t pnumwadfiles; - for (pnumwadfiles = 0; startupwadfiles[pnumwadfiles]; pnumwadfiles++) + for (pnumwadfiles = 0; list[pnumwadfiles]; pnumwadfiles++) { - free(startupwadfiles[pnumwadfiles]); - startupwadfiles[pnumwadfiles] = NULL; + free(list[pnumwadfiles]); + list[pnumwadfiles] = NULL; } } @@ -1086,7 +1087,7 @@ static void IdentifyVersion(void) // Load the IWAD if (srb2wad != NULL && FIL_ReadFileOK(srb2wad)) - D_AddFile(srb2wad); + D_AddFile(startupwadfiles, srb2wad); else I_Error("srb2.pk3 not found! Expected in %s, ss file: %s\n", srb2waddir, srb2wad); @@ -1097,14 +1098,14 @@ static void IdentifyVersion(void) // checking in D_SRB2Main // Add the maps - D_AddFile(va(pandf,srb2waddir,"zones.pk3")); + D_AddFile(startupwadfiles, va(pandf,srb2waddir,"zones.pk3")); // Add the players - D_AddFile(va(pandf,srb2waddir, "player.dta")); + D_AddFile(startupwadfiles, va(pandf,srb2waddir, "player.dta")); #ifdef USE_PATCH_DTA // Add our crappy patches to fix our bugs - D_AddFile(va(pandf,srb2waddir,"patch.pk3")); + D_AddFile(startupwadfiles, va(pandf,srb2waddir,"patch.pk3")); #endif #if !defined (HAVE_SDL) || defined (HAVE_MIXER) @@ -1114,7 +1115,7 @@ static void IdentifyVersion(void) const char *musicpath = va(pandf,srb2waddir,str);\ int ms = W_VerifyNMUSlumps(musicpath); \ if (ms == 1) \ - D_AddFile(musicpath); \ + D_AddFile(startupwadfiles, musicpath); \ else if (ms == 0) \ I_Error("File "str" has been modified with non-music/sound lumps"); \ } @@ -1304,7 +1305,7 @@ void D_SRB2Main(void) { if (!W_VerifyNMUSlumps(s)) G_SetGameModified(true); - D_AddFile(s); + D_AddFile(startuppwads, s); } } } @@ -1345,8 +1346,8 @@ void D_SRB2Main(void) // load wad, including the main wad file CONS_Printf("W_InitMultipleFiles(): Adding IWAD and main PWADs.\n"); - W_InitMultipleFiles(startupwadfiles, mainwads); - D_CleanFile(); + W_InitMultipleFiles(startupwadfiles); + D_CleanFile(startupwadfiles); #ifndef DEVELOP // md5s last updated 22/02/20 (ddmmyy) @@ -1395,6 +1396,10 @@ void D_SRB2Main(void) I_RegisterSysCommands(); + CONS_Printf("W_InitMultipleFiles(): Adding extra PWADs.\n"); + W_InitMultipleFiles(startuppwads); + D_CleanFile(startuppwads); + //--------------------------------------------------------- CONFIG.CFG M_FirstLoadConfig(); // WARNING : this do a "COM_BufExecute()" diff --git a/src/w_wad.c b/src/w_wad.c index 529be847a..1469b405f 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -887,16 +887,13 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) * * \param filenames A null-terminated list of files to use. */ -void W_InitMultipleFiles(char **filenames, UINT16 mainfiles) +void W_InitMultipleFiles(char **filenames) { - // open all the files, load headers, and count lumps - numwadfiles = 0; - // will be realloced as lumps are added for (; *filenames; filenames++) { //CONS_Debug(DBG_SETUP, "Loading %s\n", *filenames); - W_InitFile(*filenames, numwadfiles < mainfiles, true); + W_InitFile(*filenames, numwadfiles < mainwads, true); } } diff --git a/src/w_wad.h b/src/w_wad.h index fddc65529..41232cba1 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -150,7 +150,7 @@ FILE *W_OpenWadFile(const char **filename, boolean useerrors); UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup); // W_InitMultipleFiles exits if a file was not found, but not if all is okay. -void W_InitMultipleFiles(char **filenames, UINT16 mainfiles); +void W_InitMultipleFiles(char **filenames); const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump); const char *W_CheckNameForNum(lumpnum_t lumpnum); From 9adbb7dfa36194c9985f89d1ca25ceaf4d39f314 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 1 Oct 2020 16:55:17 -0700 Subject: [PATCH 125/136] Fix off by one --- src/command.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command.c b/src/command.c index fc9d44792..f2a9813e2 100644 --- a/src/command.c +++ b/src/command.c @@ -1201,7 +1201,7 @@ static consvar_t *CV_FindNetVar(UINT16 netid) { consvar_t *cvar; - if (netid >= consvar_number_of_netids) + if (netid > consvar_number_of_netids) return NULL; for (cvar = consvar_vars; cvar; cvar = cvar->next) From 3dad7ca226daf9ec70b98f3e1256f96cce6ccea3 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 2 Oct 2020 11:54:58 -0700 Subject: [PATCH 126/136] Move HU_LoadGraphics after startuppwads are loaded This is needed for graphics replacements to take effect. --- src/d_main.c | 5 +++-- src/hu_stuff.c | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 83a863463..d1a414018 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1383,8 +1383,6 @@ void D_SRB2Main(void) // setup loading screen SCR_Startup(); - // we need the font of the console - CONS_Printf("HU_Init(): Setting up heads up display.\n"); HU_Init(); CON_Init(); @@ -1400,6 +1398,9 @@ void D_SRB2Main(void) W_InitMultipleFiles(startuppwads); D_CleanFile(startuppwads); + CONS_Printf("HU_LoadGraphics()...\n"); + HU_LoadGraphics(); + //--------------------------------------------------------- CONFIG.CFG M_FirstLoadConfig(); // WARNING : this do a "COM_BufExecute()" diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 1a184ea57..72fb9272d 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -330,8 +330,6 @@ void HU_Init(void) // set shift translation table shiftxform = english_shiftxform; - - HU_LoadGraphics(); } static inline void HU_Stop(void) From e42eb9aba81b5cffd9a3bb6003c8ad950fea95d1 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 2 Oct 2020 15:37:23 -0700 Subject: [PATCH 127/136] Revert "Warn when going to OpenGL from the menu" This reverts commit 3883d44bb57d8a68dd1c15fad69a631463a5de52. And one line from 5c1b3baf18. --- src/d_netcmd.c | 3 --- src/m_menu.c | 50 +---------------------------------------------- src/screen.c | 5 +---- src/screen.h | 6 ------ src/sdl/i_video.c | 1 - 5 files changed, 2 insertions(+), 63 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 62b93bba2..4208e4c4f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -865,9 +865,6 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_fullscreen); CV_RegisterVar(&cv_renderview); CV_RegisterVar(&cv_renderer); -#ifdef HWRENDER - CV_RegisterVar(&cv_newrenderer); -#endif CV_RegisterVar(&cv_scr_depth); CV_RegisterVar(&cv_scr_width); CV_RegisterVar(&cv_scr_height); diff --git a/src/m_menu.c b/src/m_menu.c index 49acc0666..c7786a496 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -406,9 +406,6 @@ static void M_ResetCvars(void); // Consvar onchange functions static void Newgametype_OnChange(void); -#ifdef HWRENDER -static void Newrenderer_OnChange(void); -#endif static void Dummymares_OnChange(void); // ========================================================================== @@ -433,11 +430,6 @@ CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1]; consvar_t cv_newgametype = {"newgametype", "Co-op", CV_HIDEN|CV_CALL, gametype_cons_t, Newgametype_OnChange, 0, NULL, NULL, 0, 0, NULL}; -#ifdef HWRENDER -consvar_t cv_newrenderer = {"newrenderer", "Software", CV_HIDEN|CV_CALL, cv_renderer_t, Newrenderer_OnChange, 0, NULL, NULL, 0, 0, NULL}; -static int newrenderer_set = 1;/* Software doesn't need confirmation! */ -#endif - static CV_PossibleValue_t serversort_cons_t[] = { {0,"Ping"}, {1,"Modified State"}, @@ -1347,7 +1339,7 @@ static menuitem_t OP_VideoOptionsMenu[] = #endif {IT_STRING | IT_CVAR, NULL, "Vertical Sync", &cv_vidwait, 16}, #ifdef HWRENDER - {IT_STRING | IT_CVAR, NULL, "Renderer", &cv_newrenderer, 21}, + {IT_STRING | IT_CVAR, NULL, "Renderer", &cv_renderer, 21}, #else {IT_TRANSTEXT | IT_PAIR, "Renderer", "Software", &cv_renderer, 21}, #endif @@ -2458,46 +2450,6 @@ static void Newgametype_OnChange(void) } } -#ifdef HWRENDER -static void Newrenderer_AREYOUSURE(INT32 c) -{ - int n; - switch (c) - { - case 'y': - case KEY_ENTER: - n = cv_newrenderer.value; - newrenderer_set |= n; - CV_SetValue(&cv_renderer, n); - break; - default: - CV_StealthSetValue(&cv_newrenderer, cv_renderer.value); - } -} - -static void Newrenderer_OnChange(void) -{ - /* Well this works for now because there's only two options. */ - int n; - n = cv_newrenderer.value; - newrenderer_set |= cv_renderer.value; - if (( newrenderer_set & n )) - CV_SetValue(&cv_renderer, n); - else - { - M_StartMessage( - "The OpenGL renderer is incomplete.\n" - "Some visuals may fail to appear, or\n" - "appear incorrectly.\n" - "Do you still want to switch to it?\n" - "\n" - "(Press 'y' or 'n')", - Newrenderer_AREYOUSURE, MM_YESNO - ); - } -} -#endif/*HWRENDER*/ - void Screenshot_option_Onchange(void) { OP_ScreenshotOptionsMenu[op_screenshot_folder].status = diff --git a/src/screen.c b/src/screen.c index e7ff9e735..6dadd25c3 100644 --- a/src/screen.c +++ b/src/screen.c @@ -64,7 +64,7 @@ consvar_t cv_scr_depth = {"scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NUL consvar_t cv_renderview = {"renderview", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; static void SCR_ActuallyChangeRenderer(void); -CV_PossibleValue_t cv_renderer_t[] = { +static CV_PossibleValue_t cv_renderer_t[] = { {1, "Software"}, #ifdef HWRENDER {2, "OpenGL"}, @@ -499,9 +499,6 @@ void SCR_ChangeRendererCVars(INT32 mode) CV_StealthSetValue(&cv_renderer, 1); else if (mode == render_opengl) CV_StealthSetValue(&cv_renderer, 2); -#ifdef HWRENDER - CV_StealthSetValue(&cv_newrenderer, cv_renderer.value); -#endif } boolean SCR_IsAspectCorrect(INT32 width, INT32 height) diff --git a/src/screen.h b/src/screen.h index 91ec175f4..2cb2cf839 100644 --- a/src/screen.h +++ b/src/screen.h @@ -168,7 +168,6 @@ extern boolean R_SSE2; // ---------------- // screen variables // ---------------- - extern viddef_t vid; extern INT32 setmodeneeded; // mode number to set if needed, or 0 @@ -179,12 +178,7 @@ extern UINT8 setrenderneeded; extern INT32 scr_bpp; extern UINT8 *scr_borderpatch; // patch used to fill the view borders -extern CV_PossibleValue_t cv_renderer_t[]; - extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_fullscreen; -#ifdef HWRENDER -extern consvar_t cv_newrenderer; -#endif // wait for page flipping to end or not extern consvar_t cv_vidwait; diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 5c5b6119c..c8f67da77 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1489,7 +1489,6 @@ void VID_CheckGLLoaded(rendermode_t oldrender) if (setrenderneeded) { CV_StealthSetValue(&cv_renderer, oldrender); - CV_StealthSetValue(&cv_newrenderer, oldrender); setrenderneeded = 0; } } From 9ad15fd5566fcaafc012dadf1af2d87d120a7606 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Fri, 2 Oct 2020 19:48:13 -0300 Subject: [PATCH 128/136] Fix PNG issue in OpenGL + optimize PNG sprite loading --- src/hardware/hw_cache.c | 21 ++--------- src/r_picformats.c | 77 ++++++++++++++++++++++++++--------------- src/r_picformats.h | 2 +- src/r_textures.c | 22 +++++------- src/r_things.c | 37 +++++++++++++------- src/w_wad.c | 4 +-- 6 files changed, 89 insertions(+), 74 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index bf99c584e..8c85c5112 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -109,12 +109,6 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm if (mipmap->colormap) texel = mipmap->colormap[texel]; - // If the mipmap is chromakeyed, check if the texel's color - // is equivalent to the chroma key's color index. - alpha = 0xff; - if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)) - alpha = 0x00; - // hope compiler will get this switch out of the loops (dreams...) // gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?) // Alam: SRB2 uses Mingw, HUGS @@ -512,11 +506,7 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex) #ifndef NO_PNG_LUMPS if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) - { - // Dummy variables. - INT32 pngwidth, pngheight; - realpatch = (patch_t *)Picture_PNGConvert(pdata, PICFMT_PATCH, &pngwidth, &pngheight, NULL, NULL, lumplength, NULL, 0); - } + realpatch = (patch_t *)Picture_PNGConvert(pdata, PICFMT_PATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0); else #endif #ifdef WALLFLATS @@ -558,12 +548,7 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm // lump is a png so convert it size_t len = W_LumpLengthPwad(grPatch->wadnum, grPatch->lumpnum); if ((patch != NULL) && Picture_IsLumpPNG((const UINT8 *)patch, len)) - { - // Dummy variables. - INT32 pngwidth, pngheight; - INT16 topoffset, leftoffset; - patch = (patch_t *)Picture_PNGConvert((const UINT8 *)patch, PICFMT_PATCH, &pngwidth, &pngheight, &topoffset, &leftoffset, len, NULL, 0); - } + patch = (patch_t *)Picture_PNGConvert((const UINT8 *)patch, PICFMT_PATCH, NULL, NULL, NULL, NULL, len, NULL, 0); #endif // don't do it twice (like a cache) @@ -885,7 +870,7 @@ void HWR_GetLevelFlat(levelflat_t *levelflat) #ifndef NO_PNG_LUMPS else if (levelflat->type == LEVELFLAT_PNG) { - INT32 pngwidth, pngheight; + INT32 pngwidth = 0, pngheight = 0; GLMipmap_t *mipmap = levelflat->mipmap; UINT8 *flat; size_t size; diff --git a/src/r_picformats.c b/src/r_picformats.c index d48bbaaf2..f77d7dc48 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -798,6 +798,8 @@ static void PNG_warn(png_structp PNG, png_const_charp pngtext) CONS_Debug(DBG_RENDER, "libpng warning at %p: %s", PNG, pngtext); } +static png_byte grAb_chunk[5] = {'g', 'r', 'A', 'b', (png_byte)'\0'}; + static png_bytep *PNG_Read( const UINT8 *png, INT32 *w, INT32 *h, INT16 *topoffset, INT16 *leftoffset, @@ -824,8 +826,6 @@ static png_bytep *PNG_Read( png_io_t png_io; png_bytep *row_pointers; - - png_byte grAb_chunk[5] = {'g', 'r', 'A', 'b', (png_byte)'\0'}; png_voidp *user_chunk_ptr; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, PNG_error, PNG_warn); @@ -852,7 +852,6 @@ static png_bytep *PNG_Read( png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); #endif - // set our own read function png_io.buffer = png; png_io.size = size; png_io.position = 0; @@ -896,7 +895,7 @@ static png_bytep *PNG_Read( // color is present on the image, the palette flag is disabled. png_get_tRNS(png_ptr, png_info_ptr, &trans, &trans_num, &trans_values); - if (trans_num == 256) + if (trans && trans_num == 256) { int i; for (i = 0; i < trans_num; i++) @@ -950,7 +949,6 @@ static png_bytep *PNG_Read( *topoffset = (INT16)BIGENDIAN_LONG(*offsets); } - // bye png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); if (chunk.data) Z_Free(chunk.data); @@ -987,12 +985,28 @@ void *Picture_PNGConvert( png_uint_32 x, y; png_bytep row; boolean palette = false; - png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, &palette, insize); - png_uint_32 width = *w, height = *h; + png_bytep *row_pointers = NULL; + png_uint_32 width, height; + + INT32 pngwidth, pngheight; + INT16 loffs = 0, toffs = 0; if (png == NULL) I_Error("Picture_PNGConvert: picture was NULL!"); + if (w == NULL) + w = &pngwidth; + if (h == NULL) + h = &pngheight; + if (topoffset == NULL) + topoffset = &toffs; + if (leftoffset == NULL) + leftoffset = &loffs; + + row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, &palette, insize); + width = *w; + height = *h; + if (row_pointers == NULL) I_Error("Picture_PNGConvert: row_pointers was NULL!"); @@ -1158,7 +1172,6 @@ void *Picture_PNGConvert( { void *converted; pictureformat_t informat = PICFMT_NONE; - INT16 patleftoffset = 0, pattopoffset = 0; // Figure out the format of the flat, from the bit depth of the output format switch (outbpp) @@ -1174,14 +1187,8 @@ void *Picture_PNGConvert( break; } - // Also find out if leftoffset and topoffset aren't pointing to NULL. - if (leftoffset) - patleftoffset = *leftoffset; - if (topoffset) - pattopoffset = *topoffset; - // Now, convert it! - converted = Picture_PatchConvert(informat, flat, outformat, insize, outsize, (INT16)width, (INT16)height, patleftoffset, pattopoffset, flags); + converted = Picture_PatchConvert(informat, flat, outformat, insize, outsize, (INT16)width, (INT16)height, loffs, toffs, flags); Z_Free(flat); return converted; } @@ -1195,10 +1202,12 @@ void *Picture_PNGConvert( * \param png The PNG image. * \param width A pointer to the input picture's width. * \param height A pointer to the input picture's height. + * \param topoffset A pointer to the input picture's vertical offset. + * \param leftoffset A pointer to the input picture's horizontal offset. * \param size The input picture's size. * \return True if reading the file succeeded, false if it failed. */ -boolean Picture_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size) +boolean Picture_PNGDimensions(UINT8 *png, INT32 *width, INT32 *height, INT16 *topoffset, INT16 *leftoffset, size_t size) { png_structp png_ptr; png_infop png_info_ptr; @@ -1211,9 +1220,9 @@ boolean Picture_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t si #endif png_io_t png_io; + png_voidp *user_chunk_ptr; - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, - PNG_error, PNG_warn); + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, PNG_error, PNG_warn); if (!png_ptr) I_Error("Picture_PNGDimensions: Couldn't initialize libpng!"); @@ -1237,23 +1246,41 @@ boolean Picture_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t si png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); #endif - // set our own read function png_io.buffer = png; png_io.size = size; png_io.position = 0; png_set_read_fn(png_ptr, &png_io, PNG_IOReader); + memset(&chunk, 0x00, sizeof(png_chunk_t)); + chunkname = grAb_chunk; // I want to read a grAb chunk + + user_chunk_ptr = png_get_user_chunk_ptr(png_ptr); + png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, PNG_ChunkReader); + png_set_keep_unknown_chunks(png_ptr, 2, chunkname, 1); + #ifdef PNG_SET_USER_LIMITS_SUPPORTED png_set_user_limits(png_ptr, 2048, 2048); #endif png_read_info(png_ptr, png_info_ptr); + png_get_IHDR(png_ptr, png_info_ptr, &w, &h, &bit_depth, &color_type, NULL, NULL, NULL); - png_get_IHDR(png_ptr, png_info_ptr, &w, &h, &bit_depth, &color_type, - NULL, NULL, NULL); + // Read grAB chunk + if ((topoffset || leftoffset) && (chunk.data != NULL)) + { + INT32 *offsets = (INT32 *)chunk.data; + // read left offset + if (leftoffset != NULL) + *leftoffset = (INT16)BIGENDIAN_LONG(*offsets); + offsets++; + // read top offset + if (topoffset != NULL) + *topoffset = (INT16)BIGENDIAN_LONG(*offsets); + } - // okay done. stop. png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); + if (chunk.data) + Z_Free(chunk.data); *width = (INT32)w; *height = (INT32)h; @@ -1634,11 +1661,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp lumplength = W_LumpLength(lump); if (Picture_IsLumpPNG((const UINT8 *)patch, lumplength)) - { - INT32 pngwidth, pngheight; - INT16 toffs, loffs; - patch = (patch_t *)Picture_PNGConvert((const UINT8 *)patch, PICFMT_PATCH, &pngwidth, &pngheight, &toffs, &loffs, lumplength, NULL, 0); - } + patch = (patch_t *)Picture_PNGConvert((const UINT8 *)patch, PICFMT_PATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0); else #endif // Because there's something wrong with SPR_DFLM, I guess diff --git a/src/r_picformats.h b/src/r_picformats.h index 32754d64e..3ee76a92f 100644 --- a/src/r_picformats.h +++ b/src/r_picformats.h @@ -110,7 +110,7 @@ void *Picture_PNGConvert( INT16 *topoffset, INT16 *leftoffset, size_t insize, size_t *outsize, pictureflags_t flags); -boolean Picture_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size); +boolean Picture_PNGDimensions(UINT8 *png, INT32 *width, INT32 *height, INT16 *topoffset, INT16 *leftoffset, size_t size); #endif #define PICTURE_PNG_USELOOKUP diff --git a/src/r_textures.c b/src/r_textures.c index ef45863a2..a34c29c72 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -397,11 +397,7 @@ UINT8 *R_GenerateTexture(size_t texnum) #ifndef NO_PNG_LUMPS if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) - { - // Dummy variables. - INT32 pngwidth, pngheight; - realpatch = (patch_t *)Picture_PNGConvert((UINT8 *)realpatch, PICFMT_PATCH, &pngwidth, &pngheight, NULL, NULL, lumplength, NULL, 0); - } + realpatch = (patch_t *)Picture_PNGConvert((UINT8 *)realpatch, PICFMT_PATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0); else #endif #ifdef WALLFLATS @@ -800,10 +796,10 @@ Rloadflats (INT32 i, INT32 w) #ifndef NO_PNG_LUMPS if (Picture_IsLumpPNG((UINT8 *)flatlump, lumplength)) { - INT16 width, height; - Picture_PNGDimensions((UINT8 *)flatlump, &width, &height, lumplength); - texture->width = width; - texture->height = height; + INT32 width, height; + Picture_PNGDimensions((UINT8 *)flatlump, &width, &height, NULL, NULL, lumplength); + texture->width = (INT16)width; + texture->height = (INT16)height; } else #endif @@ -898,10 +894,10 @@ Rloadtextures (INT32 i, INT32 w) #ifndef NO_PNG_LUMPS if (Picture_IsLumpPNG((UINT8 *)patchlump, lumplength)) { - INT16 width, height; - Picture_PNGDimensions((UINT8 *)patchlump, &width, &height, lumplength); - texture->width = width; - texture->height = height; + INT32 width, height; + Picture_PNGDimensions((UINT8 *)patchlump, &width, &height, NULL, NULL, lumplength); + texture->width = (INT16)width; + texture->height = (INT16)height; } else #endif diff --git a/src/r_things.c b/src/r_things.c index a3ce90991..382eed560 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -259,6 +259,12 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 { if (memcmp(lumpinfo[l].name,sprname,4)==0) { + INT32 width, height; + INT16 topoffset, leftoffset; +#ifndef NO_PNG_LUMPS + boolean isPNG = false; +#endif + frame = R_Char2Frame(lumpinfo[l].name[4]); rotation = R_Char2Rotation(lumpinfo[l].name[5]); @@ -274,28 +280,35 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 // store sprite info in lookup tables //FIXME : numspritelumps do not duplicate sprite replacements - W_ReadLumpHeaderPwad(wadnum, l, &patch, sizeof (patch_t), 0); + #ifndef NO_PNG_LUMPS { patch_t *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC); size_t len = W_LumpLengthPwad(wadnum, l); - // lump is a png so convert it + if (Picture_IsLumpPNG((UINT8 *)png, len)) { - // Dummy variables. - INT32 pngwidth, pngheight; - INT16 topoffset, leftoffset; - patch_t *converted = (patch_t *)Picture_PNGConvert((UINT8 *)png, PICFMT_PATCH, &pngwidth, &pngheight, &topoffset, &leftoffset, len, NULL, 0); - M_Memcpy(&patch, converted, sizeof(INT16)*4); // only copy the header because that's all we need - Z_Free(converted); + Picture_PNGDimensions((UINT8 *)png, &width, &height, &topoffset, &leftoffset, len); + isPNG = true; } + Z_Free(png); } + + if (!isPNG) #endif - spritecachedinfo[numspritelumps].width = SHORT(patch.width)< Date: Fri, 2 Oct 2020 16:07:15 -0700 Subject: [PATCH 129/136] Update patch.pk3 --- src/config.h.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/config.h.in b/src/config.h.in index 7498bd9c0..a6f43a7d7 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -33,12 +33,13 @@ * Last updated 2020 / 07 / 07 - v2.2.5 - player.dta & patch.pk3 * Last updated 2020 / 07 / 10 - v2.2.6 - player.dta & patch.pk3 * Last updated 2020 / 09 / 27 - v2.2.7 - patch.pk3 + * Last updated 2020 / 10 / 02 - v2.2.8 - patch.pk3 */ #define ASSET_HASH_SRB2_PK3 "0277c9416756627004e83cbb5b2e3e28" #define ASSET_HASH_ZONES_PK3 "f7e88afb6af7996a834c7d663144bead" #define ASSET_HASH_PLAYER_DTA "49dad7b24634c89728cc3e0b689e12bb" #ifdef USE_PATCH_DTA -#define ASSET_HASH_PATCH_PK3 "6b200f3f49af5478935b104eb972a898" +#define ASSET_HASH_PATCH_PK3 "466cdf60075262b3f5baa5e07f0999e8" #endif #endif From 7b812a8e273fc3bbda84f87207e46a24f2d90b33 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 2 Oct 2020 16:10:21 -0700 Subject: [PATCH 130/136] Update version to 2.2.8 --- src/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/version.h b/src/version.h index 73ebc497a..ece084beb 100644 --- a/src/version.h +++ b/src/version.h @@ -1,4 +1,4 @@ -#define SRB2VERSION "2.2.7"/* this must be the first line, for cmake !! */ +#define SRB2VERSION "2.2.8"/* this must be the first line, for cmake !! */ // The Modification ID; must be obtained from a Master Server Admin ( https://mb.srb2.org/showgroups.php ). // DO NOT try to set this otherwise, or your modification will be unplayable through the Master Server. @@ -9,7 +9,7 @@ // it's only for detection of the version the player is using so the MS can alert them of an update. // Only set it higher, not lower, obviously. // Note that we use this to help keep internal testing in check; this is why v2.2.0 is not version "1". -#define MODVERSION 48 +#define MODVERSION 49 // Define this as a prerelease version suffix // #define BETAVERSION "RC1" From 92cd4fd8ba2d7a57536f2fa870ada7f440c188e4 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 2 Oct 2020 16:11:39 -0700 Subject: [PATCH 131/136] Update Srb2win.rc --- src/win32/Srb2win.rc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/win32/Srb2win.rc b/src/win32/Srb2win.rc index 2538701dc..5ba366bda 100644 --- a/src/win32/Srb2win.rc +++ b/src/win32/Srb2win.rc @@ -66,8 +66,8 @@ END #include "../doomdef.h" // Needed for version string VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,2,7,0 - PRODUCTVERSION 2,2,7,0 + FILEVERSION 2,2,8,0 + PRODUCTVERSION 2,2,8,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L From f75aa6cd18c3552c6ea4de4219e1239d32b26c15 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Fri, 2 Oct 2020 20:40:42 -0300 Subject: [PATCH 132/136] Use leftoffset and topoffset, not loffs and toffs --- src/r_picformats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_picformats.c b/src/r_picformats.c index f77d7dc48..95fe23aeb 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -1188,7 +1188,7 @@ void *Picture_PNGConvert( } // Now, convert it! - converted = Picture_PatchConvert(informat, flat, outformat, insize, outsize, (INT16)width, (INT16)height, loffs, toffs, flags); + converted = Picture_PatchConvert(informat, flat, outformat, insize, outsize, (INT16)width, (INT16)height, *leftoffset, *topoffset, flags); Z_Free(flat); return converted; } From b54bb3b596d48522e8a17cb2b075326633a7151c Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 2 Oct 2020 18:05:12 -0700 Subject: [PATCH 133/136] Update appveyor.yml version --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 820c77e8b..2acc2f712 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.2.6.{branch}-{build} +version: 2.2.8.{branch}-{build} os: MinGW environment: From e97a5d60877721c28bea56952e16a3ac381e2535 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Mon, 12 Oct 2020 16:59:47 -0500 Subject: [PATCH 134/136] Fix gzip failing to compress SRB2's objdump on non-Windows. --- src/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index ee0d50625..2fe0b26cd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -606,8 +606,9 @@ ifndef VALGRIND ifndef NOOBJDUMP @echo Dumping debugging info $(OBJDUMP) $(OBJDUMP_OPTS) $(BIN)/$(EXENAME) > $(BIN)/$(DBGNAME).txt +ifdef WINDOWSHELL -$(GZIP) $(GZIP_OPTS) $(BIN)/$(DBGNAME).txt -ifndef WINDOWSHELL +else -$(GZIP) $(GZIP_OPT2) $(BIN)/$(DBGNAME).txt endif endif @@ -627,8 +628,9 @@ endif reobjdump: @echo Redumping debugging info $(OBJDUMP) $(OBJDUMP_OPTS) $(BIN)/$(DBGNAME) > $(BIN)/$(DBGNAME).txt +ifdef WINDOWSHELL -$(GZIP) $(GZIP_OPTS) $(BIN)/$(DBGNAME).txt -ifndef WINDOWSHELL +else -$(GZIP) $(GZIP_OPT2) $(BIN)/$(DBGNAME).txt endif From 0fb69e75e1519d7f852d358d0e290b6c9f5825e1 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Tue, 20 Oct 2020 15:44:01 -0500 Subject: [PATCH 135/136] Add multithreading option for CMake --- src/CMakeLists.txt | 2 ++ src/sdl/CMakeLists.txt | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 962f8f87a..0da48fd80 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -231,6 +231,8 @@ set(SRB2_CONFIG_HAVE_OPENMPT ON CACHE BOOL "Enable OpenMPT support.") set(SRB2_CONFIG_HAVE_CURL ON CACHE BOOL "Enable curl support, used for downloading files via HTTP.") +set(SRB2_CONFIG_HAVE_THREADS ON CACHE BOOL + "Enable multithreading support.") if(${CMAKE_SYSTEM} MATCHES Windows) set(SRB2_CONFIG_HAVE_MIXERX ON CACHE BOOL "Enable SDL Mixer X support.") diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt index 72f78188f..dab7f4934 100644 --- a/src/sdl/CMakeLists.txt +++ b/src/sdl/CMakeLists.txt @@ -55,6 +55,12 @@ set(SRB2_SDL2_HEADERS sdlmain.h ) +if(${SRB2_CONFIG_HAVE_THREADS}) + set(SRB2_HAVE_THREADS ON) + set(SRB2_SDL2_SOURCES ${SRB2_SDL2_SOURCES} i_threads.c) + add_definitions(-DHAVE_THREADS) +endif() + source_group("Interface Code" FILES ${SRB2_SDL2_SOURCES} ${SRB2_SDL2_HEADERS}) # Dependency From e7b3e2598d983f8e6e6742ae80967ae49a3b7b47 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Tue, 20 Oct 2020 15:58:34 -0500 Subject: [PATCH 136/136] Add i_threads.h too --- src/CMakeLists.txt | 6 ++++++ src/sdl/CMakeLists.txt | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0da48fd80..3409c49d3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -472,6 +472,12 @@ if(${SRB2_CONFIG_HAVE_CURL}) endif() endif() +if(${SRB2_CONFIG_HAVE_THREADS}) + set(SRB2_HAVE_THREADS ON) + set(SRB2_CORE_HEADERS ${SRB2_CORE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/i_threads.h) + add_definitions(-DHAVE_THREADS) +endif() + if(${SRB2_CONFIG_HWRENDER}) add_definitions(-DHWRENDER) set(SRB2_HWRENDER_SOURCES diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt index dab7f4934..bb5edf817 100644 --- a/src/sdl/CMakeLists.txt +++ b/src/sdl/CMakeLists.txt @@ -56,9 +56,7 @@ set(SRB2_SDL2_HEADERS ) if(${SRB2_CONFIG_HAVE_THREADS}) - set(SRB2_HAVE_THREADS ON) set(SRB2_SDL2_SOURCES ${SRB2_SDL2_SOURCES} i_threads.c) - add_definitions(-DHAVE_THREADS) endif() source_group("Interface Code" FILES ${SRB2_SDL2_SOURCES} ${SRB2_SDL2_HEADERS})