From a8f828a066773a59fdf646299359d35ae0e845af Mon Sep 17 00:00:00 2001 From: wyatt8740 Date: Mon, 6 Jan 2014 12:02:16 -0500 Subject: Removed accidental upload of copyrighted map files --- WinQuake/d_sprite.c | 884 ++++++++++++++++++++++++++-------------------------- 1 file changed, 442 insertions(+), 442 deletions(-) (limited to 'WinQuake/d_sprite.c') diff --git a/WinQuake/d_sprite.c b/WinQuake/d_sprite.c index 2f02ad2..7adff96 100644 --- a/WinQuake/d_sprite.c +++ b/WinQuake/d_sprite.c @@ -1,442 +1,442 @@ -/* -Copyright (C) 1996-1997 Id Software, Inc. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ -// d_sprite.c: software top-level rasterization driver module for drawing -// sprites - -#include "quakedef.h" -#include "d_local.h" - -static int sprite_height; -static int minindex, maxindex; -static sspan_t *sprite_spans; - -#if !id386 - -/* -===================== -D_SpriteDrawSpans -===================== -*/ -void D_SpriteDrawSpans (sspan_t *pspan) -{ - int count, spancount, izistep; - int izi; - byte *pbase, *pdest; - fixed16_t s, t, snext, tnext, sstep, tstep; - float sdivz, tdivz, zi, z, du, dv, spancountminus1; - float sdivz8stepu, tdivz8stepu, zi8stepu; - byte btemp; - short *pz; - - sstep = 0; // keep compiler happy - tstep = 0; // ditto - - pbase = cacheblock; - - sdivz8stepu = d_sdivzstepu * 8; - tdivz8stepu = d_tdivzstepu * 8; - zi8stepu = d_zistepu * 8; - -// we count on FP exceptions being turned off to avoid range problems - izistep = (int)(d_zistepu * 0x8000 * 0x10000); - - do - { - pdest = (byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u; - pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; - - count = pspan->count; - - if (count <= 0) - goto NextSpan; - - // calculate the initial s/z, t/z, 1/z, s, and t and clamp - du = (float)pspan->u; - dv = (float)pspan->v; - - sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; - tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; - zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; - z = (float)0x10000 / zi; // prescale to 16.16 fixed-point - // we count on FP exceptions being turned off to avoid range problems - izi = (int)(zi * 0x8000 * 0x10000); - - s = (int)(sdivz * z) + sadjust; - if (s > bbextents) - s = bbextents; - else if (s < 0) - s = 0; - - t = (int)(tdivz * z) + tadjust; - if (t > bbextentt) - t = bbextentt; - else if (t < 0) - t = 0; - - do - { - // calculate s and t at the far end of the span - if (count >= 8) - spancount = 8; - else - spancount = count; - - count -= spancount; - - if (count) - { - // calculate s/z, t/z, zi->fixed s and t at far end of span, - // calculate s and t steps across span by shifting - sdivz += sdivz8stepu; - tdivz += tdivz8stepu; - zi += zi8stepu; - z = (float)0x10000 / zi; // prescale to 16.16 fixed-point - - snext = (int)(sdivz * z) + sadjust; - if (snext > bbextents) - snext = bbextents; - else if (snext < 8) - snext = 8; // prevent round-off error on <0 steps from - // from causing overstepping & running off the - // edge of the texture - - tnext = (int)(tdivz * z) + tadjust; - if (tnext > bbextentt) - tnext = bbextentt; - else if (tnext < 8) - tnext = 8; // guard against round-off error on <0 steps - - sstep = (snext - s) >> 3; - tstep = (tnext - t) >> 3; - } - else - { - // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so - // can't step off polygon), clamp, calculate s and t steps across - // span by division, biasing steps low so we don't run off the - // texture - spancountminus1 = (float)(spancount - 1); - sdivz += d_sdivzstepu * spancountminus1; - tdivz += d_tdivzstepu * spancountminus1; - zi += d_zistepu * spancountminus1; - z = (float)0x10000 / zi; // prescale to 16.16 fixed-point - snext = (int)(sdivz * z) + sadjust; - if (snext > bbextents) - snext = bbextents; - else if (snext < 8) - snext = 8; // prevent round-off error on <0 steps from - // from causing overstepping & running off the - // edge of the texture - - tnext = (int)(tdivz * z) + tadjust; - if (tnext > bbextentt) - tnext = bbextentt; - else if (tnext < 8) - tnext = 8; // guard against round-off error on <0 steps - - if (spancount > 1) - { - sstep = (snext - s) / (spancount - 1); - tstep = (tnext - t) / (spancount - 1); - } - } - - do - { - btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth); - if (btemp != 255) - { - if (*pz <= (izi >> 16)) - { - *pz = izi >> 16; - *pdest = btemp; - } - } - - izi += izistep; - pdest++; - pz++; - s += sstep; - t += tstep; - } while (--spancount > 0); - - s = snext; - t = tnext; - - } while (count > 0); - -NextSpan: - pspan++; - - } while (pspan->count != DS_SPAN_LIST_END); -} - -#endif - - -/* -===================== -D_SpriteScanLeftEdge -===================== -*/ -void D_SpriteScanLeftEdge (void) -{ - int i, v, itop, ibottom, lmaxindex; - emitpoint_t *pvert, *pnext; - sspan_t *pspan; - float du, dv, vtop, vbottom, slope; - fixed16_t u, u_step; - - pspan = sprite_spans; - i = minindex; - if (i == 0) - i = r_spritedesc.nump; - - lmaxindex = maxindex; - if (lmaxindex == 0) - lmaxindex = r_spritedesc.nump; - - vtop = ceil (r_spritedesc.pverts[i].v); - - do - { - pvert = &r_spritedesc.pverts[i]; - pnext = pvert - 1; - - vbottom = ceil (pnext->v); - - if (vtop < vbottom) - { - du = pnext->u - pvert->u; - dv = pnext->v - pvert->v; - slope = du / dv; - u_step = (int)(slope * 0x10000); - // adjust u to ceil the integer portion - u = (int)((pvert->u + (slope * (vtop - pvert->v))) * 0x10000) + - (0x10000 - 1); - itop = (int)vtop; - ibottom = (int)vbottom; - - for (v=itop ; vu = u >> 16; - pspan->v = v; - u += u_step; - pspan++; - } - } - - vtop = vbottom; - - i--; - if (i == 0) - i = r_spritedesc.nump; - - } while (i != lmaxindex); -} - - -/* -===================== -D_SpriteScanRightEdge -===================== -*/ -void D_SpriteScanRightEdge (void) -{ - int i, v, itop, ibottom; - emitpoint_t *pvert, *pnext; - sspan_t *pspan; - float du, dv, vtop, vbottom, slope, uvert, unext, vvert, vnext; - fixed16_t u, u_step; - - pspan = sprite_spans; - i = minindex; - - vvert = r_spritedesc.pverts[i].v; - if (vvert < r_refdef.fvrecty_adj) - vvert = r_refdef.fvrecty_adj; - if (vvert > r_refdef.fvrectbottom_adj) - vvert = r_refdef.fvrectbottom_adj; - - vtop = ceil (vvert); - - do - { - pvert = &r_spritedesc.pverts[i]; - pnext = pvert + 1; - - vnext = pnext->v; - if (vnext < r_refdef.fvrecty_adj) - vnext = r_refdef.fvrecty_adj; - if (vnext > r_refdef.fvrectbottom_adj) - vnext = r_refdef.fvrectbottom_adj; - - vbottom = ceil (vnext); - - if (vtop < vbottom) - { - uvert = pvert->u; - if (uvert < r_refdef.fvrectx_adj) - uvert = r_refdef.fvrectx_adj; - if (uvert > r_refdef.fvrectright_adj) - uvert = r_refdef.fvrectright_adj; - - unext = pnext->u; - if (unext < r_refdef.fvrectx_adj) - unext = r_refdef.fvrectx_adj; - if (unext > r_refdef.fvrectright_adj) - unext = r_refdef.fvrectright_adj; - - du = unext - uvert; - dv = vnext - vvert; - slope = du / dv; - u_step = (int)(slope * 0x10000); - // adjust u to ceil the integer portion - u = (int)((uvert + (slope * (vtop - vvert))) * 0x10000) + - (0x10000 - 1); - itop = (int)vtop; - ibottom = (int)vbottom; - - for (v=itop ; vcount = (u >> 16) - pspan->u; - u += u_step; - pspan++; - } - } - - vtop = vbottom; - vvert = vnext; - - i++; - if (i == r_spritedesc.nump) - i = 0; - - } while (i != maxindex); - - pspan->count = DS_SPAN_LIST_END; // mark the end of the span list -} - - -/* -===================== -D_SpriteCalculateGradients -===================== -*/ -void D_SpriteCalculateGradients (void) -{ - vec3_t p_normal, p_saxis, p_taxis, p_temp1; - float distinv; - - TransformVector (r_spritedesc.vpn, p_normal); - TransformVector (r_spritedesc.vright, p_saxis); - TransformVector (r_spritedesc.vup, p_taxis); - VectorInverse (p_taxis); - - distinv = 1.0 / (-DotProduct (modelorg, r_spritedesc.vpn)); - - d_sdivzstepu = p_saxis[0] * xscaleinv; - d_tdivzstepu = p_taxis[0] * xscaleinv; - - d_sdivzstepv = -p_saxis[1] * yscaleinv; - d_tdivzstepv = -p_taxis[1] * yscaleinv; - - d_zistepu = p_normal[0] * xscaleinv * distinv; - d_zistepv = -p_normal[1] * yscaleinv * distinv; - - d_sdivzorigin = p_saxis[2] - xcenter * d_sdivzstepu - - ycenter * d_sdivzstepv; - d_tdivzorigin = p_taxis[2] - xcenter * d_tdivzstepu - - ycenter * d_tdivzstepv; - d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu - - ycenter * d_zistepv; - - TransformVector (modelorg, p_temp1); - - sadjust = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - - (-(cachewidth >> 1) << 16); - tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - - (-(sprite_height >> 1) << 16); - -// -1 (-epsilon) so we never wander off the edge of the texture - bbextents = (cachewidth << 16) - 1; - bbextentt = (sprite_height << 16) - 1; -} - - -/* -===================== -D_DrawSprite -===================== -*/ -void D_DrawSprite (void) -{ - int i, nump; - float ymin, ymax; - emitpoint_t *pverts; - sspan_t spans[MAXHEIGHT+1]; - - sprite_spans = spans; - -// find the top and bottom vertices, and make sure there's at least one scan to -// draw - ymin = 999999.9; - ymax = -999999.9; - pverts = r_spritedesc.pverts; - - for (i=0 ; iv < ymin) - { - ymin = pverts->v; - minindex = i; - } - - if (pverts->v > ymax) - { - ymax = pverts->v; - maxindex = i; - } - - pverts++; - } - - ymin = ceil (ymin); - ymax = ceil (ymax); - - if (ymin >= ymax) - return; // doesn't cross any scans at all - - cachewidth = r_spritedesc.pspriteframe->width; - sprite_height = r_spritedesc.pspriteframe->height; - cacheblock = (byte *)&r_spritedesc.pspriteframe->pixels[0]; - -// copy the first vertex to the last vertex, so we don't have to deal with -// wrapping - nump = r_spritedesc.nump; - pverts = r_spritedesc.pverts; - pverts[nump] = pverts[0]; - - D_SpriteCalculateGradients (); - D_SpriteScanLeftEdge (); - D_SpriteScanRightEdge (); - D_SpriteDrawSpans (sprite_spans); -} - +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// d_sprite.c: software top-level rasterization driver module for drawing +// sprites + +#include "quakedef.h" +#include "d_local.h" + +static int sprite_height; +static int minindex, maxindex; +static sspan_t *sprite_spans; + +#if !id386 + +/* +===================== +D_SpriteDrawSpans +===================== +*/ +void D_SpriteDrawSpans (sspan_t *pspan) +{ + int count, spancount, izistep; + int izi; + byte *pbase, *pdest; + fixed16_t s, t, snext, tnext, sstep, tstep; + float sdivz, tdivz, zi, z, du, dv, spancountminus1; + float sdivz8stepu, tdivz8stepu, zi8stepu; + byte btemp; + short *pz; + + sstep = 0; // keep compiler happy + tstep = 0; // ditto + + pbase = cacheblock; + + sdivz8stepu = d_sdivzstepu * 8; + tdivz8stepu = d_tdivzstepu * 8; + zi8stepu = d_zistepu * 8; + +// we count on FP exceptions being turned off to avoid range problems + izistep = (int)(d_zistepu * 0x8000 * 0x10000); + + do + { + pdest = (byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u; + pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; + + count = pspan->count; + + if (count <= 0) + goto NextSpan; + + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float)pspan->u; + dv = (float)pspan->v; + + sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; + tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + // we count on FP exceptions being turned off to avoid range problems + izi = (int)(zi * 0x8000 * 0x10000); + + s = (int)(sdivz * z) + sadjust; + if (s > bbextents) + s = bbextents; + else if (s < 0) + s = 0; + + t = (int)(tdivz * z) + tadjust; + if (t > bbextentt) + t = bbextentt; + else if (t < 0) + t = 0; + + do + { + // calculate s and t at the far end of the span + if (count >= 8) + spancount = 8; + else + spancount = count; + + count -= spancount; + + if (count) + { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivz8stepu; + tdivz += tdivz8stepu; + zi += zi8stepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on <0 steps + + sstep = (snext - s) >> 3; + tstep = (tnext - t) >> 3; + } + else + { + // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so + // can't step off polygon), clamp, calculate s and t steps across + // span by division, biasing steps low so we don't run off the + // texture + spancountminus1 = (float)(spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on <0 steps + + if (spancount > 1) + { + sstep = (snext - s) / (spancount - 1); + tstep = (tnext - t) / (spancount - 1); + } + } + + do + { + btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth); + if (btemp != 255) + { + if (*pz <= (izi >> 16)) + { + *pz = izi >> 16; + *pdest = btemp; + } + } + + izi += izistep; + pdest++; + pz++; + s += sstep; + t += tstep; + } while (--spancount > 0); + + s = snext; + t = tnext; + + } while (count > 0); + +NextSpan: + pspan++; + + } while (pspan->count != DS_SPAN_LIST_END); +} + +#endif + + +/* +===================== +D_SpriteScanLeftEdge +===================== +*/ +void D_SpriteScanLeftEdge (void) +{ + int i, v, itop, ibottom, lmaxindex; + emitpoint_t *pvert, *pnext; + sspan_t *pspan; + float du, dv, vtop, vbottom, slope; + fixed16_t u, u_step; + + pspan = sprite_spans; + i = minindex; + if (i == 0) + i = r_spritedesc.nump; + + lmaxindex = maxindex; + if (lmaxindex == 0) + lmaxindex = r_spritedesc.nump; + + vtop = ceil (r_spritedesc.pverts[i].v); + + do + { + pvert = &r_spritedesc.pverts[i]; + pnext = pvert - 1; + + vbottom = ceil (pnext->v); + + if (vtop < vbottom) + { + du = pnext->u - pvert->u; + dv = pnext->v - pvert->v; + slope = du / dv; + u_step = (int)(slope * 0x10000); + // adjust u to ceil the integer portion + u = (int)((pvert->u + (slope * (vtop - pvert->v))) * 0x10000) + + (0x10000 - 1); + itop = (int)vtop; + ibottom = (int)vbottom; + + for (v=itop ; vu = u >> 16; + pspan->v = v; + u += u_step; + pspan++; + } + } + + vtop = vbottom; + + i--; + if (i == 0) + i = r_spritedesc.nump; + + } while (i != lmaxindex); +} + + +/* +===================== +D_SpriteScanRightEdge +===================== +*/ +void D_SpriteScanRightEdge (void) +{ + int i, v, itop, ibottom; + emitpoint_t *pvert, *pnext; + sspan_t *pspan; + float du, dv, vtop, vbottom, slope, uvert, unext, vvert, vnext; + fixed16_t u, u_step; + + pspan = sprite_spans; + i = minindex; + + vvert = r_spritedesc.pverts[i].v; + if (vvert < r_refdef.fvrecty_adj) + vvert = r_refdef.fvrecty_adj; + if (vvert > r_refdef.fvrectbottom_adj) + vvert = r_refdef.fvrectbottom_adj; + + vtop = ceil (vvert); + + do + { + pvert = &r_spritedesc.pverts[i]; + pnext = pvert + 1; + + vnext = pnext->v; + if (vnext < r_refdef.fvrecty_adj) + vnext = r_refdef.fvrecty_adj; + if (vnext > r_refdef.fvrectbottom_adj) + vnext = r_refdef.fvrectbottom_adj; + + vbottom = ceil (vnext); + + if (vtop < vbottom) + { + uvert = pvert->u; + if (uvert < r_refdef.fvrectx_adj) + uvert = r_refdef.fvrectx_adj; + if (uvert > r_refdef.fvrectright_adj) + uvert = r_refdef.fvrectright_adj; + + unext = pnext->u; + if (unext < r_refdef.fvrectx_adj) + unext = r_refdef.fvrectx_adj; + if (unext > r_refdef.fvrectright_adj) + unext = r_refdef.fvrectright_adj; + + du = unext - uvert; + dv = vnext - vvert; + slope = du / dv; + u_step = (int)(slope * 0x10000); + // adjust u to ceil the integer portion + u = (int)((uvert + (slope * (vtop - vvert))) * 0x10000) + + (0x10000 - 1); + itop = (int)vtop; + ibottom = (int)vbottom; + + for (v=itop ; vcount = (u >> 16) - pspan->u; + u += u_step; + pspan++; + } + } + + vtop = vbottom; + vvert = vnext; + + i++; + if (i == r_spritedesc.nump) + i = 0; + + } while (i != maxindex); + + pspan->count = DS_SPAN_LIST_END; // mark the end of the span list +} + + +/* +===================== +D_SpriteCalculateGradients +===================== +*/ +void D_SpriteCalculateGradients (void) +{ + vec3_t p_normal, p_saxis, p_taxis, p_temp1; + float distinv; + + TransformVector (r_spritedesc.vpn, p_normal); + TransformVector (r_spritedesc.vright, p_saxis); + TransformVector (r_spritedesc.vup, p_taxis); + VectorInverse (p_taxis); + + distinv = 1.0 / (-DotProduct (modelorg, r_spritedesc.vpn)); + + d_sdivzstepu = p_saxis[0] * xscaleinv; + d_tdivzstepu = p_taxis[0] * xscaleinv; + + d_sdivzstepv = -p_saxis[1] * yscaleinv; + d_tdivzstepv = -p_taxis[1] * yscaleinv; + + d_zistepu = p_normal[0] * xscaleinv * distinv; + d_zistepv = -p_normal[1] * yscaleinv * distinv; + + d_sdivzorigin = p_saxis[2] - xcenter * d_sdivzstepu - + ycenter * d_sdivzstepv; + d_tdivzorigin = p_taxis[2] - xcenter * d_tdivzstepu - + ycenter * d_tdivzstepv; + d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu - + ycenter * d_zistepv; + + TransformVector (modelorg, p_temp1); + + sadjust = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - + (-(cachewidth >> 1) << 16); + tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - + (-(sprite_height >> 1) << 16); + +// -1 (-epsilon) so we never wander off the edge of the texture + bbextents = (cachewidth << 16) - 1; + bbextentt = (sprite_height << 16) - 1; +} + + +/* +===================== +D_DrawSprite +===================== +*/ +void D_DrawSprite (void) +{ + int i, nump; + float ymin, ymax; + emitpoint_t *pverts; + sspan_t spans[MAXHEIGHT+1]; + + sprite_spans = spans; + +// find the top and bottom vertices, and make sure there's at least one scan to +// draw + ymin = 999999.9; + ymax = -999999.9; + pverts = r_spritedesc.pverts; + + for (i=0 ; iv < ymin) + { + ymin = pverts->v; + minindex = i; + } + + if (pverts->v > ymax) + { + ymax = pverts->v; + maxindex = i; + } + + pverts++; + } + + ymin = ceil (ymin); + ymax = ceil (ymax); + + if (ymin >= ymax) + return; // doesn't cross any scans at all + + cachewidth = r_spritedesc.pspriteframe->width; + sprite_height = r_spritedesc.pspriteframe->height; + cacheblock = (byte *)&r_spritedesc.pspriteframe->pixels[0]; + +// copy the first vertex to the last vertex, so we don't have to deal with +// wrapping + nump = r_spritedesc.nump; + pverts = r_spritedesc.pverts; + pverts[nump] = pverts[0]; + + D_SpriteCalculateGradients (); + D_SpriteScanLeftEdge (); + D_SpriteScanRightEdge (); + D_SpriteDrawSpans (sprite_spans); +} + -- cgit v1.2.3