[
  {
    "path": "README.TXT",
    "content": "================\n= fbDOOM =\n================\n\nAdaptation of the original DOOM\nto be easily portable to framebuffer devices with minimal depenencies.\nRuns on:\n* Plain desktop Linux framebuffer,\n* Siglent SSA 3021X Spectrum Analyzer,\n* Frosted OS\n\nTo build (example):\n* cd fbdoom\n* make CROSS_COMPILE=arm-linux-gnueabihf-\n\n\nOriginal readme\n===============\n\nHere it is, at long last.  The DOOM source code is released for your\nnon-profit use.  You still need real DOOM data to work with this code.\nIf you don't actually own a real copy of one of the DOOMs, you should\nstill be able to find them at software stores.\n\nMany thanks to Bernd Kreimeier for taking the time to clean up the\nproject and make sure that it actually works.  Projects tends to rot if\nyou leave it alone for a few years, and it takes effort for someone to\ndeal with it again.\n\nThe bad news:  this code only compiles and runs on linux.  We couldn't\nrelease the dos code because of a copyrighted sound library we used\n(wow, was that a mistake -- I write my own sound code now), and I\nhonestly don't even know what happened to the port that microsoft did\nto windows.\n\nStill, the code is quite portable, and it should be straightforward to\nbring it up on just about any platform.\n\nI wrote this code a long, long time ago, and there are plenty of things\nthat seem downright silly in retrospect (using polar coordinates for\nclipping comes to mind), but overall it should still be a usefull base\nto experiment and build on.\n\nThe basic rendering concept -- horizontal and vertical lines of constant\nZ with fixed light shading per band was dead-on, but the implementation\ncould be improved dramatically from the original code if it were\nrevisited.  The way the rendering proceded from walls to floors to\nsprites could be collapsed into a single front-to-back walk of the bsp\ntree to collect information, then draw all the contents of a subsector\non the way back up the tree.  It requires treating floors and ceilings\nas polygons, rather than just the gaps between walls, and it requires\nclipping sprite billboards into subsector fragments, but it would be\nThe Right Thing.\n\nThe movement and line of sight checking against the lines is one of the\nbigger misses that I look back on.  It is messy code that had some\nfailure cases, and there was a vastly simpler (and faster) solution\nsitting in front of my face.  I used the BSP tree for rendering things,\nbut I didn't realize at the time that it could also be used for\nenvironment testing.  Replacing the line of sight test with a bsp line\nclip would be pretty easy.  Sweeping volumes for movement gets a bit\ntougher, and touches on many of the challenges faced in quake / quake2\nwith edge bevels on polyhedrons.\n\nSome project ideas:\n\nPort it to your favorite operating system.\n\nAdd some rendering features -- transparency, look up / down, slopes,\netc.\n\nAdd some game features -- weapons, jumping, ducking, flying, etc.\n\nCreate a packet server based internet game.\n\nCreate a client / server based internet game.\n\nDo a 3D accelerated version.  On modern hardware (fast pentium + 3DFX)\nyou probably wouldn't even need to be clever -- you could just draw the\nentire level and get reasonable speed.  With a touch of effort, it should\neasily lock at 60 fps (well, there are some issues with DOOM's 35 hz\ntimebase...).  The biggest issues would probably be the non-power of two\ntexture sizes and the walls composed of multiple textures.\n\n\nI don't have a real good guess at how many people are going to be\nplaying with this, but if significant projects are undertaken, it would\nbe cool to see a level of community cooperation.  I know that most early\nprojects are going to be rough hacks done in isolation, but I would be\nvery pleased to see a coordinated 'net release of an improved, backwards\ncompatable version of DOOM on multiple platforms next year.\n\nHave fun.\n\nJohn Carmack\n12-23-97\n"
  },
  {
    "path": "fbdoom/Makefile",
    "content": "################################################################\n#\n# $Id:$\n#\n# $Log:$\n#\n\nCROSS_COMPILE ?= #arm-linux-gnueabihf-\n\nifeq ($(V),1)\n\tVB=''\nelse\n\tVB=@\nendif\n\n#LIBS+=-lXext -lX11 -lnsl -lm -lSDL\n#CFLAGS+=-Wunused-const-variable=0 \n#CFLAGS+=-fsanitize=address\nOBJS+=$(OBJDIR)/i_video_fbdev.o\nOBJS+=$(OBJDIR)/i_input_tty.o\n\nCC=$(CROSS_COMPILE)gcc  # gcc or g++\nCFLAGS+=-ggdb3 -Os\nLDFLAGS+=-Wl,--gc-sections\nCFLAGS+=-ggdb3 -Wall -DNORMALUNIX -DLINUX -DSNDSERV # -DUSEASM\nLIBS+=-lm -lc\n\nifneq ($(NOSDL),1)\n\tLIBS+= -lSDL\nendif\n\n# subdirectory for objects\nOBJDIR=build\nOUTPUT=fbdoom\n\nSRC_DOOM = i_main.o dummy.o am_map.o doomdef.o doomstat.o dstrings.o d_event.o d_items.o d_iwad.o d_loop.o d_main.o d_mode.o d_net.o f_finale.o f_wipe.o g_game.o hu_lib.o hu_stuff.o info.o i_cdmus.o i_endoom.o i_joystick.o i_scale.o i_sound.o i_system.o i_timer.o memio.o m_argv.o m_bbox.o m_cheat.o m_config.o m_controls.o m_fixed.o m_menu.o m_misc.o m_random.o p_ceilng.o p_doors.o p_enemy.o p_floor.o p_inter.o p_lights.o p_map.o p_maputl.o p_mobj.o p_plats.o p_pspr.o p_saveg.o p_setup.o p_sight.o p_spec.o p_switch.o p_telept.o p_tick.o p_user.o r_bsp.o r_data.o r_draw.o r_main.o r_plane.o r_segs.o r_sky.o r_things.o sha1.o sounds.o statdump.o st_lib.o st_stuff.o s_sound.o tables.o v_video.o wi_stuff.o w_checksum.o w_file.o w_file_stdc_unbuffered.o w_main.o w_wad.o z_zone.o\nOBJS += $(addprefix $(OBJDIR)/, $(SRC_DOOM))\n\nall:\t $(OUTPUT)\n\nclean:\n\trm -rf $(OBJDIR)\n\trm -f $(OUTPUT)\n\trm -f $(OUTPUT).gdb\n\trm -f $(OUTPUT).map\n\n$(OUTPUT):\t$(OBJS)\n\t@echo [Linking $@]\n\t$(VB)$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) \\\n\t-o $(OUTPUT) $(LIBS) -Wl,-Map,$(OUTPUT).map\n\t@echo [Size]\n\t-$(CROSS_COMPILE)size $(OUTPUT)\n\n$(OBJS): | $(OBJDIR)\n\n$(OBJDIR):\n\tmkdir -p $(OBJDIR)\n\n$(OBJDIR)/%.o:\t%.c\n\t@echo [Compiling $<]\n\t$(VB)$(CC) $(CFLAGS) -c $< -o $@\n\nprint:\n\t@echo OBJS: $(OBJS)\n\n"
  },
  {
    "path": "fbdoom/am_map.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n//\n// DESCRIPTION:  the automap code\n//\n\n\n#include <stdio.h>\n\n#include \"deh_main.h\"\n\n#include \"z_zone.h\"\n#include \"doomkeys.h\"\n#include \"doomdef.h\"\n#include \"st_stuff.h\"\n#include \"p_local.h\"\n#include \"w_wad.h\"\n\n#include \"m_cheat.h\"\n#include \"m_controls.h\"\n#include \"m_misc.h\"\n#include \"i_system.h\"\n\n// Needs access to LFB.\n#include \"v_video.h\"\n\n// State.\n#include \"doomstat.h\"\n#include \"r_state.h\"\n\n// Data.\n#include \"dstrings.h\"\n\n#include \"am_map.h\"\n\n\n// For use if I do walls with outsides/insides\n#define REDS\t\t(256-5*16)\n#define REDRANGE\t16\n#define BLUES\t\t(256-4*16+8)\n#define BLUERANGE\t8\n#define GREENS\t\t(7*16)\n#define GREENRANGE\t16\n#define GRAYS\t\t(6*16)\n#define GRAYSRANGE\t16\n#define BROWNS\t\t(4*16)\n#define BROWNRANGE\t16\n#define YELLOWS\t\t(256-32+7)\n#define YELLOWRANGE\t1\n#define BLACK\t\t0\n#define WHITE\t\t(256-47)\n\n// Automap colors\n#define BACKGROUND\tBLACK\n#define YOURCOLORS\tWHITE\n#define YOURRANGE\t0\n#define WALLCOLORS\tREDS\n#define WALLRANGE\tREDRANGE\n#define TSWALLCOLORS\tGRAYS\n#define TSWALLRANGE\tGRAYSRANGE\n#define FDWALLCOLORS\tBROWNS\n#define FDWALLRANGE\tBROWNRANGE\n#define CDWALLCOLORS\tYELLOWS\n#define CDWALLRANGE\tYELLOWRANGE\n#define THINGCOLORS\tGREENS\n#define THINGRANGE\tGREENRANGE\n#define SECRETWALLCOLORS WALLCOLORS\n#define SECRETWALLRANGE WALLRANGE\n#define GRIDCOLORS\t(GRAYS + GRAYSRANGE/2)\n#define GRIDRANGE\t0\n#define XHAIRCOLORS\tGRAYS\n\n// drawing stuff\n\n#define AM_NUMMARKPOINTS 10\n\n// scale on entry\n#define INITSCALEMTOF (.2*FRACUNIT)\n// how much the automap moves window per tic in frame-buffer coordinates\n// moves 140 pixels in 1 second\n#define F_PANINC\t4\n// how much zoom-in per tic\n// goes to 2x in 1 second\n#define M_ZOOMIN        ((int) (1.02*FRACUNIT))\n// how much zoom-out per tic\n// pulls out to 0.5x in 1 second\n#define M_ZOOMOUT       ((int) (FRACUNIT/1.02))\n\n// translates between frame-buffer and map distances\n#define FTOM(x) FixedMul(((x)<<16),scale_ftom)\n#define MTOF(x) (FixedMul((x),scale_mtof)>>16)\n// translates between frame-buffer and map coordinates\n#define CXMTOF(x)  (f_x + MTOF((x)-m_x))\n#define CYMTOF(y)  (f_y + (f_h - MTOF((y)-m_y)))\n\n// the following is crap\n#define LINE_NEVERSEE ML_DONTDRAW\n\ntypedef struct\n{\n    int x, y;\n} fpoint_t;\n\ntypedef struct\n{\n    fpoint_t a, b;\n} fline_t;\n\ntypedef struct\n{\n    fixed_t\t\tx,y;\n} mpoint_t;\n\ntypedef struct\n{\n    mpoint_t a, b;\n} mline_t;\n\ntypedef struct\n{\n    fixed_t slp, islp;\n} islope_t;\n\n\n\n//\n// The vector graphics for the automap.\n//  A line drawing of the player pointing right,\n//   starting from the middle.\n//\n#define R ((8*PLAYERRADIUS)/7)\nmline_t player_arrow[] = {\n    { { -R+R/8, 0 }, { R, 0 } }, // -----\n    { { R, 0 }, { R-R/2, R/4 } },  // ----->\n    { { R, 0 }, { R-R/2, -R/4 } },\n    { { -R+R/8, 0 }, { -R-R/8, R/4 } }, // >---->\n    { { -R+R/8, 0 }, { -R-R/8, -R/4 } },\n    { { -R+3*R/8, 0 }, { -R+R/8, R/4 } }, // >>--->\n    { { -R+3*R/8, 0 }, { -R+R/8, -R/4 } }\n};\n#undef R\n\n#define R ((8*PLAYERRADIUS)/7)\nmline_t cheat_player_arrow[] = {\n    { { -R+R/8, 0 }, { R, 0 } }, // -----\n    { { R, 0 }, { R-R/2, R/6 } },  // ----->\n    { { R, 0 }, { R-R/2, -R/6 } },\n    { { -R+R/8, 0 }, { -R-R/8, R/6 } }, // >----->\n    { { -R+R/8, 0 }, { -R-R/8, -R/6 } },\n    { { -R+3*R/8, 0 }, { -R+R/8, R/6 } }, // >>----->\n    { { -R+3*R/8, 0 }, { -R+R/8, -R/6 } },\n    { { -R/2, 0 }, { -R/2, -R/6 } }, // >>-d--->\n    { { -R/2, -R/6 }, { -R/2+R/6, -R/6 } },\n    { { -R/2+R/6, -R/6 }, { -R/2+R/6, R/4 } },\n    { { -R/6, 0 }, { -R/6, -R/6 } }, // >>-dd-->\n    { { -R/6, -R/6 }, { 0, -R/6 } },\n    { { 0, -R/6 }, { 0, R/4 } },\n    { { R/6, R/4 }, { R/6, -R/7 } }, // >>-ddt->\n    { { R/6, -R/7 }, { R/6+R/32, -R/7-R/32 } },\n    { { R/6+R/32, -R/7-R/32 }, { R/6+R/10, -R/7 } }\n};\n#undef R\n\n#define R (FRACUNIT)\nmline_t triangle_guy[] = {\n    { { (fixed_t)(-.867*R), (fixed_t)(-.5*R) }, { (fixed_t)(.867*R ), (fixed_t)(-.5*R) } },\n    { { (fixed_t)(.867*R ), (fixed_t)(-.5*R) }, { (fixed_t)(0      ), (fixed_t)(R    ) } },\n    { { (fixed_t)(0      ), (fixed_t)(R    ) }, { (fixed_t)(-.867*R), (fixed_t)(-.5*R) } }\n};\n#undef R\n\n#define R (FRACUNIT)\nmline_t thintriangle_guy[] = {\n    { { (fixed_t)(-.5*R), (fixed_t)(-.7*R) }, { (fixed_t)(R    ), (fixed_t)(0    ) } },\n    { { (fixed_t)(R    ), (fixed_t)(0    ) }, { (fixed_t)(-.5*R), (fixed_t)(.7*R ) } },\n    { { (fixed_t)(-.5*R), (fixed_t)(.7*R ) }, { (fixed_t)(-.5*R), (fixed_t)(-.7*R) } }\n};\n#undef R\n\n\n\n\nstatic int \tcheating = 0;\nstatic int \tgrid = 0;\n\nstatic int \tleveljuststarted = 1; \t// kluge until AM_LevelInit() is called\n\nboolean    \tautomapactive = false;\nstatic int \tfinit_width = SCREENWIDTH;\nstatic int \tfinit_height = SCREENHEIGHT - 32;\n\n// location of window on screen\nstatic int \tf_x;\nstatic int\tf_y;\n\n// size of window on screen\nstatic int \tf_w;\nstatic int\tf_h;\n\nstatic int \tlightlev; \t\t// used for funky strobing effect\nstatic byte*\tfb; \t\t\t// pseudo-frame buffer\nstatic int \tamclock;\n\nstatic mpoint_t m_paninc; // how far the window pans each tic (map coords)\nstatic fixed_t \tmtof_zoommul; // how far the window zooms in each tic (map coords)\nstatic fixed_t \tftom_zoommul; // how far the window zooms in each tic (fb coords)\n\nstatic fixed_t \tm_x, m_y;   // LL x,y where the window is on the map (map coords)\nstatic fixed_t \tm_x2, m_y2; // UR x,y where the window is on the map (map coords)\n\n//\n// width/height of window on map (map coords)\n//\nstatic fixed_t \tm_w;\nstatic fixed_t\tm_h;\n\n// based on level size\nstatic fixed_t \tmin_x;\nstatic fixed_t\tmin_y; \nstatic fixed_t \tmax_x;\nstatic fixed_t  max_y;\n\nstatic fixed_t \tmax_w; // max_x-min_x,\nstatic fixed_t  max_h; // max_y-min_y\n\n// based on player size\nstatic fixed_t \tmin_w;\nstatic fixed_t  min_h;\n\n\nstatic fixed_t \tmin_scale_mtof; // used to tell when to stop zooming out\nstatic fixed_t \tmax_scale_mtof; // used to tell when to stop zooming in\n\n// old stuff for recovery later\nstatic fixed_t old_m_w, old_m_h;\nstatic fixed_t old_m_x, old_m_y;\n\n// old location used by the Follower routine\nstatic mpoint_t f_oldloc;\n\n// used by MTOF to scale from map-to-frame-buffer coords\nstatic fixed_t scale_mtof = (fixed_t)INITSCALEMTOF;\n// used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof)\nstatic fixed_t scale_ftom;\n\nstatic player_t *plr; // the player represented by an arrow\n\nstatic patch_t *marknums[10]; // numbers used for marking by the automap\nstatic mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are\nstatic int markpointnum = 0; // next point to be assigned\n\nstatic int followplayer = 1; // specifies whether to follow the player around\n\ncheatseq_t cheat_amap = CHEAT(\"iddt\", 0);\n\nstatic boolean stopped = true;\n\n// Calculates the slope and slope according to the x-axis of a line\n// segment in map coordinates (with the upright y-axis n' all) so\n// that it can be used with the brain-dead drawing stuff.\n\nvoid\nAM_getIslope\n( mline_t*\tml,\n  islope_t*\tis )\n{\n    int dx, dy;\n\n    dy = ml->a.y - ml->b.y;\n    dx = ml->b.x - ml->a.x;\n    if (!dy) is->islp = (dx<0?-INT_MAX:INT_MAX);\n    else is->islp = FixedDiv(dx, dy);\n    if (!dx) is->slp = (dy<0?-INT_MAX:INT_MAX);\n    else is->slp = FixedDiv(dy, dx);\n\n}\n\n//\n//\n//\nvoid AM_activateNewScale(void)\n{\n    m_x += m_w/2;\n    m_y += m_h/2;\n    m_w = FTOM(f_w);\n    m_h = FTOM(f_h);\n    m_x -= m_w/2;\n    m_y -= m_h/2;\n    m_x2 = m_x + m_w;\n    m_y2 = m_y + m_h;\n}\n\n//\n//\n//\nvoid AM_saveScaleAndLoc(void)\n{\n    old_m_x = m_x;\n    old_m_y = m_y;\n    old_m_w = m_w;\n    old_m_h = m_h;\n}\n\n//\n//\n//\nvoid AM_restoreScaleAndLoc(void)\n{\n\n    m_w = old_m_w;\n    m_h = old_m_h;\n    if (!followplayer)\n    {\n\tm_x = old_m_x;\n\tm_y = old_m_y;\n    } else {\n\tm_x = plr->mo->x - m_w/2;\n\tm_y = plr->mo->y - m_h/2;\n    }\n    m_x2 = m_x + m_w;\n    m_y2 = m_y + m_h;\n\n    // Change the scaling multipliers\n    scale_mtof = FixedDiv(f_w<<FRACBITS, m_w);\n    scale_ftom = FixedDiv(FRACUNIT, scale_mtof);\n}\n\n//\n// adds a marker at the current location\n//\nvoid AM_addMark(void)\n{\n    markpoints[markpointnum].x = m_x + m_w/2;\n    markpoints[markpointnum].y = m_y + m_h/2;\n    markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS;\n\n}\n\n//\n// Determines bounding box of all vertices,\n// sets global variables controlling zoom range.\n//\nvoid AM_findMinMaxBoundaries(void)\n{\n    int i;\n    fixed_t a;\n    fixed_t b;\n\n    min_x = min_y =  INT_MAX;\n    max_x = max_y = -INT_MAX;\n  \n    for (i=0;i<numvertexes;i++)\n    {\n\tif (vertexes[i].x < min_x)\n\t    min_x = vertexes[i].x;\n\telse if (vertexes[i].x > max_x)\n\t    max_x = vertexes[i].x;\n    \n\tif (vertexes[i].y < min_y)\n\t    min_y = vertexes[i].y;\n\telse if (vertexes[i].y > max_y)\n\t    max_y = vertexes[i].y;\n    }\n  \n    max_w = max_x - min_x;\n    max_h = max_y - min_y;\n\n    min_w = 2*PLAYERRADIUS; // const? never changed?\n    min_h = 2*PLAYERRADIUS;\n\n    a = FixedDiv(f_w<<FRACBITS, max_w);\n    b = FixedDiv(f_h<<FRACBITS, max_h);\n  \n    min_scale_mtof = a < b ? a : b;\n    max_scale_mtof = FixedDiv(f_h<<FRACBITS, 2*PLAYERRADIUS);\n\n}\n\n\n//\n//\n//\nvoid AM_changeWindowLoc(void)\n{\n    if (m_paninc.x || m_paninc.y)\n    {\n\tfollowplayer = 0;\n\tf_oldloc.x = INT_MAX;\n    }\n\n    m_x += m_paninc.x;\n    m_y += m_paninc.y;\n\n    if (m_x + m_w/2 > max_x)\n\tm_x = max_x - m_w/2;\n    else if (m_x + m_w/2 < min_x)\n\tm_x = min_x - m_w/2;\n  \n    if (m_y + m_h/2 > max_y)\n\tm_y = max_y - m_h/2;\n    else if (m_y + m_h/2 < min_y)\n\tm_y = min_y - m_h/2;\n\n    m_x2 = m_x + m_w;\n    m_y2 = m_y + m_h;\n}\n\n\n//\n//\n//\nvoid AM_initVariables(void)\n{\n    int pnum;\n    static event_t st_notify = { ev_keyup, AM_MSGENTERED, 0, 0 };\n\n    automapactive = true;\n    fb = I_VideoBuffer;\n\n    f_oldloc.x = INT_MAX;\n    amclock = 0;\n    lightlev = 0;\n\n    m_paninc.x = m_paninc.y = 0;\n    ftom_zoommul = FRACUNIT;\n    mtof_zoommul = FRACUNIT;\n\n    m_w = FTOM(f_w);\n    m_h = FTOM(f_h);\n\n    // find player to center on initially\n    if (playeringame[consoleplayer])\n    {\n        plr = &players[consoleplayer];\n    }\n    else\n    {\n        plr = &players[0];\n\n\tfor (pnum=0;pnum<MAXPLAYERS;pnum++)\n        {\n\t    if (playeringame[pnum])\n            {\n                plr = &players[pnum];\n\t\tbreak;\n            }\n        }\n    }\n\n    m_x = plr->mo->x - m_w/2;\n    m_y = plr->mo->y - m_h/2;\n    AM_changeWindowLoc();\n\n    // for saving & restoring\n    old_m_x = m_x;\n    old_m_y = m_y;\n    old_m_w = m_w;\n    old_m_h = m_h;\n\n    // inform the status bar of the change\n    ST_Responder(&st_notify);\n\n}\n\n//\n// \n//\nvoid AM_loadPics(void)\n{\n    int i;\n    char namebuf[9];\n  \n    for (i=0;i<10;i++)\n    {\n\tDEH_snprintf(namebuf, 9, \"AMMNUM%d\", i);\n\tmarknums[i] = W_CacheLumpName(namebuf, PU_STATIC);\n    }\n\n}\n\nvoid AM_unloadPics(void)\n{\n    int i;\n    char namebuf[9];\n  \n    for (i=0;i<10;i++)\n    {\n\tDEH_snprintf(namebuf, 9, \"AMMNUM%d\", i);\n\tW_ReleaseLumpName(namebuf);\n    }\n}\n\nvoid AM_clearMarks(void)\n{\n    int i;\n\n    for (i=0;i<AM_NUMMARKPOINTS;i++)\n\tmarkpoints[i].x = -1; // means empty\n    markpointnum = 0;\n}\n\n//\n// should be called at the start of every level\n// right now, i figure it out myself\n//\nvoid AM_LevelInit(void)\n{\n    leveljuststarted = 0;\n\n    f_x = f_y = 0;\n    f_w = finit_width;\n    f_h = finit_height;\n\n    AM_clearMarks();\n\n    AM_findMinMaxBoundaries();\n    scale_mtof = FixedDiv(min_scale_mtof, (int) (0.7*FRACUNIT));\n    if (scale_mtof > max_scale_mtof)\n\tscale_mtof = min_scale_mtof;\n    scale_ftom = FixedDiv(FRACUNIT, scale_mtof);\n}\n\n\n\n\n//\n//\n//\nvoid AM_Stop (void)\n{\n    static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED, 0 };\n\n    AM_unloadPics();\n    automapactive = false;\n    ST_Responder(&st_notify);\n    stopped = true;\n}\n\n//\n//\n//\nvoid AM_Start (void)\n{\n    static int lastlevel = -1, lastepisode = -1;\n\n    if (!stopped) AM_Stop();\n    stopped = false;\n    if (lastlevel != gamemap || lastepisode != gameepisode)\n    {\n\tAM_LevelInit();\n\tlastlevel = gamemap;\n\tlastepisode = gameepisode;\n    }\n    AM_initVariables();\n    AM_loadPics();\n}\n\n//\n// set the window scale to the maximum size\n//\nvoid AM_minOutWindowScale(void)\n{\n    scale_mtof = min_scale_mtof;\n    scale_ftom = FixedDiv(FRACUNIT, scale_mtof);\n    AM_activateNewScale();\n}\n\n//\n// set the window scale to the minimum size\n//\nvoid AM_maxOutWindowScale(void)\n{\n    scale_mtof = max_scale_mtof;\n    scale_ftom = FixedDiv(FRACUNIT, scale_mtof);\n    AM_activateNewScale();\n}\n\n\n//\n// Handle events (user inputs) in automap mode\n//\nboolean\nAM_Responder\n( event_t*\tev )\n{\n\n    int rc;\n    static int bigstate=0;\n    static char buffer[20];\n    int key;\n\n    rc = false;\n\n    if (!automapactive)\n    {\n\tif (ev->type == ev_keydown && ev->data1 == key_map_toggle)\n\t{\n\t    AM_Start ();\n\t    viewactive = false;\n\t    rc = true;\n\t}\n    }\n    else if (ev->type == ev_keydown)\n    {\n\trc = true;\n        key = ev->data1;\n\n        if (key == key_map_east)          // pan right\n        {\n            if (!followplayer) m_paninc.x = FTOM(F_PANINC);\n            else rc = false;\n        }\n        else if (key == key_map_west)     // pan left\n        {\n            if (!followplayer) m_paninc.x = -FTOM(F_PANINC);\n            else rc = false;\n        }\n        else if (key == key_map_north)    // pan up\n        {\n            if (!followplayer) m_paninc.y = FTOM(F_PANINC);\n            else rc = false;\n        }\n        else if (key == key_map_south)    // pan down\n        {\n            if (!followplayer) m_paninc.y = -FTOM(F_PANINC);\n            else rc = false;\n        }\n        else if (key == key_map_zoomout)  // zoom out\n        {\n            mtof_zoommul = M_ZOOMOUT;\n            ftom_zoommul = M_ZOOMIN;\n        }\n        else if (key == key_map_zoomin)   // zoom in\n        {\n            mtof_zoommul = M_ZOOMIN;\n            ftom_zoommul = M_ZOOMOUT;\n        }\n        else if (key == key_map_toggle)\n        {\n            bigstate = 0;\n            viewactive = true;\n            AM_Stop ();\n        }\n        else if (key == key_map_maxzoom)\n        {\n            bigstate = !bigstate;\n            if (bigstate)\n            {\n                AM_saveScaleAndLoc();\n                AM_minOutWindowScale();\n            }\n            else AM_restoreScaleAndLoc();\n        }\n        else if (key == key_map_follow)\n        {\n            followplayer = !followplayer;\n            f_oldloc.x = INT_MAX;\n            if (followplayer)\n                plr->message = DEH_String(AMSTR_FOLLOWON);\n            else\n                plr->message = DEH_String(AMSTR_FOLLOWOFF);\n        }\n        else if (key == key_map_grid)\n        {\n            grid = !grid;\n            if (grid)\n                plr->message = DEH_String(AMSTR_GRIDON);\n            else\n                plr->message = DEH_String(AMSTR_GRIDOFF);\n        }\n        else if (key == key_map_mark)\n        {\n            M_snprintf(buffer, sizeof(buffer), \"%s %d\",\n                       DEH_String(AMSTR_MARKEDSPOT), markpointnum);\n            plr->message = buffer;\n            AM_addMark();\n        }\n        else if (key == key_map_clearmark)\n        {\n            AM_clearMarks();\n            plr->message = DEH_String(AMSTR_MARKSCLEARED);\n        }\n        else\n        {\n            rc = false;\n        }\n\n\tif (!deathmatch && cht_CheckCheat(&cheat_amap, ev->data2))\n\t{\n\t    rc = false;\n\t    cheating = (cheating+1) % 3;\n\t}\n    }\n    else if (ev->type == ev_keyup)\n    {\n        rc = false;\n        key = ev->data1;\n\n        if (key == key_map_east)\n        {\n            if (!followplayer) m_paninc.x = 0;\n        }\n        else if (key == key_map_west)\n        {\n            if (!followplayer) m_paninc.x = 0;\n        }\n        else if (key == key_map_north)\n        {\n            if (!followplayer) m_paninc.y = 0;\n        }\n        else if (key == key_map_south)\n        {\n            if (!followplayer) m_paninc.y = 0;\n        }\n        else if (key == key_map_zoomout || key == key_map_zoomin)\n        {\n            mtof_zoommul = FRACUNIT;\n            ftom_zoommul = FRACUNIT;\n        }\n    }\n\n    return rc;\n\n}\n\n\n//\n// Zooming\n//\nvoid AM_changeWindowScale(void)\n{\n\n    // Change the scaling multipliers\n    scale_mtof = FixedMul(scale_mtof, mtof_zoommul);\n    scale_ftom = FixedDiv(FRACUNIT, scale_mtof);\n\n    if (scale_mtof < min_scale_mtof)\n\tAM_minOutWindowScale();\n    else if (scale_mtof > max_scale_mtof)\n\tAM_maxOutWindowScale();\n    else\n\tAM_activateNewScale();\n}\n\n\n//\n//\n//\nvoid AM_doFollowPlayer(void)\n{\n\n    if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y)\n    {\n\tm_x = FTOM(MTOF(plr->mo->x)) - m_w/2;\n\tm_y = FTOM(MTOF(plr->mo->y)) - m_h/2;\n\tm_x2 = m_x + m_w;\n\tm_y2 = m_y + m_h;\n\tf_oldloc.x = plr->mo->x;\n\tf_oldloc.y = plr->mo->y;\n\n\t//  m_x = FTOM(MTOF(plr->mo->x - m_w/2));\n\t//  m_y = FTOM(MTOF(plr->mo->y - m_h/2));\n\t//  m_x = plr->mo->x - m_w/2;\n\t//  m_y = plr->mo->y - m_h/2;\n\n    }\n\n}\n\n//\n//\n//\nvoid AM_updateLightLev(void)\n{\n    static int nexttic = 0;\n    //static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 };\n    static int litelevels[] = { 0, 4, 7, 10, 12, 14, 15, 15 };\n    static int litelevelscnt = 0;\n   \n    // Change light level\n    if (amclock>nexttic)\n    {\n\tlightlev = litelevels[litelevelscnt++];\n\tif (litelevelscnt == arrlen(litelevels)) litelevelscnt = 0;\n\tnexttic = amclock + 6 - (amclock % 6);\n    }\n\n}\n\n\n//\n// Updates on Game Tick\n//\nvoid AM_Ticker (void)\n{\n\n    if (!automapactive)\n\treturn;\n\n    amclock++;\n\n    if (followplayer)\n\tAM_doFollowPlayer();\n\n    // Change the zoom if necessary\n    if (ftom_zoommul != FRACUNIT)\n\tAM_changeWindowScale();\n\n    // Change x,y location\n    if (m_paninc.x || m_paninc.y)\n\tAM_changeWindowLoc();\n\n    // Update light level\n    // AM_updateLightLev();\n\n}\n\n\n//\n// Clear automap frame buffer.\n//\nvoid AM_clearFB(int color)\n{\n    memset(fb, color, f_w*f_h);\n}\n\n\n//\n// Automap clipping of lines.\n//\n// Based on Cohen-Sutherland clipping algorithm but with a slightly\n// faster reject and precalculated slopes.  If the speed is needed,\n// use a hash algorithm to handle  the common cases.\n//\nboolean\nAM_clipMline\n( mline_t*\tml,\n  fline_t*\tfl )\n{\n    enum\n    {\n\tLEFT\t=1,\n\tRIGHT\t=2,\n\tBOTTOM\t=4,\n\tTOP\t=8\n    };\n    \n    register int\toutcode1 = 0;\n    register int\toutcode2 = 0;\n    register int\toutside;\n    \n    fpoint_t\ttmp;\n    int\t\tdx;\n    int\t\tdy;\n\n    \n#define DOOUTCODE(oc, mx, my) \\\n    (oc) = 0; \\\n    if ((my) < 0) (oc) |= TOP; \\\n    else if ((my) >= f_h) (oc) |= BOTTOM; \\\n    if ((mx) < 0) (oc) |= LEFT; \\\n    else if ((mx) >= f_w) (oc) |= RIGHT;\n\n    \n    // do trivial rejects and outcodes\n    if (ml->a.y > m_y2)\n\toutcode1 = TOP;\n    else if (ml->a.y < m_y)\n\toutcode1 = BOTTOM;\n\n    if (ml->b.y > m_y2)\n\toutcode2 = TOP;\n    else if (ml->b.y < m_y)\n\toutcode2 = BOTTOM;\n    \n    if (outcode1 & outcode2)\n\treturn false; // trivially outside\n\n    if (ml->a.x < m_x)\n\toutcode1 |= LEFT;\n    else if (ml->a.x > m_x2)\n\toutcode1 |= RIGHT;\n    \n    if (ml->b.x < m_x)\n\toutcode2 |= LEFT;\n    else if (ml->b.x > m_x2)\n\toutcode2 |= RIGHT;\n    \n    if (outcode1 & outcode2)\n\treturn false; // trivially outside\n\n    // transform to frame-buffer coordinates.\n    fl->a.x = CXMTOF(ml->a.x);\n    fl->a.y = CYMTOF(ml->a.y);\n    fl->b.x = CXMTOF(ml->b.x);\n    fl->b.y = CYMTOF(ml->b.y);\n\n    DOOUTCODE(outcode1, fl->a.x, fl->a.y);\n    DOOUTCODE(outcode2, fl->b.x, fl->b.y);\n\n    if (outcode1 & outcode2)\n\treturn false;\n\n    while (outcode1 | outcode2)\n    {\n\t// may be partially inside box\n\t// find an outside point\n\tif (outcode1)\n\t    outside = outcode1;\n\telse\n\t    outside = outcode2;\n\t\n\t// clip to each side\n\tif (outside & TOP)\n\t{\n\t    dy = fl->a.y - fl->b.y;\n\t    dx = fl->b.x - fl->a.x;\n\t    tmp.x = fl->a.x + (dx*(fl->a.y))/dy;\n\t    tmp.y = 0;\n\t}\n\telse if (outside & BOTTOM)\n\t{\n\t    dy = fl->a.y - fl->b.y;\n\t    dx = fl->b.x - fl->a.x;\n\t    tmp.x = fl->a.x + (dx*(fl->a.y-f_h))/dy;\n\t    tmp.y = f_h-1;\n\t}\n\telse if (outside & RIGHT)\n\t{\n\t    dy = fl->b.y - fl->a.y;\n\t    dx = fl->b.x - fl->a.x;\n\t    tmp.y = fl->a.y + (dy*(f_w-1 - fl->a.x))/dx;\n\t    tmp.x = f_w-1;\n\t}\n\telse if (outside & LEFT)\n\t{\n\t    dy = fl->b.y - fl->a.y;\n\t    dx = fl->b.x - fl->a.x;\n\t    tmp.y = fl->a.y + (dy*(-fl->a.x))/dx;\n\t    tmp.x = 0;\n\t}\n        else\n        {\n            tmp.x = 0;\n            tmp.y = 0;\n        }\n\n\tif (outside == outcode1)\n\t{\n\t    fl->a = tmp;\n\t    DOOUTCODE(outcode1, fl->a.x, fl->a.y);\n\t}\n\telse\n\t{\n\t    fl->b = tmp;\n\t    DOOUTCODE(outcode2, fl->b.x, fl->b.y);\n\t}\n\t\n\tif (outcode1 & outcode2)\n\t    return false; // trivially outside\n    }\n\n    return true;\n}\n#undef DOOUTCODE\n\n\n//\n// Classic Bresenham w/ whatever optimizations needed for speed\n//\nvoid\nAM_drawFline\n( fline_t*\tfl,\n  int\t\tcolor )\n{\n    register int x;\n    register int y;\n    register int dx;\n    register int dy;\n    register int sx;\n    register int sy;\n    register int ax;\n    register int ay;\n    register int d;\n    \n    static int fuck = 0;\n\n    // For debugging only\n    if (      fl->a.x < 0 || fl->a.x >= f_w\n\t   || fl->a.y < 0 || fl->a.y >= f_h\n\t   || fl->b.x < 0 || fl->b.x >= f_w\n\t   || fl->b.y < 0 || fl->b.y >= f_h)\n    {\n        DEH_fprintf(stderr, \"fuck %d \\r\", fuck++);\n\treturn;\n    }\n\n#define PUTDOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc)\n\n    dx = fl->b.x - fl->a.x;\n    ax = 2 * (dx<0 ? -dx : dx);\n    sx = dx<0 ? -1 : 1;\n\n    dy = fl->b.y - fl->a.y;\n    ay = 2 * (dy<0 ? -dy : dy);\n    sy = dy<0 ? -1 : 1;\n\n    x = fl->a.x;\n    y = fl->a.y;\n\n    if (ax > ay)\n    {\n\td = ay - ax/2;\n\twhile (1)\n\t{\n\t    PUTDOT(x,y,color);\n\t    if (x == fl->b.x) return;\n\t    if (d>=0)\n\t    {\n\t\ty += sy;\n\t\td -= ax;\n\t    }\n\t    x += sx;\n\t    d += ay;\n\t}\n    }\n    else\n    {\n\td = ax - ay/2;\n\twhile (1)\n\t{\n\t    PUTDOT(x, y, color);\n\t    if (y == fl->b.y) return;\n\t    if (d >= 0)\n\t    {\n\t\tx += sx;\n\t\td -= ay;\n\t    }\n\t    y += sy;\n\t    d += ax;\n\t}\n    }\n}\n\n\n//\n// Clip lines, draw visible part sof lines.\n//\nvoid\nAM_drawMline\n( mline_t*\tml,\n  int\t\tcolor )\n{\n    static fline_t fl;\n\n    if (AM_clipMline(ml, &fl))\n\tAM_drawFline(&fl, color); // draws it on frame buffer using fb coords\n}\n\n\n\n//\n// Draws flat (floor/ceiling tile) aligned grid lines.\n//\nvoid AM_drawGrid(int color)\n{\n    fixed_t x, y;\n    fixed_t start, end;\n    mline_t ml;\n\n    // Figure out start of vertical gridlines\n    start = m_x;\n    if ((start-bmaporgx)%(MAPBLOCKUNITS<<FRACBITS))\n\tstart += (MAPBLOCKUNITS<<FRACBITS)\n\t    - ((start-bmaporgx)%(MAPBLOCKUNITS<<FRACBITS));\n    end = m_x + m_w;\n\n    // draw vertical gridlines\n    ml.a.y = m_y;\n    ml.b.y = m_y+m_h;\n    for (x=start; x<end; x+=(MAPBLOCKUNITS<<FRACBITS))\n    {\n\tml.a.x = x;\n\tml.b.x = x;\n\tAM_drawMline(&ml, color);\n    }\n\n    // Figure out start of horizontal gridlines\n    start = m_y;\n    if ((start-bmaporgy)%(MAPBLOCKUNITS<<FRACBITS))\n\tstart += (MAPBLOCKUNITS<<FRACBITS)\n\t    - ((start-bmaporgy)%(MAPBLOCKUNITS<<FRACBITS));\n    end = m_y + m_h;\n\n    // draw horizontal gridlines\n    ml.a.x = m_x;\n    ml.b.x = m_x + m_w;\n    for (y=start; y<end; y+=(MAPBLOCKUNITS<<FRACBITS))\n    {\n\tml.a.y = y;\n\tml.b.y = y;\n\tAM_drawMline(&ml, color);\n    }\n\n}\n\n//\n// Determines visible lines, draws them.\n// This is LineDef based, not LineSeg based.\n//\nvoid AM_drawWalls(void)\n{\n    int i;\n    static mline_t l;\n\n    for (i=0;i<numlines;i++)\n    {\n\tl.a.x = lines[i].v1->x;\n\tl.a.y = lines[i].v1->y;\n\tl.b.x = lines[i].v2->x;\n\tl.b.y = lines[i].v2->y;\n\tif (cheating || (lines[i].flags & ML_MAPPED))\n\t{\n\t    if ((lines[i].flags & LINE_NEVERSEE) && !cheating)\n\t\tcontinue;\n\t    if (!lines[i].backsector)\n\t    {\n\t\tAM_drawMline(&l, WALLCOLORS+lightlev);\n\t    }\n\t    else\n\t    {\n\t\tif (lines[i].special == 39)\n\t\t{ // teleporters\n\t\t    AM_drawMline(&l, WALLCOLORS+WALLRANGE/2);\n\t\t}\n\t\telse if (lines[i].flags & ML_SECRET) // secret door\n\t\t{\n\t\t    if (cheating) AM_drawMline(&l, SECRETWALLCOLORS + lightlev);\n\t\t    else AM_drawMline(&l, WALLCOLORS+lightlev);\n\t\t}\n\t\telse if (lines[i].backsector->floorheight\n\t\t\t   != lines[i].frontsector->floorheight) {\n\t\t    AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change\n\t\t}\n\t\telse if (lines[i].backsector->ceilingheight\n\t\t\t   != lines[i].frontsector->ceilingheight) {\n\t\t    AM_drawMline(&l, CDWALLCOLORS+lightlev); // ceiling level change\n\t\t}\n\t\telse if (cheating) {\n\t\t    AM_drawMline(&l, TSWALLCOLORS+lightlev);\n\t\t}\n\t    }\n\t}\n\telse if (plr->powers[pw_allmap])\n\t{\n\t    if (!(lines[i].flags & LINE_NEVERSEE)) AM_drawMline(&l, GRAYS+3);\n\t}\n    }\n}\n\n\n//\n// Rotation in 2D.\n// Used to rotate player arrow line character.\n//\nvoid\nAM_rotate\n( fixed_t*\tx,\n  fixed_t*\ty,\n  angle_t\ta )\n{\n    fixed_t tmpx;\n\n    tmpx =\n\tFixedMul(*x,finecosine[a>>ANGLETOFINESHIFT])\n\t- FixedMul(*y,finesine[a>>ANGLETOFINESHIFT]);\n    \n    *y   =\n\tFixedMul(*x,finesine[a>>ANGLETOFINESHIFT])\n\t+ FixedMul(*y,finecosine[a>>ANGLETOFINESHIFT]);\n\n    *x = tmpx;\n}\n\nvoid\nAM_drawLineCharacter\n( mline_t*\tlineguy,\n  int\t\tlineguylines,\n  fixed_t\tscale,\n  angle_t\tangle,\n  int\t\tcolor,\n  fixed_t\tx,\n  fixed_t\ty )\n{\n    int\t\ti;\n    mline_t\tl;\n\n    for (i=0;i<lineguylines;i++)\n    {\n\tl.a.x = lineguy[i].a.x;\n\tl.a.y = lineguy[i].a.y;\n\n\tif (scale)\n\t{\n\t    l.a.x = FixedMul(scale, l.a.x);\n\t    l.a.y = FixedMul(scale, l.a.y);\n\t}\n\n\tif (angle)\n\t    AM_rotate(&l.a.x, &l.a.y, angle);\n\n\tl.a.x += x;\n\tl.a.y += y;\n\n\tl.b.x = lineguy[i].b.x;\n\tl.b.y = lineguy[i].b.y;\n\n\tif (scale)\n\t{\n\t    l.b.x = FixedMul(scale, l.b.x);\n\t    l.b.y = FixedMul(scale, l.b.y);\n\t}\n\n\tif (angle)\n\t    AM_rotate(&l.b.x, &l.b.y, angle);\n\t\n\tl.b.x += x;\n\tl.b.y += y;\n\n\tAM_drawMline(&l, color);\n    }\n}\n\nvoid AM_drawPlayers(void)\n{\n    int\t\ti;\n    player_t*\tp;\n    static int \ttheir_colors[] = { GREENS, GRAYS, BROWNS, REDS };\n    int\t\ttheir_color = -1;\n    int\t\tcolor;\n\n    if (!netgame)\n    {\n\tif (cheating)\n\t    AM_drawLineCharacter\n\t\t(cheat_player_arrow, arrlen(cheat_player_arrow), 0,\n\t\t plr->mo->angle, WHITE, plr->mo->x, plr->mo->y);\n\telse\n\t    AM_drawLineCharacter\n\t\t(player_arrow, arrlen(player_arrow), 0, plr->mo->angle,\n\t\t WHITE, plr->mo->x, plr->mo->y);\n\treturn;\n    }\n\n    for (i=0;i<MAXPLAYERS;i++)\n    {\n\ttheir_color++;\n\tp = &players[i];\n\n\tif ( (deathmatch && !singledemo) && p != plr)\n\t    continue;\n\n\tif (!playeringame[i])\n\t    continue;\n\n\tif (p->powers[pw_invisibility])\n\t    color = 246; // *close* to black\n\telse\n\t    color = their_colors[their_color];\n\t\n\tAM_drawLineCharacter\n\t    (player_arrow, arrlen(player_arrow), 0, p->mo->angle,\n\t     color, p->mo->x, p->mo->y);\n    }\n\n}\n\nvoid\nAM_drawThings\n( int\tcolors,\n  int \tcolorrange)\n{\n    int\t\ti;\n    mobj_t*\tt;\n\n    for (i=0;i<numsectors;i++)\n    {\n\tt = sectors[i].thinglist;\n\twhile (t)\n\t{\n\t    AM_drawLineCharacter\n\t\t(thintriangle_guy, arrlen(thintriangle_guy),\n\t\t 16<<FRACBITS, t->angle, colors+lightlev, t->x, t->y);\n\t    t = t->snext;\n\t}\n    }\n}\n\nvoid AM_drawMarks(void)\n{\n    int i, fx, fy, w, h;\n\n    for (i=0;i<AM_NUMMARKPOINTS;i++)\n    {\n\tif (markpoints[i].x != -1)\n\t{\n\t    //      w = SHORT(marknums[i]->width);\n\t    //      h = SHORT(marknums[i]->height);\n\t    w = 5; // because something's wrong with the wad, i guess\n\t    h = 6; // because something's wrong with the wad, i guess\n\t    fx = CXMTOF(markpoints[i].x);\n\t    fy = CYMTOF(markpoints[i].y);\n\t    if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h)\n\t\tV_DrawPatch(fx, fy, marknums[i]);\n\t}\n    }\n\n}\n\nvoid AM_drawCrosshair(int color)\n{\n    fb[(f_w*(f_h+1))/2] = color; // single point for now\n\n}\n\nvoid AM_Drawer (void)\n{\n    if (!automapactive) return;\n\n    AM_clearFB(BACKGROUND);\n    if (grid)\n\tAM_drawGrid(GRIDCOLORS);\n    AM_drawWalls();\n    AM_drawPlayers();\n    if (cheating==2)\n\tAM_drawThings(THINGCOLORS, THINGRANGE);\n    AM_drawCrosshair(XHAIRCOLORS);\n\n    AM_drawMarks();\n\n    V_MarkRect(f_x, f_y, f_w, f_h);\n\n}\n"
  },
  {
    "path": "fbdoom/am_map.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//  AutoMap module.\n//\n\n#ifndef __AMMAP_H__\n#define __AMMAP_H__\n\n#include \"d_event.h\"\n#include \"m_cheat.h\"\n\n// Used by ST StatusBar stuff.\n#define AM_MSGHEADER (('a'<<24)+('m'<<16))\n#define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8))\n#define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8))\n\n\n// Called by main loop.\nboolean AM_Responder (event_t* ev);\n\n// Called by main loop.\nvoid AM_Ticker (void);\n\n// Called by main loop,\n// called instead of view drawer if automap active.\nvoid AM_Drawer (void);\n\n// Called to force the automap to quit\n// if the level is completed while it is up.\nvoid AM_Stop (void);\n\n\nextern cheatseq_t cheat_amap;\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/config.h",
    "content": "/* config.hin.  Generated from configure.ac by autoheader.  */\n\n/* Define to 1 if you have the <dev/isa/spkrio.h> header file. */\n#undef HAVE_DEV_ISA_SPKRIO_H\n\n/* Define to 1 if you have the <dev/speaker/speaker.h> header file. */\n#undef HAVE_DEV_SPEAKER_SPEAKER_H\n\n/* Define to 1 if you have the <inttypes.h> header file. */\n#define HAVE_INTTYPES_H 1\n\n/* Define to 1 if you have the `ioperm' function. */\n#undef HAVE_IOPERM\n\n/* Define to 1 if you have the `amd64' library (-lamd64). */\n#undef HAVE_LIBAMD64\n\n/* Define to 1 if you have the `i386' library (-li386). */\n#undef HAVE_LIBI386\n\n/* Define to 1 if you have the `m' library (-lm). */\n#undef HAVE_LIBM\n\n/* Define to 1 if you have the `png' library (-lpng). */\n#undef HAVE_LIBPNG\n\n/* Define to 1 if you have the `samplerate' library (-lsamplerate). */\n#undef HAVE_LIBSAMPLERATE\n\n/* Define to 1 if you have the `z' library (-lz). */\n#undef HAVE_LIBZ\n\n/* Define to 1 if you have the <linux/kd.h> header file. */\n#undef HAVE_LINUX_KD_H\n\n/* Define to 1 if you have the <memory.h> header file. */\n#undef HAVE_MEMORY_H\n\n/* Define to 1 if you have the `mmap' function. */\n#undef HAVE_MMAP\n\n/* Define to 1 if you have the `sched_setaffinity' function. */\n#undef HAVE_SCHED_SETAFFINITY\n\n/* Define to 1 if you have the <stdint.h> header file. */\n#define HAVE_STDINT_H 1\n\n/* Define to 1 if you have the <stdlib.h> header file. */\n#define HAVE_STDLIB_H 1\n\n/* Define to 1 if you have the <strings.h> header file. */\n#define HAVE_STRINGS_H 1\n\n/* Define to 1 if you have the <string.h> header file. */\n#define HAVE_STRING_H 1\n\n/* Define to 1 if you have the <sys/stat.h> header file. */\n#undef HAVE_SYS_STAT_H\n\n/* Define to 1 if you have the <sys/types.h> header file. */\n#define HAVE_SYS_TYPES_H 1\n\n/* Define to 1 if you have the <unistd.h> header file. */\n#undef HAVE_UNISTD_H\n\n/* Name of package */\n#define PACKAGE \"Doom\"\n\n/* Define to the address where bug reports for this package should be sent. */\n#undef PACKAGE_BUGREPORT\n\n/* Define to the full name of this package. */\n#define PACKAGE_NAME \"FDoom\"\n\n/* Define to the full name and version of this package. */\n#define PACKAGE_STRING \"FDoom 0.1\"\n\n/* Define to the one symbol short name of this package. */\n#define PACKAGE_TARNAME \"fdoom.tar\"\n\n/* Define to the home page for this package. */\n#define PACKAGE_URL \"\"\n\n/* Define to the version of this package. */\n#define PACKAGE_VERSION 0.1\n\n/* Change this when you create your awesome forked version */\n#define PROGRAM_PREFIX \"fdoom\"\n\n/* Define to 1 if you have the ANSI C header files. */\n#define STDC_HEADERS 1\n\n/* Version number of package */\n#define VERSION 0.1\n\n/* Define to 1 if you want to compile the unmodified code */\n#undef ORIGCODE\n\n/* Define to the directory where all game files are located */\n#define FILES_DIR \"/mnt\"\n"
  },
  {
    "path": "fbdoom/d_englsh.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tPrinted strings for translation.\n//\tEnglish language support (default).\n//\n\n#ifndef __D_ENGLSH__\n#define __D_ENGLSH__\n\n//\n//\tPrinted strings for translation\n//\n\n//\n// D_Main.C\n//\n#define D_DEVSTR\t\"Development mode ON.\\n\"\n#define D_CDROM\t\"CD-ROM Version: default.cfg from c:\\\\doomdata\\n\"\n\n//\n//\tM_Menu.C\n//\n#define PRESSKEY \t\"press a key.\"\n#define PRESSYN \t\"press y or n.\"\n#define QUITMSG\t\"are you sure you want to\\nquit this great game?\"\n#define LOADNET \t\"you can't do load while in a net game!\\n\\n\"PRESSKEY\n#define QLOADNET\t\"you can't quickload during a netgame!\\n\\n\"PRESSKEY\n#define QSAVESPOT\t\"you haven't picked a quicksave slot yet!\\n\\n\"PRESSKEY\n#define SAVEDEAD \t\"you can't save if you aren't playing!\\n\\n\"PRESSKEY\n#define QSPROMPT \t\"quicksave over your game named\\n\\n'%s'?\\n\\n\"PRESSYN\n#define QLPROMPT\t\"do you want to quickload the game named\\n\\n'%s'?\\n\\n\"PRESSYN\n\n#define NEWGAME\t\\\n\"you can't start a new game\\n\"\\\n\"while in a network game.\\n\\n\"PRESSKEY\n\n#define NIGHTMARE\t\\\n\"are you sure? this skill level\\n\"\\\n\"isn't even remotely fair.\\n\\n\"PRESSYN\n\n#define SWSTRING\t\\\n\"this is the shareware version of doom.\\n\\n\"\\\n\"you need to order the entire trilogy.\\n\\n\"PRESSKEY\n\n#define MSGOFF\t\"Messages OFF\"\n#define MSGON\t\t\"Messages ON\"\n#define NETEND\t\"you can't end a netgame!\\n\\n\"PRESSKEY\n#define ENDGAME\t\"are you sure you want to end the game?\\n\\n\"PRESSYN\n\n#define DOSY\t\t\"(press y to quit to dos.)\"\n\n#define DETAILHI\t\"High detail\"\n#define DETAILLO\t\"Low detail\"\n#define GAMMALVL0\t\"Gamma correction OFF\"\n#define GAMMALVL1\t\"Gamma correction level 1\"\n#define GAMMALVL2\t\"Gamma correction level 2\"\n#define GAMMALVL3\t\"Gamma correction level 3\"\n#define GAMMALVL4\t\"Gamma correction level 4\"\n#define EMPTYSTRING\t\"empty slot\"\n\n//\n//\tP_inter.C\n//\n#define GOTARMOR\t\"Picked up the armor.\"\n#define GOTMEGA\t\"Picked up the MegaArmor!\"\n#define GOTHTHBONUS\t\"Picked up a health bonus.\"\n#define GOTARMBONUS\t\"Picked up an armor bonus.\"\n#define GOTSTIM\t\"Picked up a stimpack.\"\n#define GOTMEDINEED\t\"Picked up a medikit that you REALLY need!\"\n#define GOTMEDIKIT\t\"Picked up a medikit.\"\n#define GOTSUPER\t\"Supercharge!\"\n\n#define GOTBLUECARD\t\"Picked up a blue keycard.\"\n#define GOTYELWCARD\t\"Picked up a yellow keycard.\"\n#define GOTREDCARD\t\"Picked up a red keycard.\"\n#define GOTBLUESKUL\t\"Picked up a blue skull key.\"\n#define GOTYELWSKUL\t\"Picked up a yellow skull key.\"\n#define GOTREDSKULL\t\"Picked up a red skull key.\"\n\n#define GOTINVUL\t\"Invulnerability!\"\n#define GOTBERSERK\t\"Berserk!\"\n#define GOTINVIS\t\"Partial Invisibility\"\n#define GOTSUIT\t\"Radiation Shielding Suit\"\n#define GOTMAP\t\"Computer Area Map\"\n#define GOTVISOR\t\"Light Amplification Visor\"\n#define GOTMSPHERE\t\"MegaSphere!\"\n\n#define GOTCLIP\t\"Picked up a clip.\"\n#define GOTCLIPBOX\t\"Picked up a box of bullets.\"\n#define GOTROCKET\t\"Picked up a rocket.\"\n#define GOTROCKBOX\t\"Picked up a box of rockets.\"\n#define GOTCELL\t\"Picked up an energy cell.\"\n#define GOTCELLBOX\t\"Picked up an energy cell pack.\"\n#define GOTSHELLS\t\"Picked up 4 shotgun shells.\"\n#define GOTSHELLBOX\t\"Picked up a box of shotgun shells.\"\n#define GOTBACKPACK\t\"Picked up a backpack full of ammo!\"\n\n#define GOTBFG9000\t\"You got the BFG9000!  Oh, yes.\"\n#define GOTCHAINGUN\t\"You got the chaingun!\"\n#define GOTCHAINSAW\t\"A chainsaw!  Find some meat!\"\n#define GOTLAUNCHER\t\"You got the rocket launcher!\"\n#define GOTPLASMA\t\"You got the plasma gun!\"\n#define GOTSHOTGUN\t\"You got the shotgun!\"\n#define GOTSHOTGUN2\t\"You got the super shotgun!\"\n\n//\n// P_Doors.C\n//\n#define PD_BLUEO\t\"You need a blue key to activate this object\"\n#define PD_REDO\t\"You need a red key to activate this object\"\n#define PD_YELLOWO\t\"You need a yellow key to activate this object\"\n#define PD_BLUEK\t\"You need a blue key to open this door\"\n#define PD_REDK\t\"You need a red key to open this door\"\n#define PD_YELLOWK\t\"You need a yellow key to open this door\"\n\n//\n//\tG_game.C\n//\n#define GGSAVED\t\"game saved.\"\n\n//\n//\tHU_stuff.C\n//\n#define HUSTR_MSGU\t\"[Message unsent]\"\n\n#define HUSTR_E1M1\t\"E1M1: Hangar\"\n#define HUSTR_E1M2\t\"E1M2: Nuclear Plant\"\n#define HUSTR_E1M3\t\"E1M3: Toxin Refinery\"\n#define HUSTR_E1M4\t\"E1M4: Command Control\"\n#define HUSTR_E1M5\t\"E1M5: Phobos Lab\"\n#define HUSTR_E1M6\t\"E1M6: Central Processing\"\n#define HUSTR_E1M7\t\"E1M7: Computer Station\"\n#define HUSTR_E1M8\t\"E1M8: Phobos Anomaly\"\n#define HUSTR_E1M9\t\"E1M9: Military Base\"\n\n#define HUSTR_E2M1\t\"E2M1: Deimos Anomaly\"\n#define HUSTR_E2M2\t\"E2M2: Containment Area\"\n#define HUSTR_E2M3\t\"E2M3: Refinery\"\n#define HUSTR_E2M4\t\"E2M4: Deimos Lab\"\n#define HUSTR_E2M5\t\"E2M5: Command Center\"\n#define HUSTR_E2M6\t\"E2M6: Halls of the Damned\"\n#define HUSTR_E2M7\t\"E2M7: Spawning Vats\"\n#define HUSTR_E2M8\t\"E2M8: Tower of Babel\"\n#define HUSTR_E2M9\t\"E2M9: Fortress of Mystery\"\n\n#define HUSTR_E3M1\t\"E3M1: Hell Keep\"\n#define HUSTR_E3M2\t\"E3M2: Slough of Despair\"\n#define HUSTR_E3M3\t\"E3M3: Pandemonium\"\n#define HUSTR_E3M4\t\"E3M4: House of Pain\"\n#define HUSTR_E3M5\t\"E3M5: Unholy Cathedral\"\n#define HUSTR_E3M6\t\"E3M6: Mt. Erebus\"\n#define HUSTR_E3M7\t\"E3M7: Limbo\"\n#define HUSTR_E3M8\t\"E3M8: Dis\"\n#define HUSTR_E3M9\t\"E3M9: Warrens\"\n\n#define HUSTR_E4M1\t\"E4M1: Hell Beneath\"\n#define HUSTR_E4M2\t\"E4M2: Perfect Hatred\"\n#define HUSTR_E4M3\t\"E4M3: Sever The Wicked\"\n#define HUSTR_E4M4\t\"E4M4: Unruly Evil\"\n#define HUSTR_E4M5\t\"E4M5: They Will Repent\"\n#define HUSTR_E4M6\t\"E4M6: Against Thee Wickedly\"\n#define HUSTR_E4M7\t\"E4M7: And Hell Followed\"\n#define HUSTR_E4M8\t\"E4M8: Unto The Cruel\"\n#define HUSTR_E4M9\t\"E4M9: Fear\"\n\n#define HUSTR_1\t\"level 1: entryway\"\n#define HUSTR_2\t\"level 2: underhalls\"\n#define HUSTR_3\t\"level 3: the gantlet\"\n#define HUSTR_4\t\"level 4: the focus\"\n#define HUSTR_5\t\"level 5: the waste tunnels\"\n#define HUSTR_6\t\"level 6: the crusher\"\n#define HUSTR_7\t\"level 7: dead simple\"\n#define HUSTR_8\t\"level 8: tricks and traps\"\n#define HUSTR_9\t\"level 9: the pit\"\n#define HUSTR_10\t\"level 10: refueling base\"\n#define HUSTR_11\t\"level 11: 'o' of destruction!\"\n\n#define HUSTR_12\t\"level 12: the factory\"\n#define HUSTR_13\t\"level 13: downtown\"\n#define HUSTR_14\t\"level 14: the inmost dens\"\n#define HUSTR_15\t\"level 15: industrial zone\"\n#define HUSTR_16\t\"level 16: suburbs\"\n#define HUSTR_17\t\"level 17: tenements\"\n#define HUSTR_18\t\"level 18: the courtyard\"\n#define HUSTR_19\t\"level 19: the citadel\"\n#define HUSTR_20\t\"level 20: gotcha!\"\n\n#define HUSTR_21\t\"level 21: nirvana\"\n#define HUSTR_22\t\"level 22: the catacombs\"\n#define HUSTR_23\t\"level 23: barrels o' fun\"\n#define HUSTR_24\t\"level 24: the chasm\"\n#define HUSTR_25\t\"level 25: bloodfalls\"\n#define HUSTR_26\t\"level 26: the abandoned mines\"\n#define HUSTR_27\t\"level 27: monster condo\"\n#define HUSTR_28\t\"level 28: the spirit world\"\n#define HUSTR_29\t\"level 29: the living end\"\n#define HUSTR_30\t\"level 30: icon of sin\"\n\n#define HUSTR_31\t\"level 31: wolfenstein\"\n#define HUSTR_32\t\"level 32: grosse\"\n\n#define PHUSTR_1\t\"level 1: congo\"\n#define PHUSTR_2\t\"level 2: well of souls\"\n#define PHUSTR_3\t\"level 3: aztec\"\n#define PHUSTR_4\t\"level 4: caged\"\n#define PHUSTR_5\t\"level 5: ghost town\"\n#define PHUSTR_6\t\"level 6: baron's lair\"\n#define PHUSTR_7\t\"level 7: caughtyard\"\n#define PHUSTR_8\t\"level 8: realm\"\n#define PHUSTR_9\t\"level 9: abattoire\"\n#define PHUSTR_10\t\"level 10: onslaught\"\n#define PHUSTR_11\t\"level 11: hunted\"\n\n#define PHUSTR_12\t\"level 12: speed\"\n#define PHUSTR_13\t\"level 13: the crypt\"\n#define PHUSTR_14\t\"level 14: genesis\"\n#define PHUSTR_15\t\"level 15: the twilight\"\n#define PHUSTR_16\t\"level 16: the omen\"\n#define PHUSTR_17\t\"level 17: compound\"\n#define PHUSTR_18\t\"level 18: neurosphere\"\n#define PHUSTR_19\t\"level 19: nme\"\n#define PHUSTR_20\t\"level 20: the death domain\"\n\n#define PHUSTR_21\t\"level 21: slayer\"\n#define PHUSTR_22\t\"level 22: impossible mission\"\n#define PHUSTR_23\t\"level 23: tombstone\"\n#define PHUSTR_24\t\"level 24: the final frontier\"\n#define PHUSTR_25\t\"level 25: the temple of darkness\"\n#define PHUSTR_26\t\"level 26: bunker\"\n#define PHUSTR_27\t\"level 27: anti-christ\"\n#define PHUSTR_28\t\"level 28: the sewers\"\n#define PHUSTR_29\t\"level 29: odyssey of noises\"\n#define PHUSTR_30\t\"level 30: the gateway of hell\"\n\n#define PHUSTR_31\t\"level 31: cyberden\"\n#define PHUSTR_32\t\"level 32: go 2 it\"\n\n#define THUSTR_1\t\"level 1: system control\"\n#define THUSTR_2\t\"level 2: human bbq\"\n#define THUSTR_3\t\"level 3: power control\"\n#define THUSTR_4\t\"level 4: wormhole\"\n#define THUSTR_5\t\"level 5: hanger\"\n#define THUSTR_6\t\"level 6: open season\"\n#define THUSTR_7\t\"level 7: prison\"\n#define THUSTR_8\t\"level 8: metal\"\n#define THUSTR_9\t\"level 9: stronghold\"\n#define THUSTR_10\t\"level 10: redemption\"\n#define THUSTR_11\t\"level 11: storage facility\"\n\n#define THUSTR_12\t\"level 12: crater\"\n#define THUSTR_13\t\"level 13: nukage processing\"\n#define THUSTR_14\t\"level 14: steel works\"\n#define THUSTR_15\t\"level 15: dead zone\"\n#define THUSTR_16\t\"level 16: deepest reaches\"\n#define THUSTR_17\t\"level 17: processing area\"\n#define THUSTR_18\t\"level 18: mill\"\n#define THUSTR_19\t\"level 19: shipping/respawning\"\n#define THUSTR_20\t\"level 20: central processing\"\n\n#define THUSTR_21\t\"level 21: administration center\"\n#define THUSTR_22\t\"level 22: habitat\"\n#define THUSTR_23\t\"level 23: lunar mining project\"\n#define THUSTR_24\t\"level 24: quarry\"\n#define THUSTR_25\t\"level 25: baron's den\"\n#define THUSTR_26\t\"level 26: ballistyx\"\n#define THUSTR_27\t\"level 27: mount pain\"\n#define THUSTR_28\t\"level 28: heck\"\n#define THUSTR_29\t\"level 29: river styx\"\n#define THUSTR_30\t\"level 30: last call\"\n\n#define THUSTR_31\t\"level 31: pharaoh\"\n#define THUSTR_32\t\"level 32: caribbean\"\n\n#define HUSTR_CHATMACRO1\t\"I'm ready to kick butt!\"\n#define HUSTR_CHATMACRO2\t\"I'm OK.\"\n#define HUSTR_CHATMACRO3\t\"I'm not looking too good!\"\n#define HUSTR_CHATMACRO4\t\"Help!\"\n#define HUSTR_CHATMACRO5\t\"You suck!\"\n#define HUSTR_CHATMACRO6\t\"Next time, scumbag...\"\n#define HUSTR_CHATMACRO7\t\"Come here!\"\n#define HUSTR_CHATMACRO8\t\"I'll take care of it.\"\n#define HUSTR_CHATMACRO9\t\"Yes\"\n#define HUSTR_CHATMACRO0\t\"No\"\n\n#define HUSTR_TALKTOSELF1\t\"You mumble to yourself\"\n#define HUSTR_TALKTOSELF2\t\"Who's there?\"\n#define HUSTR_TALKTOSELF3\t\"You scare yourself\"\n#define HUSTR_TALKTOSELF4\t\"You start to rave\"\n#define HUSTR_TALKTOSELF5\t\"You've lost it...\"\n\n#define HUSTR_MESSAGESENT\t\"[Message Sent]\"\n\n// The following should NOT be changed unless it seems\n// just AWFULLY necessary\n\n#define HUSTR_PLRGREEN\t\"Green: \"\n#define HUSTR_PLRINDIGO\t\"Indigo: \"\n#define HUSTR_PLRBROWN\t\"Brown: \"\n#define HUSTR_PLRRED\t\t\"Red: \"\n\n#define HUSTR_KEYGREEN\t'g'\n#define HUSTR_KEYINDIGO\t'i'\n#define HUSTR_KEYBROWN\t'b'\n#define HUSTR_KEYRED\t'r'\n\n//\n//\tAM_map.C\n//\n\n#define AMSTR_FOLLOWON\t\"Follow Mode ON\"\n#define AMSTR_FOLLOWOFF\t\"Follow Mode OFF\"\n\n#define AMSTR_GRIDON\t\"Grid ON\"\n#define AMSTR_GRIDOFF\t\"Grid OFF\"\n\n#define AMSTR_MARKEDSPOT\t\"Marked Spot\"\n#define AMSTR_MARKSCLEARED\t\"All Marks Cleared\"\n\n//\n//\tST_stuff.C\n//\n\n#define STSTR_MUS\t\t\"Music Change\"\n#define STSTR_NOMUS\t\t\"IMPOSSIBLE SELECTION\"\n#define STSTR_DQDON\t\t\"Degreelessness Mode On\"\n#define STSTR_DQDOFF\t\"Degreelessness Mode Off\"\n\n#define STSTR_KFAADDED\t\"Very Happy Ammo Added\"\n#define STSTR_FAADDED\t\"Ammo (no keys) Added\"\n\n#define STSTR_NCON\t\t\"No Clipping Mode ON\"\n#define STSTR_NCOFF\t\t\"No Clipping Mode OFF\"\n\n#define STSTR_BEHOLD\t\"inVuln, Str, Inviso, Rad, Allmap, or Lite-amp\"\n#define STSTR_BEHOLDX\t\"Power-up Toggled\"\n\n#define STSTR_CHOPPERS\t\"... doesn't suck - GM\"\n#define STSTR_CLEV\t\t\"Changing Level...\"\n\n//\n//\tF_Finale.C\n//\n#define E1TEXT \\\n\"Once you beat the big badasses and\\n\"\\\n\"clean out the moon base you're supposed\\n\"\\\n\"to win, aren't you? Aren't you? Where's\\n\"\\\n\"your fat reward and ticket home? What\\n\"\\\n\"the hell is this? It's not supposed to\\n\"\\\n\"end this way!\\n\"\\\n\"\\n\" \\\n\"It stinks like rotten meat, but looks\\n\"\\\n\"like the lost Deimos base.  Looks like\\n\"\\\n\"you're stuck on The Shores of Hell.\\n\"\\\n\"The only way out is through.\\n\"\\\n\"\\n\"\\\n\"To continue the DOOM experience, play\\n\"\\\n\"The Shores of Hell and its amazing\\n\"\\\n\"sequel, Inferno!\\n\"\n\n\n#define E2TEXT \\\n\"You've done it! The hideous cyber-\\n\"\\\n\"demon lord that ruled the lost Deimos\\n\"\\\n\"moon base has been slain and you\\n\"\\\n\"are triumphant! But ... where are\\n\"\\\n\"you? You clamber to the edge of the\\n\"\\\n\"moon and look down to see the awful\\n\"\\\n\"truth.\\n\" \\\n\"\\n\"\\\n\"Deimos floats above Hell itself!\\n\"\\\n\"You've never heard of anyone escaping\\n\"\\\n\"from Hell, but you'll make the bastards\\n\"\\\n\"sorry they ever heard of you! Quickly,\\n\"\\\n\"you rappel down to  the surface of\\n\"\\\n\"Hell.\\n\"\\\n\"\\n\" \\\n\"Now, it's on to the final chapter of\\n\"\\\n\"DOOM! -- Inferno.\"\n\n\n#define E3TEXT \\\n\"The loathsome spiderdemon that\\n\"\\\n\"masterminded the invasion of the moon\\n\"\\\n\"bases and caused so much death has had\\n\"\\\n\"its ass kicked for all time.\\n\"\\\n\"\\n\"\\\n\"A hidden doorway opens and you enter.\\n\"\\\n\"You've proven too tough for Hell to\\n\"\\\n\"contain, and now Hell at last plays\\n\"\\\n\"fair -- for you emerge from the door\\n\"\\\n\"to see the green fields of Earth!\\n\"\\\n\"Home at last.\\n\" \\\n\"\\n\"\\\n\"You wonder what's been happening on\\n\"\\\n\"Earth while you were battling evil\\n\"\\\n\"unleashed. It's good that no Hell-\\n\"\\\n\"spawn could have come through that\\n\"\\\n\"door with you ...\"\n\n\n#define E4TEXT \\\n\"the spider mastermind must have sent forth\\n\"\\\n\"its legions of hellspawn before your\\n\"\\\n\"final confrontation with that terrible\\n\"\\\n\"beast from hell.  but you stepped forward\\n\"\\\n\"and brought forth eternal damnation and\\n\"\\\n\"suffering upon the horde as a true hero\\n\"\\\n\"would in the face of something so evil.\\n\"\\\n\"\\n\"\\\n\"besides, someone was gonna pay for what\\n\"\\\n\"happened to daisy, your pet rabbit.\\n\"\\\n\"\\n\"\\\n\"but now, you see spread before you more\\n\"\\\n\"potential pain and gibbitude as a nation\\n\"\\\n\"of demons run amok among our cities.\\n\"\\\n\"\\n\"\\\n\"next stop, hell on earth!\"\n\n\n// after level 6, put this:\n\n#define C1TEXT \\\n\"YOU HAVE ENTERED DEEPLY INTO THE INFESTED\\n\" \\\n\"STARPORT. BUT SOMETHING IS WRONG. THE\\n\" \\\n\"MONSTERS HAVE BROUGHT THEIR OWN REALITY\\n\" \\\n\"WITH THEM, AND THE STARPORT'S TECHNOLOGY\\n\" \\\n\"IS BEING SUBVERTED BY THEIR PRESENCE.\\n\" \\\n\"\\n\"\\\n\"AHEAD, YOU SEE AN OUTPOST OF HELL, A\\n\" \\\n\"FORTIFIED ZONE. IF YOU CAN GET PAST IT,\\n\" \\\n\"YOU CAN PENETRATE INTO THE HAUNTED HEART\\n\" \\\n\"OF THE STARBASE AND FIND THE CONTROLLING\\n\" \\\n\"SWITCH WHICH HOLDS EARTH'S POPULATION\\n\" \\\n\"HOSTAGE.\"\n\n// After level 11, put this:\n\n#define C2TEXT \\\n\"YOU HAVE WON! YOUR VICTORY HAS ENABLED\\n\" \\\n\"HUMANKIND TO EVACUATE EARTH AND ESCAPE\\n\"\\\n\"THE NIGHTMARE.  NOW YOU ARE THE ONLY\\n\"\\\n\"HUMAN LEFT ON THE FACE OF THE PLANET.\\n\"\\\n\"CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\\n\"\\\n\"AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\\n\"\\\n\"YOU SIT BACK AND WAIT FOR DEATH, CONTENT\\n\"\\\n\"THAT YOU HAVE SAVED YOUR SPECIES.\\n\"\\\n\"\\n\"\\\n\"BUT THEN, EARTH CONTROL BEAMS DOWN A\\n\"\\\n\"MESSAGE FROM SPACE: \\\"SENSORS HAVE LOCATED\\n\"\\\n\"THE SOURCE OF THE ALIEN INVASION. IF YOU\\n\"\\\n\"GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\\n\"\\\n\"ENTRY.  THE ALIEN BASE IS IN THE HEART OF\\n\"\\\n\"YOUR OWN HOME CITY, NOT FAR FROM THE\\n\"\\\n\"STARPORT.\\\" SLOWLY AND PAINFULLY YOU GET\\n\"\\\n\"UP AND RETURN TO THE FRAY.\"\n\n\n// After level 20, put this:\n\n#define C3TEXT \\\n\"YOU ARE AT THE CORRUPT HEART OF THE CITY,\\n\"\\\n\"SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\\n\"\\\n\"YOU SEE NO WAY TO DESTROY THE CREATURES'\\n\"\\\n\"ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\\n\"\\\n\"TEETH AND PLUNGE THROUGH IT.\\n\"\\\n\"\\n\"\\\n\"THERE MUST BE A WAY TO CLOSE IT ON THE\\n\"\\\n\"OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\\n\"\\\n\"GOT TO GO THROUGH HELL TO GET TO IT?\"\n\n\n// After level 29, put this:\n\n#define C4TEXT \\\n\"THE HORRENDOUS VISAGE OF THE BIGGEST\\n\"\\\n\"DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\\n\"\\\n\"YOU, AFTER YOU PUMP YOUR ROCKETS INTO\\n\"\\\n\"HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\\n\"\\\n\"UP AND DIES, ITS THRASHING LIMBS\\n\"\\\n\"DEVASTATING UNTOLD MILES OF HELL'S\\n\"\\\n\"SURFACE.\\n\"\\\n\"\\n\"\\\n\"YOU'VE DONE IT. THE INVASION IS OVER.\\n\"\\\n\"EARTH IS SAVED. HELL IS A WRECK. YOU\\n\"\\\n\"WONDER WHERE BAD FOLKS WILL GO WHEN THEY\\n\"\\\n\"DIE, NOW. WIPING THE SWEAT FROM YOUR\\n\"\\\n\"FOREHEAD YOU BEGIN THE LONG TREK BACK\\n\"\\\n\"HOME. REBUILDING EARTH OUGHT TO BE A\\n\"\\\n\"LOT MORE FUN THAN RUINING IT WAS.\\n\"\n\n\n\n// Before level 31, put this:\n\n#define C5TEXT \\\n\"CONGRATULATIONS, YOU'VE FOUND THE SECRET\\n\"\\\n\"LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\\n\"\\\n\"HUMANS, RATHER THAN DEMONS. YOU WONDER\\n\"\\\n\"WHO THE INMATES OF THIS CORNER OF HELL\\n\"\\\n\"WILL BE.\"\n\n\n// Before level 32, put this:\n\n#define C6TEXT \\\n\"CONGRATULATIONS, YOU'VE FOUND THE\\n\"\\\n\"SUPER SECRET LEVEL!  YOU'D BETTER\\n\"\\\n\"BLAZE THROUGH THIS ONE!\\n\"\n\n\n// after map 06\t\n\n#define P1TEXT  \\\n\"You gloat over the steaming carcass of the\\n\"\\\n\"Guardian.  With its death, you've wrested\\n\"\\\n\"the Accelerator from the stinking claws\\n\"\\\n\"of Hell.  You relax and glance around the\\n\"\\\n\"room.  Damn!  There was supposed to be at\\n\"\\\n\"least one working prototype, but you can't\\n\"\\\n\"see it. The demons must have taken it.\\n\"\\\n\"\\n\"\\\n\"You must find the prototype, or all your\\n\"\\\n\"struggles will have been wasted. Keep\\n\"\\\n\"moving, keep fighting, keep killing.\\n\"\\\n\"Oh yes, keep living, too.\"\n\n\n// after map 11\n\n#define P2TEXT \\\n\"Even the deadly Arch-Vile labyrinth could\\n\"\\\n\"not stop you, and you've gotten to the\\n\"\\\n\"prototype Accelerator which is soon\\n\"\\\n\"efficiently and permanently deactivated.\\n\"\\\n\"\\n\"\\\n\"You're good at that kind of thing.\"\n\n\n// after map 20\n\n#define P3TEXT \\\n\"You've bashed and battered your way into\\n\"\\\n\"the heart of the devil-hive.  Time for a\\n\"\\\n\"Search-and-Destroy mission, aimed at the\\n\"\\\n\"Gatekeeper, whose foul offspring is\\n\"\\\n\"cascading to Earth.  Yeah, he's bad. But\\n\"\\\n\"you know who's worse!\\n\"\\\n\"\\n\"\\\n\"Grinning evilly, you check your gear, and\\n\"\\\n\"get ready to give the bastard a little Hell\\n\"\\\n\"of your own making!\"\n\n// after map 30\n\n#define P4TEXT \\\n\"The Gatekeeper's evil face is splattered\\n\"\\\n\"all over the place.  As its tattered corpse\\n\"\\\n\"collapses, an inverted Gate forms and\\n\"\\\n\"sucks down the shards of the last\\n\"\\\n\"prototype Accelerator, not to mention the\\n\"\\\n\"few remaining demons.  You're done. Hell\\n\"\\\n\"has gone back to pounding bad dead folks \\n\"\\\n\"instead of good live ones.  Remember to\\n\"\\\n\"tell your grandkids to put a rocket\\n\"\\\n\"launcher in your coffin. If you go to Hell\\n\"\\\n\"when you die, you'll need it for some\\n\"\\\n\"final cleaning-up ...\"\n\n// before map 31\n\n#define P5TEXT \\\n\"You've found the second-hardest level we\\n\"\\\n\"got. Hope you have a saved game a level or\\n\"\\\n\"two previous.  If not, be prepared to die\\n\"\\\n\"aplenty. For master marines only.\"\n\n// before map 32\n\n#define P6TEXT \\\n\"Betcha wondered just what WAS the hardest\\n\"\\\n\"level we had ready for ya?  Now you know.\\n\"\\\n\"No one gets out alive.\"\n\n\n#define T1TEXT \\\n\"You've fought your way out of the infested\\n\"\\\n\"experimental labs.   It seems that UAC has\\n\"\\\n\"once again gulped it down.  With their\\n\"\\\n\"high turnover, it must be hard for poor\\n\"\\\n\"old UAC to buy corporate health insurance\\n\"\\\n\"nowadays..\\n\"\\\n\"\\n\"\\\n\"Ahead lies the military complex, now\\n\"\\\n\"swarming with diseased horrors hot to get\\n\"\\\n\"their teeth into you. With luck, the\\n\"\\\n\"complex still has some warlike ordnance\\n\"\\\n\"laying around.\"\n\n\n#define T2TEXT \\\n\"You hear the grinding of heavy machinery\\n\"\\\n\"ahead.  You sure hope they're not stamping\\n\"\\\n\"out new hellspawn, but you're ready to\\n\"\\\n\"ream out a whole herd if you have to.\\n\"\\\n\"They might be planning a blood feast, but\\n\"\\\n\"you feel about as mean as two thousand\\n\"\\\n\"maniacs packed into one mad killer.\\n\"\\\n\"\\n\"\\\n\"You don't plan to go down easy.\"\n\n\n#define T3TEXT \\\n\"The vista opening ahead looks real damn\\n\"\\\n\"familiar. Smells familiar, too -- like\\n\"\\\n\"fried excrement. You didn't like this\\n\"\\\n\"place before, and you sure as hell ain't\\n\"\\\n\"planning to like it now. The more you\\n\"\\\n\"brood on it, the madder you get.\\n\"\\\n\"Hefting your gun, an evil grin trickles\\n\"\\\n\"onto your face. Time to take some names.\"\n\n#define T4TEXT \\\n\"Suddenly, all is silent, from one horizon\\n\"\\\n\"to the other. The agonizing echo of Hell\\n\"\\\n\"fades away, the nightmare sky turns to\\n\"\\\n\"blue, the heaps of monster corpses start \\n\"\\\n\"to evaporate along with the evil stench \\n\"\\\n\"that filled the air. Jeeze, maybe you've\\n\"\\\n\"done it. Have you really won?\\n\"\\\n\"\\n\"\\\n\"Something rumbles in the distance.\\n\"\\\n\"A blue light begins to glow inside the\\n\"\\\n\"ruined skull of the demon-spitter.\"\n\n\n#define T5TEXT \\\n\"What now? Looks totally different. Kind\\n\"\\\n\"of like King Tut's condo. Well,\\n\"\\\n\"whatever's here can't be any worse\\n\"\\\n\"than usual. Can it?  Or maybe it's best\\n\"\\\n\"to let sleeping gods lie..\"\n\n\n#define T6TEXT \\\n\"Time for a vacation. You've burst the\\n\"\\\n\"bowels of hell and by golly you're ready\\n\"\\\n\"for a break. You mutter to yourself,\\n\"\\\n\"Maybe someone else can kick Hell's ass\\n\"\\\n\"next time around. Ahead lies a quiet town,\\n\"\\\n\"with peaceful flowing water, quaint\\n\"\\\n\"buildings, and presumably no Hellspawn.\\n\"\\\n\"\\n\"\\\n\"As you step off the transport, you hear\\n\"\\\n\"the stomp of a cyberdemon's iron shoe.\"\n\n\n\n//\n// Character cast strings F_FINALE.C\n//\n#define CC_ZOMBIE\t\"ZOMBIEMAN\"\n#define CC_SHOTGUN\t\"SHOTGUN GUY\"\n#define CC_HEAVY\t\"HEAVY WEAPON DUDE\"\n#define CC_IMP\t\"IMP\"\n#define CC_DEMON\t\"DEMON\"\n#define CC_LOST\t\"LOST SOUL\"\n#define CC_CACO\t\"CACODEMON\"\n#define CC_HELL\t\"HELL KNIGHT\"\n#define CC_BARON\t\"BARON OF HELL\"\n#define CC_ARACH\t\"ARACHNOTRON\"\n#define CC_PAIN\t\"PAIN ELEMENTAL\"\n#define CC_REVEN\t\"REVENANT\"\n#define CC_MANCU\t\"MANCUBUS\"\n#define CC_ARCH\t\"ARCH-VILE\"\n#define CC_SPIDER\t\"THE SPIDER MASTERMIND\"\n#define CC_CYBER\t\"THE CYBERDEMON\"\n#define CC_HERO\t\"OUR HERO\"\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/d_event.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n//\n// DESCRIPTION: Event handling.\n//\n// Events are asynchronous inputs generally generated by the game user.\n// Events can be discarded if no responder claims them\n//\n\n#include <stdlib.h>\n#include \"d_event.h\"\n\n#define MAXEVENTS 64\n\nstatic event_t events[MAXEVENTS];\nstatic int eventhead;\nstatic int eventtail;\n\n//\n// D_PostEvent\n// Called by the I/O functions when input is detected\n//\nvoid D_PostEvent (event_t* ev)\n{\n    events[eventhead] = *ev;\n    eventhead = (eventhead + 1) % MAXEVENTS;\n}\n\n// Read an event from the queue.\n\nevent_t *D_PopEvent(void)\n{\n    event_t *result;\n\n    // No more events waiting.\n\n    if (eventtail == eventhead)\n    {\n        return NULL;\n    }\n    \n    result = &events[eventtail];\n\n    // Advance to the next event in the queue.\n\n    eventtail = (eventtail + 1) % MAXEVENTS;\n\n    return result;\n}\n\n\n"
  },
  {
    "path": "fbdoom/d_event.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\n//    \n\n\n#ifndef __D_EVENT__\n#define __D_EVENT__\n\n\n#include \"doomtype.h\"\n\n\n//\n// Event handling.\n//\n\n// Input event types.\ntypedef enum\n{\n    ev_keydown,\n    ev_keyup,\n    ev_mouse,\n    ev_joystick,\n    ev_quit\n} evtype_t;\n\n// Event structure.\ntypedef struct\n{\n    evtype_t type;\n\n    // Event-related data that depends on the type of event:\n    //\n    // ev_keydown/ev_keyup:\n    //    data1: Key code (from doomkeys.h) of the key that was\n    //           pressed or released.\n    //    data2: Ascii text of the character that was pressed,\n    //           shifted appropriately (eg. '$' if 4 was pressed\n    //           while shift was held).\n    //\n    // ev_mouse:\n    //    data1: Bitfield of buttons currently held down.\n    //           (bit 0 = left; bit 1 = right; bit 2 = middle).\n    //    data2: X axis mouse movement (turn).\n    //    data3: Y axis mouse movement (forward/backward).\n    //\n    // ev_joystick:\n    //    data1: Bitfield of buttons currently pressed.\n    //    data2: X axis mouse movement (turn).\n    //    data3: Y axis mouse movement (forward/backward).\n    //    data4: Third axis mouse movement (strafe).\n\n    int data1, data2, data3, data4;\n} event_t;\n\n \n//\n// Button/action code definitions.\n//\ntypedef enum\n{\n    // Press \"Fire\".\n    BT_ATTACK\t\t= 1,\n    // Use button, to open doors, activate switches.\n    BT_USE\t\t= 2,\n\n    // Flag: game events, not really buttons.\n    BT_SPECIAL\t\t= 128,\n    BT_SPECIALMASK\t= 3,\n    \n    // Flag, weapon change pending.\n    // If true, the next 3 bits hold weapon num.\n    BT_CHANGE\t\t= 4,\n    // The 3bit weapon mask and shift, convenience.\n    BT_WEAPONMASK\t= (8+16+32),\n    BT_WEAPONSHIFT\t= 3,\n\n    // Pause the game.\n    BTS_PAUSE\t\t= 1,\n    // Save the game at each console.\n    BTS_SAVEGAME\t= 2,\n\n    // Savegame slot numbers\n    //  occupy the second byte of buttons.    \n    BTS_SAVEMASK\t= (4+8+16),\n    BTS_SAVESHIFT \t= 2,\n  \n} buttoncode_t;\n\n// villsa [STRIFE] Strife specific buttons\n// TODO - not finished\ntypedef enum\n{\n    // Player view look up\n    BT2_LOOKUP          = 1,\n    // Player view look down\n    BT2_LOOKDOWN        = 2,\n    // Center player's view\n    BT2_CENTERVIEW      = 4,\n    // Use inventory item\n    BT2_INVUSE          = 8,\n    // Drop inventory item\n    BT2_INVDROP         = 16,\n    // Jump up and down\n    BT2_JUMP            = 32,\n    // Use medkit\n    BT2_HEALTH          = 128,\n  \n} buttoncode2_t;\n\n\n\n\n// Called by IO functions when input is detected.\nvoid D_PostEvent (event_t *ev);\n\n// Read an event from the event queue\n\nevent_t *D_PopEvent(void);\n\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/d_items.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\n\n\n// We are referring to sprite numbers.\n#include \"info.h\"\n\n#include \"d_items.h\"\n\n\n//\n// PSPRITE ACTIONS for waepons.\n// This struct controls the weapon animations.\n//\n// Each entry is:\n//   ammo/amunition type\n//  upstate\n//  downstate\n// readystate\n// atkstate, i.e. attack/fire/hit frame\n// flashstate, muzzle flash\n//\nweaponinfo_t\tweaponinfo[NUMWEAPONS] =\n{\n    {\n\t// fist\n\tam_noammo,\n\tS_PUNCHUP,\n\tS_PUNCHDOWN,\n\tS_PUNCH,\n\tS_PUNCH1,\n\tS_NULL\n    },\t\n    {\n\t// pistol\n\tam_clip,\n\tS_PISTOLUP,\n\tS_PISTOLDOWN,\n\tS_PISTOL,\n\tS_PISTOL1,\n\tS_PISTOLFLASH\n    },\t\n    {\n\t// shotgun\n\tam_shell,\n\tS_SGUNUP,\n\tS_SGUNDOWN,\n\tS_SGUN,\n\tS_SGUN1,\n\tS_SGUNFLASH1\n    },\n    {\n\t// chaingun\n\tam_clip,\n\tS_CHAINUP,\n\tS_CHAINDOWN,\n\tS_CHAIN,\n\tS_CHAIN1,\n\tS_CHAINFLASH1\n    },\n    {\n\t// missile launcher\n\tam_misl,\n\tS_MISSILEUP,\n\tS_MISSILEDOWN,\n\tS_MISSILE,\n\tS_MISSILE1,\n\tS_MISSILEFLASH1\n    },\n    {\n\t// plasma rifle\n\tam_cell,\n\tS_PLASMAUP,\n\tS_PLASMADOWN,\n\tS_PLASMA,\n\tS_PLASMA1,\n\tS_PLASMAFLASH1\n    },\n    {\n\t// bfg 9000\n\tam_cell,\n\tS_BFGUP,\n\tS_BFGDOWN,\n\tS_BFG,\n\tS_BFG1,\n\tS_BFGFLASH1\n    },\n    {\n\t// chainsaw\n\tam_noammo,\n\tS_SAWUP,\n\tS_SAWDOWN,\n\tS_SAW,\n\tS_SAW1,\n\tS_NULL\n    },\n    {\n\t// super shotgun\n\tam_shell,\n\tS_DSGUNUP,\n\tS_DSGUNDOWN,\n\tS_DSGUN,\n\tS_DSGUN1,\n\tS_DSGUNFLASH1\n    },\t\n};\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "fbdoom/d_items.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tItems: key cards, artifacts, weapon, ammunition.\n//\n\n\n#ifndef __D_ITEMS__\n#define __D_ITEMS__\n\n#include \"doomdef.h\"\n\n\n\n// Weapon info: sprite frames, ammunition use.\ntypedef struct\n{\n    ammotype_t\tammo;\n    int\t\tupstate;\n    int\t\tdownstate;\n    int\t\treadystate;\n    int\t\tatkstate;\n    int\t\tflashstate;\n\n} weaponinfo_t;\n\nextern  weaponinfo_t    weaponinfo[NUMWEAPONS];\n\n#endif\n"
  },
  {
    "path": "fbdoom/d_iwad.c",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     Search for and locate an IWAD file, and initialize according\n//     to the IWAD type.\n//\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <string.h>\n\n#include \"config.h\"\n#include \"deh_str.h\"\n#include \"doomkeys.h\"\n#include \"d_iwad.h\"\n#include \"i_system.h\"\n#include \"m_argv.h\"\n#include \"m_config.h\"\n#include \"m_misc.h\"\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\nstatic const iwad_t iwads[] =\n{\n    { \"doom2.wad\",    doom2,     commercial, \"Doom II\" },\n    { \"plutonia.wad\", pack_plut, commercial, \"Final Doom: Plutonia Experiment\" },\n    { \"tnt.wad\",      pack_tnt,  commercial, \"Final Doom: TNT: Evilution\" },\n    { \"doom.wad\",     doom,      retail,     \"Doom\" },\n    { \"DOOM1.WAD\",    doom,      shareware,  \"Doom Shareware\" },\n    { \"chex.wad\",     pack_chex, shareware,  \"Chex Quest\" },\n    { \"hacx.wad\",     pack_hacx, commercial, \"Hacx\" },\n    { \"freedm.wad\",   doom2,     commercial, \"FreeDM\" },\n    { \"freedoom2.wad\", doom2,    commercial, \"Freedoom: Phase 2\" },\n    { \"freedoom1.wad\", doom,     retail,     \"Freedoom: Phase 1\" },\n    { \"heretic.wad\",  heretic,   retail,     \"Heretic\" },\n    { \"heretic1.wad\", heretic,   shareware,  \"Heretic Shareware\" },\n    { \"hexen.wad\",    hexen,     commercial, \"Hexen\" },\n    //{ \"strife0.wad\",  strife,    commercial, \"Strife\" }, // haleyjd: STRIFE-FIXME\n    { \"strife1.wad\",  strife,    commercial, \"Strife\" },\n};\n\n// Array of locations to search for IWAD files\n//\n// \"128 IWAD search directories should be enough for anybody\".\n\n#define MAX_IWAD_DIRS 128\n\nstatic boolean iwad_dirs_built = false;\nstatic char *iwad_dirs[MAX_IWAD_DIRS];\nstatic int num_iwad_dirs = 0;\n\nstatic void AddIWADDir(char *dir)\n{\n    if (num_iwad_dirs < MAX_IWAD_DIRS)\n    {\n        iwad_dirs[num_iwad_dirs] = dir;\n        ++num_iwad_dirs;\n    }\n}\n\n// This is Windows-specific code that automatically finds the location\n// of installed IWAD files.  The registry is inspected to find special\n// keys installed by the Windows installers for various CD versions\n// of Doom.  From these keys we can deduce where to find an IWAD.\n\n#if defined(_WIN32) && !defined(_WIN32_WCE)\n\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n\ntypedef struct \n{\n    HKEY root;\n    char *path;\n    char *value;\n} registry_value_t;\n\n#define UNINSTALLER_STRING \"\\\\uninstl.exe /S \"\n\n// Keys installed by the various CD editions.  These are actually the \n// commands to invoke the uninstaller and look like this:\n//\n// C:\\Program Files\\Path\\uninstl.exe /S C:\\Program Files\\Path\n//\n// With some munging we can find where Doom was installed.\n\n// [AlexMax] From the persepctive of a 64-bit executable, 32-bit registry\n// keys are located in a different spot.\n#if _WIN64\n#define SOFTWARE_KEY \"Software\\\\Wow6432Node\"\n#else\n#define SOFTWARE_KEY \"Software\"\n#endif\n\nstatic registry_value_t uninstall_values[] =\n{\n    // Ultimate Doom, CD version (Depths of Doom trilogy)\n\n    {\n        HKEY_LOCAL_MACHINE,\n        SOFTWARE_KEY \"\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\\"\n            \"Uninstall\\\\Ultimate Doom for Windows 95\",\n        \"UninstallString\",\n    },\n\n    // Doom II, CD version (Depths of Doom trilogy)\n\n    {\n        HKEY_LOCAL_MACHINE,\n        SOFTWARE_KEY \"\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\\"\n            \"Uninstall\\\\Doom II for Windows 95\",\n        \"UninstallString\",\n    },\n\n    // Final Doom\n\n    {\n        HKEY_LOCAL_MACHINE,\n        SOFTWARE_KEY \"\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\\"\n            \"Uninstall\\\\Final Doom for Windows 95\",\n        \"UninstallString\",\n    },\n\n    // Shareware version\n\n    {\n        HKEY_LOCAL_MACHINE,\n        SOFTWARE_KEY \"\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\\"\n            \"Uninstall\\\\Doom Shareware for Windows 95\",\n        \"UninstallString\",\n    },\n};\n\n// Value installed by the Collector's Edition when it is installed\n\nstatic registry_value_t collectors_edition_value =\n{\n    HKEY_LOCAL_MACHINE,\n    SOFTWARE_KEY \"\\\\Activision\\\\DOOM Collector's Edition\\\\v1.0\",\n    \"INSTALLPATH\",\n};\n\n// Subdirectories of the above install path, where IWADs are installed.\n\nstatic char *collectors_edition_subdirs[] = \n{\n    \"Doom2\",\n    \"Final Doom\",\n    \"Ultimate Doom\",\n};\n\n// Location where Steam is installed\n\nstatic registry_value_t steam_install_location =\n{\n    HKEY_LOCAL_MACHINE,\n    SOFTWARE_KEY \"\\\\Valve\\\\Steam\",\n    \"InstallPath\",\n};\n\n// Subdirs of the steam install directory where IWADs are found\n\nstatic char *steam_install_subdirs[] =\n{\n    \"steamapps\\\\common\\\\doom 2\\\\base\",\n    \"steamapps\\\\common\\\\final doom\\\\base\",\n    \"steamapps\\\\common\\\\ultimate doom\\\\base\",\n    \"steamapps\\\\common\\\\heretic shadow of the serpent riders\\\\base\",\n    \"steamapps\\\\common\\\\hexen\\\\base\",\n    \"steamapps\\\\common\\\\hexen deathkings of the dark citadel\\\\base\",\n\n    // From Doom 3: BFG Edition:\n\n    \"steamapps\\\\common\\\\DOOM 3 BFG Edition\\\\base\\\\wads\",\n};\n\n#define STEAM_BFG_GUS_PATCHES \\\n    \"steamapps\\\\common\\\\DOOM 3 BFG Edition\\\\base\\\\classicmusic\\\\instruments\"\n\nstatic char *GetRegistryString(registry_value_t *reg_val)\n{\n    HKEY key;\n    DWORD len;\n    DWORD valtype;\n    char *result;\n\n    // Open the key (directory where the value is stored)\n\n    if (RegOpenKeyEx(reg_val->root, reg_val->path,\n                     0, KEY_READ, &key) != ERROR_SUCCESS)\n    {\n        return NULL;\n    }\n\n    result = NULL;\n\n    // Find the type and length of the string, and only accept strings.\n\n    if (RegQueryValueEx(key, reg_val->value,\n                        NULL, &valtype, NULL, &len) == ERROR_SUCCESS\n     && valtype == REG_SZ)\n    {\n        // Allocate a buffer for the value and read the value\n\n        result = malloc(len);\n\n        if (RegQueryValueEx(key, reg_val->value, NULL, &valtype,\n                            (unsigned char *) result, &len) != ERROR_SUCCESS)\n        {\n            free(result);\n            result = NULL;\n        }\n    }\n\n    // Close the key\n\n    RegCloseKey(key);\n\n    return result;\n}\n\n// Check for the uninstall strings from the CD versions\n\nstatic void CheckUninstallStrings(void)\n{\n    unsigned int i;\n\n    for (i=0; i<arrlen(uninstall_values); ++i)\n    {\n        char *val;\n        char *path;\n        char *unstr;\n\n        val = GetRegistryString(&uninstall_values[i]);\n\n        if (val == NULL)\n        {\n            continue;\n        }\n\n        unstr = strstr(val, UNINSTALLER_STRING);\n\n        if (unstr == NULL)\n        {\n            free(val);\n        }\n        else\n        {\n            path = unstr + strlen(UNINSTALLER_STRING);\n\n            AddIWADDir(path);\n        }\n    }\n}\n\n// Check for Doom: Collector's Edition\n\nstatic void CheckCollectorsEdition(void)\n{\n    char *install_path;\n    char *subpath;\n    unsigned int i;\n\n    install_path = GetRegistryString(&collectors_edition_value);\n\n    if (install_path == NULL)\n    {\n        return;\n    }\n\n    for (i=0; i<arrlen(collectors_edition_subdirs); ++i)\n    {\n        subpath = M_StringJoin(install_path, DIR_SEPARATOR_S,\n                               collectors_edition_subdirs[i], NULL);\n\n        AddIWADDir(subpath);\n    }\n\n    free(install_path);\n}\n\n\n// Check for Doom downloaded via Steam\n\nstatic void CheckSteamEdition(void)\n{\n    char *install_path;\n    char *subpath;\n    size_t i;\n\n    install_path = GetRegistryString(&steam_install_location);\n\n    if (install_path == NULL)\n    {\n        return;\n    }\n\n    for (i=0; i<arrlen(steam_install_subdirs); ++i)\n    {\n        subpath = M_StringJoin(install_path, DIR_SEPARATOR_S,\n                               steam_install_subdirs[i], NULL);\n\n        AddIWADDir(subpath);\n    }\n\n    free(install_path);\n}\n\n// The BFG edition ships with a full set of GUS patches. If we find them,\n// we can autoconfigure to use them.\n\nstatic void CheckSteamGUSPatches(void)\n{\n    const char *current_path;\n    char *install_path;\n    char *patch_path;\n    int len;\n\n    // Already configured? Don't stomp on the user's choices.\n    current_path = M_GetStrVariable(\"gus_patch_path\");\n    if (current_path != NULL && strlen(current_path) > 0)\n    {\n        return;\n    }\n\n    install_path = GetRegistryString(&steam_install_location);\n\n    if (install_path == NULL)\n    {\n        return;\n    }\n\n    len = strlen(install_path) + strlen(STEAM_BFG_GUS_PATCHES) + 20;\n    patch_path = malloc(len);\n    M_snprintf(patch_path, len, \"%s\\\\%s\\\\ACBASS.PAT\",\n               install_path, STEAM_BFG_GUS_PATCHES);\n\n    // Does acbass.pat exist? If so, then set gus_patch_path.\n    if (M_FileExists(patch_path))\n    {\n        M_snprintf(patch_path, len, \"%s\\\\%s\",\n                   install_path, STEAM_BFG_GUS_PATCHES);\n        M_SetVariable(\"gus_patch_path\", patch_path);\n    }\n\n    free(patch_path);\n    free(install_path);\n}\n\n// Default install directories for DOS Doom\n\nstatic void CheckDOSDefaults(void)\n{\n    // These are the default install directories used by the deice\n    // installer program:\n\n    AddIWADDir(\"\\\\doom2\");              // Doom II\n    AddIWADDir(\"\\\\plutonia\");           // Final Doom\n    AddIWADDir(\"\\\\tnt\");\n    AddIWADDir(\"\\\\doom_se\");            // Ultimate Doom\n    AddIWADDir(\"\\\\doom\");               // Shareware / Registered Doom\n    AddIWADDir(\"\\\\dooms\");              // Shareware versions\n    AddIWADDir(\"\\\\doomsw\");\n\n    AddIWADDir(\"\\\\heretic\");            // Heretic\n    AddIWADDir(\"\\\\hrtic_se\");           // Heretic Shareware from Quake disc\n\n    AddIWADDir(\"\\\\hexen\");              // Hexen\n    AddIWADDir(\"\\\\hexendk\");            // Hexen Deathkings of the Dark Citadel\n\n    AddIWADDir(\"\\\\strife\");             // Strife\n}\n\n#endif\n\n// Returns true if the specified path is a path to a file\n// of the specified name.\n\nstatic boolean DirIsFile(char *path, char *filename)\n{\n    size_t path_len;\n    size_t filename_len;\n\n    path_len = strlen(path);\n    filename_len = strlen(filename);\n\n    return path_len >= filename_len + 1\n        && path[path_len - filename_len - 1] == DIR_SEPARATOR\n        && !strcasecmp(&path[path_len - filename_len], filename);\n}\n\n// Check if the specified directory contains the specified IWAD\n// file, returning the full path to the IWAD if found, or NULL\n// if not found.\n\nstatic char *CheckDirectoryHasIWAD(char *dir, char *iwadname)\n{\n    char *filename; \n\n    // As a special case, the \"directory\" may refer directly to an\n    // IWAD file if the path comes from DOOMWADDIR or DOOMWADPATH.\n\n    if (DirIsFile(dir, iwadname) && M_FileExists(dir))\n    {\n        return strdup(dir);\n    }\n\n    // Construct the full path to the IWAD if it is located in\n    // this directory, and check if it exists.\n\n    if (!strcmp(dir, \".\"))\n    {\n        filename = strdup(iwadname);\n    }\n    else\n    {\n        filename = M_StringJoin(dir, DIR_SEPARATOR_S, iwadname, NULL);\n    }\n\n    if (M_FileExists(filename))\n    {\n        return filename;\n    }\n\n    free(filename);\n\n    return NULL;\n}\n\n// Search a directory to try to find an IWAD\n// Returns the location of the IWAD if found, otherwise NULL.\n\nstatic char *SearchDirectoryForIWAD(char *dir, int mask, GameMission_t *mission)\n{\n    char *filename;\n    size_t i;\n\n    for (i=0; i<arrlen(iwads); ++i) \n    {\n        if (((1 << iwads[i].mission) & mask) == 0)\n        {\n            continue;\n        }\n\n        filename = CheckDirectoryHasIWAD(dir, DEH_String(iwads[i].name));\n\n        if (filename != NULL)\n        {\n            *mission = iwads[i].mission;\n\n            return filename;\n        }\n    }\n\n    return NULL;\n}\n\n// When given an IWAD with the '-iwad' parameter,\n// attempt to identify it by its name.\n\nstatic GameMission_t IdentifyIWADByName(char *name, int mask)\n{\n    size_t i;\n    GameMission_t mission;\n    char *p;\n\n    p = strrchr(name, DIR_SEPARATOR);\n\n    if (p != NULL)\n    {\n        name = p + 1;\n    }\n\n    mission = none;\n\n    for (i=0; i<arrlen(iwads); ++i)\n    {\n        // Check if the filename is this IWAD name.\n\n        // Only use supported missions:\n\n        if (((1 << iwads[i].mission) & mask) == 0)\n            continue;\n\n        // Check if it ends in this IWAD name.\n\n        if (!strcasecmp(name, iwads[i].name))\n        {\n            mission = iwads[i].mission;\n            break;\n        }\n    }\n\n    return mission;\n}\n\n#if ORIGCODE\n//\n// Add directories from the list in the DOOMWADPATH environment variable.\n//\n\nstatic void AddDoomWadPath(void)\n{\n    char *doomwadpath;\n    char *p;\n\n    // Check the DOOMWADPATH environment variable.\n\n    doomwadpath = getenv(\"DOOMWADPATH\");\n\n    if (doomwadpath == NULL)\n    {\n        return;\n    }\n\n    doomwadpath = strdup(doomwadpath);\n\n    // Add the initial directory\n\n    AddIWADDir(doomwadpath);\n\n    // Split into individual dirs within the list.\n\n    p = doomwadpath;\n\n    for (;;)\n    {\n        p = strchr(p, PATH_SEPARATOR);\n\n        if (p != NULL)\n        {\n            // Break at the separator and store the right hand side\n            // as another iwad dir\n  \n            *p = '\\0';\n            p += 1;\n\n            AddIWADDir(p);\n        }\n        else\n        {\n            break;\n        }\n    }\n}\n\n#endif\n\n//\n// Build a list of IWAD files\n//\n\nstatic void BuildIWADDirList(void)\n{\n#if ORIGCODE\n    char *doomwaddir;\n\n    if (iwad_dirs_built)\n    {\n        return;\n    }\n\n    // Look in the current directory.  Doom always does this.\n\n    AddIWADDir(\".\");\n\n    // Add DOOMWADDIR if it is in the environment\n\n    doomwaddir = getenv(\"DOOMWADDIR\");\n\n    if (doomwaddir != NULL)\n    {\n        AddIWADDir(doomwaddir);\n    }        \n\n    // Add dirs from DOOMWADPATH\n\n    AddDoomWadPath();\n\n#ifdef _WIN32\n\n    // Search the registry and find where IWADs have been installed.\n\n    CheckUninstallStrings();\n    CheckCollectorsEdition();\n    CheckSteamEdition();\n    CheckDOSDefaults();\n\n    // Check for GUS patches installed with the BFG edition!\n\n    CheckSteamGUSPatches();\n\n#else\n\n    // Standard places where IWAD files are installed under Unix.\n\n    AddIWADDir(\"/usr/share/games/doom\");\n    AddIWADDir(\"/usr/local/share/games/doom\");\n\n#endif\n#else\n    AddIWADDir (FILES_DIR);\n\n    // Don't run this function again.\n\n    iwad_dirs_built = true;\n#endif\n}\n\n//\n// Searches WAD search paths for an WAD with a specific filename.\n// \n\nchar *D_FindWADByName(char *name)\n{\n    char *path;\n    int i;\n    \n    // Absolute path?\n\n    if (M_FileExists(name))\n    {\n        return name;\n    }\n\n    BuildIWADDirList();\n\n    // Search through all IWAD paths for a file with the given name.\n\n    for (i=0; i<num_iwad_dirs; ++i)\n    {\n        // As a special case, if this is in DOOMWADDIR or DOOMWADPATH,\n        // the \"directory\" may actually refer directly to an IWAD\n        // file.\n\n        if (DirIsFile(iwad_dirs[i], name) && M_FileExists(iwad_dirs[i]))\n        {\n            return strdup(iwad_dirs[i]);\n        }\n\n        // Construct a string for the full path\n\n        path = M_StringJoin(iwad_dirs[i], DIR_SEPARATOR_S, name, NULL);\n\n        if (M_FileExists(path))\n        {\n            return path;\n        }\n\n        free(path);\n    }\n\n    // File not found\n\n    return NULL;\n}\n\n//\n// D_TryWADByName\n//\n// Searches for a WAD by its filename, or passes through the filename\n// if not found.\n//\n\nchar *D_TryFindWADByName(char *filename)\n{\n    char *result;\n\n    result = D_FindWADByName(filename);\n\n    if (result != NULL)\n    {\n        return result;\n    }\n    else\n    {\n        return filename;\n    }\n}\n\n//\n// FindIWAD\n// Checks availability of IWAD files by name,\n// to determine whether registered/commercial features\n// should be executed (notably loading PWADs).\n//\n\nchar *D_FindIWAD(int mask, GameMission_t *mission)\n{\n    char *result;\n    char *iwadfile;\n    int iwadparm;\n    int i;\n\n    // Check for the -iwad parameter\n\n    //!\n    // Specify an IWAD file to use.\n    //\n    // @arg <file>\n    //\n\n    iwadparm = M_CheckParmWithArgs(\"-iwad\", 1);\n\n    if (iwadparm)\n    {\n        // Search through IWAD dirs for an IWAD with the given name.\n\n        iwadfile = myargv[iwadparm + 1];\n\n        result = D_FindWADByName(iwadfile);\n\n        if (result == NULL)\n        {\n            I_Error(\"IWAD file '%s' not found!\", iwadfile);\n        }\n        \n        *mission = IdentifyIWADByName(result, mask);\n    }\n    else\n    {\n        // Search through the list and look for an IWAD\n\n        result = NULL;\n\n        BuildIWADDirList();\n    \n        for (i=0; result == NULL && i<num_iwad_dirs; ++i)\n        {\n            result = SearchDirectoryForIWAD(iwad_dirs[i], mask, mission);\n        }\n    }\n\n    return result;\n}\n\n// Find all IWADs in the IWAD search path matching the given mask.\n\nconst iwad_t **D_FindAllIWADs(int mask)\n{\n    const iwad_t **result;\n    int result_len;\n    char *filename;\n    int i;\n\n    result = malloc(sizeof(iwad_t *) * (arrlen(iwads) + 1));\n    result_len = 0;\n\n    // Try to find all IWADs\n\n    for (i=0; i<arrlen(iwads); ++i)\n    {\n        if (((1 << iwads[i].mission) & mask) == 0)\n        {\n            continue;\n        }\n\n        filename = D_FindWADByName(iwads[i].name);\n\n        if (filename != NULL)\n        {\n            result[result_len] = &iwads[i];\n            ++result_len;\n        }\n    }\n\n    // End of list\n\n    result[result_len] = NULL;\n\n    return result;\n}\n\n//\n// Get the IWAD name used for savegames.\n//\n\nchar *D_SaveGameIWADName(GameMission_t gamemission)\n{\n    size_t i;\n\n    // Determine the IWAD name to use for savegames.\n    // This determines the directory the savegame files get put into.\n    //\n    // Note that we match on gamemission rather than on IWAD name.\n    // This ensures that doom1.wad and doom.wad saves are stored\n    // in the same place.\n\n    for (i=0; i<arrlen(iwads); ++i)\n    {\n        if (gamemission == iwads[i].mission)\n        {\n            return iwads[i].name;\n        }\n    }\n\n    // Default fallback:\n\n    return \"unknown.wad\";\n}\n\nchar *D_SuggestIWADName(GameMission_t mission, GameMode_t mode)\n{\n    int i;\n\n    for (i = 0; i < arrlen(iwads); ++i)\n    {\n        if (iwads[i].mission == mission && iwads[i].mode == mode)\n        {\n            return iwads[i].name;\n        }\n    }\n\n    return \"unknown.wad\";\n}\n\nchar *D_SuggestGameName(GameMission_t mission, GameMode_t mode)\n{\n    int i;\n\n    for (i = 0; i < arrlen(iwads); ++i)\n    {\n        if (iwads[i].mission == mission\n         && (mode == indetermined || iwads[i].mode == mode))\n        {\n            return iwads[i].description;\n        }\n    }\n\n    return \"Unknown game?\";\n}\n\n"
  },
  {
    "path": "fbdoom/d_iwad.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     Find IWAD and initialize according to IWAD type.\n//\n\n\n#ifndef __D_IWAD__\n#define __D_IWAD__\n\n#include \"d_mode.h\"\n\n#define IWAD_MASK_DOOM    ((1 << doom)           \\\n                         | (1 << doom2)          \\\n                         | (1 << pack_tnt)       \\\n                         | (1 << pack_plut)      \\\n                         | (1 << pack_chex)      \\\n                         | (1 << pack_hacx))\n#define IWAD_MASK_HERETIC (1 << heretic)\n#define IWAD_MASK_HEXEN   (1 << hexen)\n#define IWAD_MASK_STRIFE  (1 << strife)\n\ntypedef struct\n{\n    char *name;\n    GameMission_t mission;\n    GameMode_t mode;\n    char *description;\n} iwad_t;\n\nchar *D_FindWADByName(char *filename);\nchar *D_TryFindWADByName(char *filename);\nchar *D_FindIWAD(int mask, GameMission_t *mission);\nconst iwad_t **D_FindAllIWADs(int mask);\nchar *D_SaveGameIWADName(GameMission_t gamemission);\nchar *D_SuggestIWADName(GameMission_t mission, GameMode_t mode);\nchar *D_SuggestGameName(GameMission_t mission, GameMode_t mode);\nvoid D_CheckCorrectIWAD(GameMission_t mission);\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/d_loop.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     Main loop code.\n//\n\n#include <stdlib.h>\n#include <string.h>\n\n#include \"doomfeatures.h\"\n\n#include \"d_event.h\"\n#include \"d_loop.h\"\n#include \"d_ticcmd.h\"\n\n#include \"i_system.h\"\n#include \"i_timer.h\"\n#include \"i_video.h\"\n\n#include \"m_argv.h\"\n#include \"m_fixed.h\"\n\n#include \"net_client.h\"\n#include \"net_gui.h\"\n#include \"net_io.h\"\n#include \"net_query.h\"\n#include \"net_server.h\"\n#include \"net_sdl.h\"\n#include \"net_loop.h\"\n\n// The complete set of data for a particular tic.\n\ntypedef struct\n{\n    ticcmd_t cmds[NET_MAXPLAYERS];\n    boolean ingame[NET_MAXPLAYERS];\n} ticcmd_set_t;\n\n//\n// gametic is the tic about to (or currently being) run\n// maketic is the tic that hasn't had control made for it yet\n// recvtic is the latest tic received from the server.\n//\n// a gametic cannot be run until ticcmds are received for it\n// from all players.\n//\n\nstatic ticcmd_set_t ticdata[BACKUPTICS];\n\n// The index of the next tic to be made (with a call to BuildTiccmd).\n\nstatic int maketic;\n\n// The number of complete tics received from the server so far.\n\nstatic int recvtic;\n\n// The number of tics that have been run (using RunTic) so far.\n\nint gametic;\n\n// When set to true, a single tic is run each time TryRunTics() is called.\n// This is used for -timedemo mode.\n\nboolean singletics = false;\n\n// Index of the local player.\n\nstatic int localplayer;\n\n// Used for original sync code.\n\nstatic int      skiptics = 0;\n\n// Reduce the bandwidth needed by sampling game input less and transmitting\n// less.  If ticdup is 2, sample half normal, 3 = one third normal, etc.\n\nint\t\tticdup;\n\n// Amount to offset the timer for game sync.\n\nfixed_t         offsetms;\n\n// Use new client syncronisation code\n\nstatic boolean  new_sync = true;\n\n// Callback functions for loop code.\n\nstatic loop_interface_t *loop_interface = NULL;\n\n// Current players in the multiplayer game.\n// This is distinct from playeringame[] used by the game code, which may\n// modify playeringame[] when playing back multiplayer demos.\n\nstatic boolean local_playeringame[NET_MAXPLAYERS];\n\n// Requested player class \"sent\" to the server on connect.\n// If we are only doing a single player game then this needs to be remembered\n// and saved in the game settings.\n\nstatic int player_class;\n\n\n// 35 fps clock adjusted by offsetms milliseconds\n\nstatic int GetAdjustedTime(void)\n{\n    int time_ms;\n\n    time_ms = I_GetTimeMS();\n\n    if (new_sync)\n    {\n\t// Use the adjustments from net_client.c only if we are\n\t// using the new sync mode.\n\n        time_ms += (offsetms / FRACUNIT);\n    }\n\n    return (time_ms * TICRATE) / 1000;\n}\n\nstatic boolean BuildNewTic(void)\n{\n    int\tgameticdiv;\n    ticcmd_t cmd;\n\n    gameticdiv = gametic/ticdup;\n\n    I_StartTic ();\n    loop_interface->ProcessEvents();\n\n    // Always run the menu\n\n    loop_interface->RunMenu();\n\n    if (drone)\n    {\n        // In drone mode, do not generate any ticcmds.\n\n        return false;\n    }\n\n    if (new_sync)\n    {\n       // If playing single player, do not allow tics to buffer\n       // up very far\n\n       if (!net_client_connected && maketic - gameticdiv > 2)\n           return false;\n\n       // Never go more than ~200ms ahead\n\n       if (maketic - gameticdiv > 8)\n           return false;\n    }\n    else\n    {\n       if (maketic - gameticdiv >= 5)\n           return false;\n    }\n\n    //printf (\"mk:%i \",maketic);\n    memset(&cmd, 0, sizeof(ticcmd_t));\n    loop_interface->BuildTiccmd(&cmd, maketic);\n\n#ifdef FEATURE_MULTIPLAYER\n\n    if (net_client_connected)\n    {\n        NET_CL_SendTiccmd(&cmd, maketic);\n    }\n\n#endif\n    ticdata[maketic % BACKUPTICS].cmds[localplayer] = cmd;\n    ticdata[maketic % BACKUPTICS].ingame[localplayer] = true;\n\n    ++maketic;\n\n    return true;\n}\n\n//\n// NetUpdate\n// Builds ticcmds for console player,\n// sends out a packet\n//\nint      lasttime;\n\nvoid NetUpdate (void)\n{\n    int nowtime;\n    int newtics;\n    int\ti;\n\n    // If we are running with singletics (timing a demo), this\n    // is all done separately.\n\n    if (singletics)\n        return;\n\n#ifdef FEATURE_MULTIPLAYER\n\n    // Run network subsystems\n\n    NET_CL_Run();\n    NET_SV_Run();\n\n#endif\n\n    // check time\n    nowtime = GetAdjustedTime() / ticdup;\n    newtics = nowtime - lasttime;\n\n    lasttime = nowtime;\n\n    if (skiptics <= newtics)\n    {\n        newtics -= skiptics;\n        skiptics = 0;\n    }\n    else\n    {\n        skiptics -= newtics;\n        newtics = 0;\n    }\n\n    // build new ticcmds for console player\n\n    for (i=0 ; i<newtics ; i++)\n    {\n        if (!BuildNewTic())\n        {\n            break;\n        }\n    }\n}\n\nstatic void D_Disconnected(void)\n{\n    // In drone mode, the game cannot continue once disconnected.\n\n    if (drone)\n    {\n        I_Error(\"Disconnected from server in drone mode.\");\n    }\n\n    // disconnected from server\n\n    printf(\"Disconnected from server.\\n\");\n}\n\n//\n// Invoked by the network engine when a complete set of ticcmds is\n// available.\n//\n\nvoid D_ReceiveTic(ticcmd_t *ticcmds, boolean *players_mask)\n{\n    int i;\n\n    // Disconnected from server?\n\n    if (ticcmds == NULL && players_mask == NULL)\n    {\n        D_Disconnected();\n        return;\n    }\n\n    for (i = 0; i < NET_MAXPLAYERS; ++i)\n    {\n        if (!drone && i == localplayer)\n        {\n            // This is us.  Don't overwrite it.\n        }\n        else\n        {\n            ticdata[recvtic % BACKUPTICS].cmds[i] = ticcmds[i];\n            ticdata[recvtic % BACKUPTICS].ingame[i] = players_mask[i];\n        }\n    }\n\n    ++recvtic;\n}\n\n//\n// Start game loop\n//\n// Called after the screen is set but before the game starts running.\n//\n\nvoid D_StartGameLoop(void)\n{\n    lasttime = GetAdjustedTime() / ticdup;\n}\n\n#if ORIGCODE\n//\n// Block until the game start message is received from the server.\n//\n\nstatic void BlockUntilStart(net_gamesettings_t *settings,\n                            netgame_startup_callback_t callback)\n{\n    while (!NET_CL_GetSettings(settings))\n    {\n        NET_CL_Run();\n        NET_SV_Run();\n\n        if (!net_client_connected)\n        {\n            I_Error(\"Lost connection to server\");\n        }\n\n        if (callback != NULL && !callback(net_client_wait_data.ready_players,\n                                          net_client_wait_data.num_players))\n        {\n            I_Error(\"Netgame startup aborted.\");\n        }\n\n        I_Sleep(100);\n    }\n}\n\n#endif\n\nvoid D_StartNetGame(net_gamesettings_t *settings,\n                    netgame_startup_callback_t callback)\n{\n#if ORIGCODE\n    int i;\n\n    offsetms = 0;\n    recvtic = 0;\n\n    settings->consoleplayer = 0;\n    settings->num_players = 1;\n    settings->player_classes[0] = player_class;\n\n    //!\n    // @category net\n    //\n    // Use new network client sync code rather than the classic\n    // sync code. This is currently disabled by default because it\n    // has some bugs.\n    //\n    if (M_CheckParm(\"-newsync\") > 0)\n        settings->new_sync = 1;\n    else\n        settings->new_sync = 0;\n\n    // TODO: New sync code is not enabled by default because it's\n    // currently broken. \n    //if (M_CheckParm(\"-oldsync\") > 0)\n    //    settings->new_sync = 0;\n    //else\n    //    settings->new_sync = 1;\n\n    //!\n    // @category net\n    // @arg <n>\n    //\n    // Send n extra tics in every packet as insurance against dropped\n    // packets.\n    //\n\n    i = M_CheckParmWithArgs(\"-extratics\", 1);\n\n    if (i > 0)\n        settings->extratics = atoi(myargv[i+1]);\n    else\n        settings->extratics = 1;\n\n    //!\n    // @category net\n    // @arg <n>\n    //\n    // Reduce the resolution of the game by a factor of n, reducing\n    // the amount of network bandwidth needed.\n    //\n\n    i = M_CheckParmWithArgs(\"-dup\", 1);\n\n    if (i > 0)\n        settings->ticdup = atoi(myargv[i+1]);\n    else\n        settings->ticdup = 1;\n\n    if (net_client_connected)\n    {\n        // Send our game settings and block until game start is received\n        // from the server.\n\n        NET_CL_StartGame(settings);\n        BlockUntilStart(settings, callback);\n\n        // Read the game settings that were received.\n\n        NET_CL_GetSettings(settings);\n    }\n\n    if (drone)\n    {\n        settings->consoleplayer = 0;\n    }\n\n    // Set the local player and playeringame[] values.\n\n    localplayer = settings->consoleplayer;\n\n    for (i = 0; i < NET_MAXPLAYERS; ++i)\n    {\n        local_playeringame[i] = i < settings->num_players;\n    }\n\n    // Copy settings to global variables.\n\n    ticdup = settings->ticdup;\n    new_sync = settings->new_sync;\n\n    // TODO: Message disabled until we fix new_sync.\n    //if (!new_sync)\n    //{\n    //    printf(\"Syncing netgames like Vanilla Doom.\\n\");\n    //}\n#else\n    settings->consoleplayer = 0;\n\tsettings->num_players = 1;\n\tsettings->player_classes[0] = player_class;\n\tsettings->new_sync = 0;\n\tsettings->extratics = 1;\n\tsettings->ticdup = 1;\n\n\tticdup = settings->ticdup;\n\tnew_sync = settings->new_sync;\n#endif\n}\n\nboolean D_InitNetGame(net_connect_data_t *connect_data)\n{\n    boolean result = false;\n#ifdef FEATURE_MULTIPLAYER\n    net_addr_t *addr = NULL;\n    int i;\n#endif\n\n    // Call D_QuitNetGame on exit:\n\n    I_AtExit(D_QuitNetGame, true);\n\n    player_class = connect_data->player_class;\n\n#ifdef FEATURE_MULTIPLAYER\n\n    //!\n    // @category net\n    //\n    // Start a multiplayer server, listening for connections.\n    //\n\n    if (M_CheckParm(\"-server\") > 0\n     || M_CheckParm(\"-privateserver\") > 0)\n    {\n        NET_SV_Init();\n        NET_SV_AddModule(&net_loop_server_module);\n        NET_SV_AddModule(&net_sdl_module);\n        NET_SV_RegisterWithMaster();\n\n        net_loop_client_module.InitClient();\n        addr = net_loop_client_module.ResolveAddress(NULL);\n    }\n    else\n    {\n        //!\n        // @category net\n        //\n        // Automatically search the local LAN for a multiplayer\n        // server and join it.\n        //\n\n        i = M_CheckParm(\"-autojoin\");\n\n        if (i > 0)\n        {\n            addr = NET_FindLANServer();\n\n            if (addr == NULL)\n            {\n                I_Error(\"No server found on local LAN\");\n            }\n        }\n\n        //!\n        // @arg <address>\n        // @category net\n        //\n        // Connect to a multiplayer server running on the given\n        // address.\n        //\n\n        i = M_CheckParmWithArgs(\"-connect\", 1);\n\n        if (i > 0)\n        {\n            net_sdl_module.InitClient();\n            addr = net_sdl_module.ResolveAddress(myargv[i+1]);\n\n            if (addr == NULL)\n            {\n                I_Error(\"Unable to resolve '%s'\\n\", myargv[i+1]);\n            }\n        }\n    }\n\n    if (addr != NULL)\n    {\n        if (M_CheckParm(\"-drone\") > 0)\n        {\n            connect_data->drone = true;\n        }\n\n        if (!NET_CL_Connect(addr, connect_data))\n        {\n            I_Error(\"D_InitNetGame: Failed to connect to %s\\n\",\n                    NET_AddrToString(addr));\n        }\n\n        printf(\"D_InitNetGame: Connected to %s\\n\", NET_AddrToString(addr));\n\n        // Wait for launch message received from server.\n\n        NET_WaitForLaunch();\n\n        result = true;\n    }\n#endif\n\n    return result;\n}\n\n\n//\n// D_QuitNetGame\n// Called before quitting to leave a net game\n// without hanging the other players\n//\nvoid D_QuitNetGame (void)\n{\n#ifdef FEATURE_MULTIPLAYER\n    NET_SV_Shutdown();\n    NET_CL_Disconnect();\n#endif\n}\n\nstatic int GetLowTic(void)\n{\n    int lowtic;\n\n    lowtic = maketic;\n\n#ifdef FEATURE_MULTIPLAYER\n    if (net_client_connected)\n    {\n        if (drone || recvtic < lowtic)\n        {\n            lowtic = recvtic;\n        }\n    }\n#endif\n\n    return lowtic;\n}\n\nstatic int frameon;\nstatic int frameskip[4];\nstatic int oldnettics;\n\nstatic void OldNetSync(void)\n{\n    unsigned int i;\n    int keyplayer = -1;\n\n    frameon++;\n\n    // ideally maketic should be 1 - 3 tics above lowtic\n    // if we are consistantly slower, speed up time\n\n    for (i=0 ; i<NET_MAXPLAYERS ; i++)\n    {\n        if (local_playeringame[i])\n        {\n            keyplayer = i;\n            break;\n        }\n    }\n\n    if (keyplayer < 0)\n    {\n        // If there are no players, we can never advance anyway\n\n        return;\n    }\n\n    if (localplayer == keyplayer)\n    {\n        // the key player does not adapt\n    }\n    else\n    {\n        if (maketic <= recvtic)\n        {\n            lasttime--;\n            // printf (\"-\");\n        }\n\n        frameskip[frameon & 3] = oldnettics > recvtic;\n        oldnettics = maketic;\n\n        if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])\n        {\n            skiptics = 1;\n            // printf (\"+\");\n        }\n    }\n}\n\n// Returns true if there are players in the game:\n\nstatic boolean PlayersInGame(void)\n{\n    boolean result = false;\n    unsigned int i;\n\n    // If we are connected to a server, check if there are any players\n    // in the game.\n\n    if (net_client_connected)\n    {\n        for (i = 0; i < NET_MAXPLAYERS; ++i)\n        {\n            result = result || local_playeringame[i];\n        }\n    }\n\n    // Whether single or multi-player, unless we are running as a drone,\n    // we are in the game.\n\n    if (!drone)\n    {\n        result = true;\n    }\n\n    return result;\n}\n\n// When using ticdup, certain values must be cleared out when running\n// the duplicate ticcmds.\n\nstatic void TicdupSquash(ticcmd_set_t *set)\n{\n    ticcmd_t *cmd;\n    unsigned int i;\n\n    for (i = 0; i < NET_MAXPLAYERS ; ++i)\n    {\n        cmd = &set->cmds[i];\n        cmd->chatchar = 0;\n        if (cmd->buttons & BT_SPECIAL)\n            cmd->buttons = 0;\n    }\n}\n\n// When running in single player mode, clear all the ingame[] array\n// except the local player.\n\nstatic void SinglePlayerClear(ticcmd_set_t *set)\n{\n    unsigned int i;\n\n    for (i = 0; i < NET_MAXPLAYERS; ++i)\n    {\n        if (i != localplayer)\n        {\n            set->ingame[i] = false;\n        }\n    }\n}\n\n//\n// TryRunTics\n//\n\nvoid TryRunTics (void)\n{\n    int\ti;\n    int\tlowtic;\n    int\tentertic;\n    static int oldentertics;\n    int realtics;\n    int\tavailabletics;\n    int\tcounts;\n\n    // get real tics\n    entertic = I_GetTime() / ticdup;\n    realtics = entertic - oldentertics;\n    oldentertics = entertic;\n\n    // in singletics mode, run a single tic every time this function\n    // is called.\n\n    if (singletics)\n    {\n        BuildNewTic();\n    }\n    else\n    {\n        NetUpdate ();\n    }\n\n    lowtic = GetLowTic();\n\n    availabletics = lowtic - gametic/ticdup;\n\n    // decide how many tics to run\n\n    if (new_sync)\n    {\n\tcounts = availabletics;\n    }\n    else\n    {\n        // decide how many tics to run\n        if (realtics < availabletics-1)\n            counts = realtics+1;\n        else if (realtics < availabletics)\n            counts = realtics;\n        else\n            counts = availabletics;\n\n        if (counts < 1)\n            counts = 1;\n\n        if (net_client_connected)\n        {\n            OldNetSync();\n        }\n    }\n\n    if (counts < 1)\n\tcounts = 1;\n\n    // wait for new tics if needed\n\n    while (!PlayersInGame() || lowtic < gametic/ticdup + counts)\n    {\n\tNetUpdate ();\n\n        lowtic = GetLowTic();\n\n\tif (lowtic < gametic/ticdup)\n\t    I_Error (\"TryRunTics: lowtic < gametic\");\n\n        // Don't stay in this loop forever.  The menu is still running,\n        // so return to update the screen\n\n\tif (I_GetTime() / ticdup - entertic > 0)\n\t{\n\t    return;\n\t}\n\n        I_Sleep(1);\n    }\n\n    // run the count * ticdup dics\n    while (counts--)\n    {\n        ticcmd_set_t *set;\n\n        if (!PlayersInGame())\n        {\n            return;\n        }\n\n        set = &ticdata[(gametic / ticdup) % BACKUPTICS];\n\n        if (!net_client_connected)\n        {\n            SinglePlayerClear(set);\n        }\n\n\tfor (i=0 ; i<ticdup ; i++)\n\t{\n            if (gametic/ticdup > lowtic)\n                I_Error (\"gametic>lowtic\");\n\n            memcpy(local_playeringame, set->ingame, sizeof(local_playeringame));\n\n            loop_interface->RunTic(set->cmds, set->ingame);\n\t    gametic++;\n\n\t    // modify command for duplicated tics\n\n            TicdupSquash(set);\n\t}\n\n\tNetUpdate ();\t// check for new console commands\n    }\n}\n\nvoid D_RegisterLoopCallbacks(loop_interface_t *i)\n{\n    loop_interface = i;\n}\n"
  },
  {
    "path": "fbdoom/d_loop.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tMain loop stuff.\n//\n\n#ifndef __D_LOOP__\n#define __D_LOOP__\n\n#include \"net_defs.h\"\n\n// Callback function invoked while waiting for the netgame to start.\n// The callback is invoked when new players are ready. The callback\n// should return true, or return false to abort startup.\n\ntypedef boolean (*netgame_startup_callback_t)(int ready_players,\n                                              int num_players);\n\ntypedef struct\n{\n    // Read events from the event queue, and process them.\n\n    void (*ProcessEvents)();\n\n    // Given the current input state, fill in the fields of the specified\n    // ticcmd_t structure with data for a new tic.\n\n    void (*BuildTiccmd)(ticcmd_t *cmd, int maketic);\n\n    // Advance the game forward one tic, using the specified player input.\n\n    void (*RunTic)(ticcmd_t *cmds, boolean *ingame);\n\n    // Run the menu (runs independently of the game).\n\n    void (*RunMenu)();\n} loop_interface_t;\n\n// Register callback functions for the main loop code to use.\nvoid D_RegisterLoopCallbacks(loop_interface_t *i);\n\n// Create any new ticcmds and broadcast to other players.\nvoid NetUpdate (void);\n\n// Broadcasts special packets to other players\n//  to notify of game exit\nvoid D_QuitNetGame (void);\n\n//? how many ticks to run?\nvoid TryRunTics (void);\n\n// Called at start of game loop to initialize timers\nvoid D_StartGameLoop(void);\n\n// Initialize networking code and connect to server.\n\nboolean D_InitNetGame(net_connect_data_t *connect_data);\n\n// Start game with specified settings. The structure will be updated\n// with the actual settings for the game.\n\nvoid D_StartNetGame(net_gamesettings_t *settings,\n                    netgame_startup_callback_t callback);\n\nextern boolean singletics;\nextern int gametic, ticdup;\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/d_main.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tDOOM main program (D_DoomMain) and game loop (D_DoomLoop),\n//\tplus functions to determine game mode (shareware, registered),\n//\tparse command line parameters, configure game parameters (turbo),\n//\tand call the startup functions.\n//\n\n\n#include <ctype.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"config.h\"\n#include \"deh_main.h\"\n#include \"doomdef.h\"\n#include \"doomstat.h\"\n\n#include \"dstrings.h\"\n#include \"doomfeatures.h\"\n#include \"sounds.h\"\n\n#include \"d_iwad.h\"\n\n#include \"z_zone.h\"\n#include \"w_main.h\"\n#include \"w_wad.h\"\n#include \"s_sound.h\"\n#include \"v_video.h\"\n\n#include \"f_finale.h\"\n#include \"f_wipe.h\"\n\n#include \"m_argv.h\"\n#include \"m_config.h\"\n#include \"m_controls.h\"\n#include \"m_misc.h\"\n#include \"m_menu.h\"\n#include \"p_saveg.h\"\n\n#include \"i_endoom.h\"\n#include \"i_joystick.h\"\n#include \"i_system.h\"\n#include \"i_timer.h\"\n#include \"i_video.h\"\n\n#include \"g_game.h\"\n\n#include \"hu_stuff.h\"\n#include \"wi_stuff.h\"\n#include \"st_stuff.h\"\n#include \"am_map.h\"\n#include \"net_client.h\"\n#include \"net_dedicated.h\"\n#include \"net_query.h\"\n\n#include \"p_setup.h\"\n#include \"r_local.h\"\n#include \"statdump.h\"\n\n\n#include \"d_main.h\"\n\n//\n// D-DoomLoop()\n// Not a globally visible function,\n//  just included for source reference,\n//  called by D_DoomMain, never exits.\n// Manages timing and IO,\n//  calls all ?_Responder, ?_Ticker, and ?_Drawer,\n//  calls I_GetTime, I_StartFrame, and I_StartTic\n//\nvoid D_DoomLoop (void);\n\n// Location where savegames are stored\n\nchar *          savegamedir;\n\n// location of IWAD and WAD files\n\nchar *          iwadfile;\n\n\nboolean\t\tdevparm;\t// started game with -devparm\nboolean         nomonsters;\t// checkparm of -nomonsters\nboolean         respawnparm;\t// checkparm of -respawn\nboolean         fastparm;\t// checkparm of -fast\n\n//extern int soundVolume;\n//extern  int\tsfxVolume;\n//extern  int\tmusicVolume;\n\nextern  boolean\tinhelpscreens;\n\nskill_t\t\tstartskill;\nint             startepisode;\nint\t\tstartmap;\nboolean\t\tautostart;\nint             startloadgame;\n\nboolean\t\tadvancedemo;\n\n// Store demo, do not accept any inputs\nboolean         storedemo;\n\n// \"BFG Edition\" version of doom2.wad does not include TITLEPIC.\nboolean         bfgedition;\n\n// If true, the main game loop has started.\nboolean         main_loop_started = false;\n\nchar\t\twadfile[1024];\t\t// primary wad file\nchar\t\tmapdir[1024];           // directory of development maps\n\nint             show_endoom = 1;\n\n\nvoid D_ConnectNetGame(void);\nvoid D_CheckNetGame(void);\n\n\n//\n// D_ProcessEvents\n// Send all the events of the given timestamp down the responder chain\n//\nvoid D_ProcessEvents (void)\n{\n    event_t*\tev;\n\t\n    // IF STORE DEMO, DO NOT ACCEPT INPUT\n    if (storedemo)\n        return;\n\t\n    while ((ev = D_PopEvent()) != NULL)\n    {\n\tif (M_Responder (ev))\n\t    continue;               // menu ate the event\n\tG_Responder (ev);\n    }\n}\n\n\n\n\n//\n// D_Display\n//  draw current display, possibly wiping it from the previous\n//\n\n// wipegamestate can be set to -1 to force a wipe on the next draw\ngamestate_t     wipegamestate = GS_DEMOSCREEN;\nextern  boolean setsizeneeded;\nextern  int             showMessages;\nvoid R_ExecuteSetViewSize (void);\n\nvoid D_Display (void)\n{\n    static  boolean\t\tviewactivestate = false;\n    static  boolean\t\tmenuactivestate = false;\n    static  boolean\t\tinhelpscreensstate = false;\n    static  boolean\t\tfullscreen = false;\n    static  gamestate_t\t\toldgamestate = -1;\n    static  int\t\t\tborderdrawcount;\n    int\t\t\t\tnowtime;\n    int\t\t\t\ttics;\n    int\t\t\t\twipestart;\n    int\t\t\t\ty;\n    boolean\t\t\tdone;\n    boolean\t\t\twipe;\n    boolean\t\t\tredrawsbar;\n\n    if (nodrawers)\n    \treturn;                    // for comparative timing / profiling\n\t\t\n    redrawsbar = false;\n    \n    // change the view size if needed\n    if (setsizeneeded)\n    {\n\t\tR_ExecuteSetViewSize ();\n\t\toldgamestate = -1;                      // force background redraw\n\t\tborderdrawcount = 3;\n    }\n\n    // save the current screen if about to wipe\n    if (gamestate != wipegamestate)\n\t\t{\n\t\twipe = true;\n\t\twipe_StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT);\n    }\n    else\n    \twipe = false;\n\n    if (gamestate == GS_LEVEL && gametic)\n    \tHU_Erase();\n    \n    // do buffered drawing\n    switch (gamestate)\n    {\n      case GS_LEVEL:\n\t\tif (!gametic)\n\t\t\tbreak;\n\t\tif (automapactive)\n\t\t\tAM_Drawer ();\n\t\tif (wipe || (viewheight != 200 && fullscreen) )\n\t\t\tredrawsbar = true;\n\t\tif (inhelpscreensstate && !inhelpscreens)\n\t\t\tredrawsbar = true;              // just put away the help screen\n\t\tST_Drawer (viewheight == 200, redrawsbar );\n\t\tfullscreen = viewheight == 200;\n\t\tbreak;\n\n      case GS_INTERMISSION:\n\t\tWI_Drawer ();\n\t\tbreak;\n\n      case GS_FINALE:\n\t\tF_Drawer ();\n\t\tbreak;\n\n      case GS_DEMOSCREEN:\n\t\tD_PageDrawer ();\n\t\tbreak;\n    }\n    \n    // draw buffered stuff to screen\n    I_UpdateNoBlit ();\n    \n    // draw the view directly\n    if (gamestate == GS_LEVEL && !automapactive && gametic)\n    \tR_RenderPlayerView (&players[displayplayer]);\n\n    if (gamestate == GS_LEVEL && gametic)\n    \tHU_Drawer ();\n    \n    // clean up border stuff\n    if (gamestate != oldgamestate && gamestate != GS_LEVEL)\n    \tI_SetPalette (W_CacheLumpName (DEH_String(\"PLAYPAL\"),PU_CACHE));\n\n    // see if the border needs to be initially drawn\n    if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL)\n    {\n\t\tviewactivestate = false;        // view was not active\n\t\tR_FillBackScreen ();    // draw the pattern into the back screen\n    }\n\n    // see if the border needs to be updated to the screen\n    if (gamestate == GS_LEVEL && !automapactive && scaledviewwidth != 320)\n    {\n\t\tif (menuactive || menuactivestate || !viewactivestate)\n\t\t\tborderdrawcount = 3;\n\t\tif (borderdrawcount)\n\t\t{\n\t\t\tR_DrawViewBorder ();    // erase old menu stuff\n\t\t\tborderdrawcount--;\n\t\t}\n    }\n\n    if (testcontrols)\n    {\n        // Box showing current mouse speed\n\n        V_DrawMouseSpeedBox(testcontrols_mousespeed);\n    }\n\n    menuactivestate = menuactive;\n    viewactivestate = viewactive;\n    inhelpscreensstate = inhelpscreens;\n    oldgamestate = wipegamestate = gamestate;\n    \n    // draw pause pic\n    if (paused)\n    {\n\t\tif (automapactive)\n\t\t\ty = 4;\n\t\telse\n\t\t\ty = viewwindowy+4;\n\t\tV_DrawPatchDirect(viewwindowx + (scaledviewwidth - 68) / 2, y,\n\t\t\t\t\t\t\t  W_CacheLumpName (DEH_String(\"M_PAUSE\"), PU_CACHE));\n    }\n\n\n    // menus go directly to the screen\n    M_Drawer ();          // menu is drawn even on top of everything\n    NetUpdate ();         // send out any new accumulation\n\n\n    // normal update\n    if (!wipe)\n    {\n\tI_FinishUpdate ();              // page flip or blit buffer\n\treturn;\n    }\n    \n    // wipe update\n    wipe_EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT);\n\n    wipestart = I_GetTime () - 1;\n\n    do\n    {\n\tdo\n\t{\n\t    nowtime = I_GetTime ();\n\t    tics = nowtime - wipestart;\n            I_Sleep(1);\n\t} while (tics <= 0);\n        \n\twipestart = nowtime;\n\tdone = wipe_ScreenWipe(wipe_Melt\n\t\t\t       , 0, 0, SCREENWIDTH, SCREENHEIGHT, tics);\n\tI_UpdateNoBlit ();\n\tM_Drawer ();                            // menu is drawn even on top of wipes\n\tI_FinishUpdate ();                      // page flip or blit buffer\n    } while (!done);\n}\n\n//\n// Add configuration file variable bindings.\n//\n\nvoid D_BindVariables(void)\n{\n    int i;\n\n    M_ApplyPlatformDefaults();\n\n    I_BindVideoVariables();\n    I_BindJoystickVariables();\n    I_BindSoundVariables();\n\n    M_BindBaseControls();\n    M_BindWeaponControls();\n    M_BindMapControls();\n    M_BindMenuControls();\n    M_BindChatControls(MAXPLAYERS);\n\n    key_multi_msgplayer[0] = HUSTR_KEYGREEN;\n    key_multi_msgplayer[1] = HUSTR_KEYINDIGO;\n    key_multi_msgplayer[2] = HUSTR_KEYBROWN;\n    key_multi_msgplayer[3] = HUSTR_KEYRED;\n\n#ifdef FEATURE_MULTIPLAYER\n    NET_BindVariables();\n#endif\n\n    M_BindVariable(\"mouse_sensitivity\",      &mouseSensitivity);\n    M_BindVariable(\"sfx_volume\",             &sfxVolume);\n    M_BindVariable(\"music_volume\",           &musicVolume);\n    M_BindVariable(\"show_messages\",          &showMessages);\n    M_BindVariable(\"screenblocks\",           &screenblocks);\n    M_BindVariable(\"detaillevel\",            &detailLevel);\n    M_BindVariable(\"snd_channels\",           &snd_channels);\n    M_BindVariable(\"vanilla_savegame_limit\", &vanilla_savegame_limit);\n    M_BindVariable(\"vanilla_demo_limit\",     &vanilla_demo_limit);\n    M_BindVariable(\"show_endoom\",            &show_endoom);\n\n    // Multiplayer chat macros\n\n    for (i=0; i<10; ++i)\n    {\n        char buf[12];\n\n        M_snprintf(buf, sizeof(buf), \"chatmacro%i\", i);\n        M_BindVariable(buf, &chat_macros[i]);\n    }\n}\n\n//\n// D_GrabMouseCallback\n//\n// Called to determine whether to grab the mouse pointer\n//\n\nboolean D_GrabMouseCallback(void)\n{\n    // Drone players don't need mouse focus\n\n    if (drone)\n        return false;\n\n    // when menu is active or game is paused, release the mouse \n \n    if (menuactive || paused)\n        return false;\n\n    // only grab mouse when playing levels (but not demos)\n\n    return (gamestate == GS_LEVEL) && !demoplayback && !advancedemo;\n}\n\n//\n//  D_DoomLoop\n//\nvoid D_DoomLoop (void)\n{\n    if (bfgedition &&\n        (demorecording || (gameaction == ga_playdemo) || netgame))\n    {\n        printf(\" WARNING: You are playing using one of the Doom Classic\\n\"\n               \" IWAD files shipped with the Doom 3: BFG Edition. These are\\n\"\n               \" known to be incompatible with the regular IWAD files and\\n\"\n               \" may cause demos and network games to get out of sync.\\n\");\n    }\n\n    if (demorecording)\n    \tG_BeginRecording ();\n\n    main_loop_started = true;\n\n    TryRunTics();\n\n    I_SetWindowTitle(gamedescription);\n    I_GraphicsCheckCommandLine();\n    I_SetGrabMouseCallback(D_GrabMouseCallback);\n    I_InitGraphics();\n    I_EnableLoadingDisk();\n\n    V_RestoreBuffer();\n    R_ExecuteSetViewSize();\n\n    D_StartGameLoop();\n\n    if (testcontrols)\n    {\n        wipegamestate = gamestate;\n    }\n\n    while (1)\n    {\n\t\t// frame syncronous IO operations\n\t\tI_StartFrame ();\n\n\t\tTryRunTics (); // will run at least one tic\n\n\t\tS_UpdateSounds (players[consoleplayer].mo);// move positional sounds\n\n\t\t// Update display, next frame, with current state.\n\t\tif (screenvisible)\n\t\t{\n\t\t\tD_Display ();\n\t\t}\n    }\n}\n\n\n\n//\n//  DEMO LOOP\n//\nint             demosequence;\nint             pagetic;\nchar                    *pagename;\n\n\n//\n// D_PageTicker\n// Handles timing for warped projection\n//\nvoid D_PageTicker (void)\n{\n    if (--pagetic < 0)\n\tD_AdvanceDemo ();\n}\n\n\n\n//\n// D_PageDrawer\n//\nvoid D_PageDrawer (void)\n{\n    V_DrawPatch (0, 0, W_CacheLumpName(pagename, PU_CACHE));\n}\n\n\n//\n// D_AdvanceDemo\n// Called after each demo or intro demosequence finishes\n//\nvoid D_AdvanceDemo (void)\n{\n    advancedemo = true;\n}\n\n\n//\n// This cycles through the demo sequences.\n// FIXME - version dependend demo numbers?\n//\nvoid D_DoAdvanceDemo (void)\n{\n    players[consoleplayer].playerstate = PST_LIVE;  // not reborn\n    advancedemo = false;\n    usergame = false;               // no save / end game here\n    paused = false;\n    gameaction = ga_nothing;\n\n    // The Ultimate Doom executable changed the demo sequence to add\n    // a DEMO4 demo.  Final Doom was based on Ultimate, so also\n    // includes this change; however, the Final Doom IWADs do not\n    // include a DEMO4 lump, so the game bombs out with an error\n    // when it reaches this point in the demo sequence.\n\n    // However! There is an alternate version of Final Doom that\n    // includes a fixed executable.\n\n    if (gameversion == exe_ultimate || gameversion == exe_final)\n      demosequence = (demosequence+1)%7;\n    else\n      demosequence = (demosequence+1)%6;\n    \n    switch (demosequence)\n    {\n      case 0:\n\tif ( gamemode == commercial )\n\t    pagetic = TICRATE * 11;\n\telse\n\t    pagetic = 170;\n\tgamestate = GS_DEMOSCREEN;\n\tpagename = DEH_String(\"TITLEPIC\");\n\tif ( gamemode == commercial )\n\t  S_StartMusic(mus_dm2ttl);\n\telse\n\t  S_StartMusic (mus_intro);\n\tbreak;\n      case 1:\n\tG_DeferedPlayDemo(DEH_String(\"demo1\"));\n\tbreak;\n      case 2:\n\tpagetic = 200;\n\tgamestate = GS_DEMOSCREEN;\n\tpagename = DEH_String(\"CREDIT\");\n\tbreak;\n      case 3:\n\tG_DeferedPlayDemo(DEH_String(\"demo2\"));\n\tbreak;\n      case 4:\n\tgamestate = GS_DEMOSCREEN;\n\tif ( gamemode == commercial)\n\t{\n\t    pagetic = TICRATE * 11;\n\t    pagename = DEH_String(\"TITLEPIC\");\n\t    S_StartMusic(mus_dm2ttl);\n\t}\n\telse\n\t{\n\t    pagetic = 200;\n\n\t    if ( gamemode == retail )\n\t      pagename = DEH_String(\"CREDIT\");\n\t    else\n\t      pagename = DEH_String(\"HELP2\");\n\t}\n\tbreak;\n      case 5:\n\tG_DeferedPlayDemo(DEH_String(\"demo3\"));\n\tbreak;\n        // THE DEFINITIVE DOOM Special Edition demo\n      case 6:\n\tG_DeferedPlayDemo(DEH_String(\"demo4\"));\n\tbreak;\n    }\n\n    // The Doom 3: BFG Edition version of doom2.wad does not have a\n    // TITLETPIC lump. Use INTERPIC instead as a workaround.\n    if (bfgedition && !strcasecmp(pagename, \"TITLEPIC\")\n        && W_CheckNumForName(\"titlepic\") < 0)\n    {\n        pagename = DEH_String(\"INTERPIC\");\n    }\n}\n\n\n\n//\n// D_StartTitle\n//\nvoid D_StartTitle (void)\n{\n    gameaction = ga_nothing;\n    demosequence = -1;\n    D_AdvanceDemo ();\n}\n\n// Strings for dehacked replacements of the startup banner\n//\n// These are from the original source: some of them are perhaps\n// not used in any dehacked patches\n\nstatic char *banners[] =\n{\n    // doom2.wad\n    \"                         \"\n    \"DOOM 2: Hell on Earth v%i.%i\"\n    \"                           \",\n    // doom1.wad\n    \"                            \"\n    \"DOOM Shareware Startup v%i.%i\"\n    \"                           \",\n    // doom.wad\n    \"                            \"\n    \"DOOM Registered Startup v%i.%i\"\n    \"                           \",\n    // Registered DOOM uses this\n    \"                          \"\n    \"DOOM System Startup v%i.%i\"\n    \"                          \",\n    // doom.wad (Ultimate DOOM)\n    \"                         \"\n    \"The Ultimate DOOM Startup v%i.%i\"\n    \"                        \",\n    // tnt.wad\n    \"                     \"\n    \"DOOM 2: TNT - Evilution v%i.%i\"\n    \"                           \",\n    // plutonia.wad\n    \"                   \"\n    \"DOOM 2: Plutonia Experiment v%i.%i\"\n    \"                           \",\n};\n\n//\n// Get game name: if the startup banner has been replaced, use that.\n// Otherwise, use the name given\n// \n\nstatic char *GetGameName(char *gamename)\n{\n    size_t i;\n    char *deh_sub;\n    \n    for (i=0; i<arrlen(banners); ++i)\n    {\n        // Has the banner been replaced?\n\n        deh_sub = DEH_String(banners[i]);\n        \n        if (deh_sub != banners[i])\n        {\n            size_t gamename_size;\n            int version;\n\n            // Has been replaced.\n            // We need to expand via printf to include the Doom version number\n            // We also need to cut off spaces to get the basic name\n\n            gamename_size = strlen(deh_sub) + 10;\n            gamename = Z_Malloc(gamename_size, PU_STATIC, 0);\n            version = G_VanillaVersionCode();\n            M_snprintf(gamename, gamename_size, deh_sub,\n                       version / 100, version % 100);\n\n            while (gamename[0] != '\\0' && isspace((int)gamename[0]))\n            {\n                memmove(gamename, gamename + 1, gamename_size - 1);\n            }\n\n            while (gamename[0] != '\\0' && isspace((int)gamename[strlen(gamename)-1]))\n            {\n                gamename[strlen(gamename) - 1] = '\\0';\n            }\n\n            return gamename;\n        }\n    }\n\n    return gamename;\n}\n\nstatic void SetMissionForPackName(char *pack_name)\n{\n    int i;\n    static const struct\n    {\n        char *name;\n        int mission;\n    } packs[] = {\n        { \"doom2\",    doom2 },\n        { \"tnt\",      pack_tnt },\n        { \"plutonia\", pack_plut },\n    };\n\n    for (i = 0; i < arrlen(packs); ++i)\n    {\n        if (!strcasecmp(pack_name, packs[i].name))\n        {\n            gamemission = packs[i].mission;\n            return;\n        }\n    }\n\n    printf(\"Valid mission packs are:\\n\");\n\n    for (i = 0; i < arrlen(packs); ++i)\n    {\n        printf(\"\\t%s\\n\", packs[i].name);\n    }\n\n    I_Error(\"Unknown mission pack name: %s\", pack_name);\n}\n\n//\n// Find out what version of Doom is playing.\n//\n\nvoid D_IdentifyVersion(void)\n{\n    // gamemission is set up by the D_FindIWAD function.  But if \n    // we specify '-iwad', we have to identify using \n    // IdentifyIWADByName.  However, if the iwad does not match\n    // any known IWAD name, we may have a dilemma.  Try to \n    // identify by its contents.\n\n    if (gamemission == none)\n    {\n        unsigned int i;\n\n        for (i=0; i<numlumps; ++i)\n        {\n            if (!strncasecmp(lumpinfo[i].name, \"MAP01\", 8))\n            {\n                gamemission = doom2;\n                break;\n            } \n            else if (!strncasecmp(lumpinfo[i].name, \"E1M1\", 8))\n            {\n                gamemission = doom;\n                break;\n            }\n        }\n\n        if (gamemission == none)\n        {\n            // Still no idea.  I don't think this is going to work.\n\n            I_Error(\"Unknown or invalid IWAD file.\");\n        }\n    }\n\n    // Make sure gamemode is set up correctly\n\n    if (logical_gamemission == doom)\n    {\n        // Doom 1.  But which version?\n\n        if (W_CheckNumForName(\"E4M1\") > 0)\n        {\n            // Ultimate Doom\n\n            gamemode = retail;\n        } \n        else if (W_CheckNumForName(\"E3M1\") > 0)\n        {\n            gamemode = registered;\n        }\n        else\n        {\n            gamemode = shareware;\n        }\n    }\n    else\n    {\n        int p;\n\n        // Doom 2 of some kind.\n        gamemode = commercial;\n\n        // We can manually override the gamemission that we got from the\n        // IWAD detection code. This allows us to eg. play Plutonia 2\n        // with Freedoom and get the right level names.\n\n        //!\n        // @arg <pack>\n        //\n        // Explicitly specify a Doom II \"mission pack\" to run as, instead of\n        // detecting it based on the filename. Valid values are: \"doom2\",\n        // \"tnt\" and \"plutonia\".\n        //\n        p = M_CheckParmWithArgs(\"-pack\", 1);\n        if (p > 0)\n        {\n            SetMissionForPackName(myargv[p + 1]);\n        }\n    }\n}\n\n// Set the gamedescription string\n\nvoid D_SetGameDescription(void)\n{\n    boolean is_freedoom = W_CheckNumForName(\"FREEDOOM\") >= 0,\n            is_freedm = W_CheckNumForName(\"FREEDM\") >= 0;\n\n    gamedescription = \"Unknown\";\n\n    if (logical_gamemission == doom)\n    {\n        // Doom 1.  But which version?\n\n        if (is_freedoom)\n        {\n            gamedescription = GetGameName(\"Freedoom: Phase 1\");\n        }\n        else if (gamemode == retail)\n        {\n            // Ultimate Doom\n\n            gamedescription = GetGameName(\"The Ultimate DOOM\");\n        }\n        else if (gamemode == registered)\n        {\n            gamedescription = GetGameName(\"DOOM Registered\");\n        }\n        else if (gamemode == shareware)\n        {\n            gamedescription = GetGameName(\"DOOM Shareware\");\n        }\n    }\n    else\n    {\n        // Doom 2 of some kind.  But which mission?\n\n        if (is_freedoom)\n        {\n            if (is_freedm)\n            {\n                gamedescription = GetGameName(\"FreeDM\");\n            }\n            else\n            {\n                gamedescription = GetGameName(\"Freedoom: Phase 2\");\n            }\n        }\n        else if (logical_gamemission == doom2)\n        {\n            gamedescription = GetGameName(\"DOOM 2: Hell on Earth\");\n        }\n        else if (logical_gamemission == pack_plut)\n        {\n            gamedescription = GetGameName(\"DOOM 2: Plutonia Experiment\"); \n        }\n        else if (logical_gamemission == pack_tnt)\n        {\n            gamedescription = GetGameName(\"DOOM 2: TNT - Evilution\");\n        }\n    }\n}\n\n//      print title for every printed line\nchar            title[128];\n\nstatic boolean D_AddFile(char *filename)\n{\n    wad_file_t *handle;\n\n    printf(\" adding %s\\n\", filename);\n    handle = W_AddFile(filename);\n\n    return handle != NULL;\n}\n\n// Copyright message banners\n// Some dehacked mods replace these.  These are only displayed if they are \n// replaced by dehacked.\n\nstatic char *copyright_banners[] =\n{\n    \"===========================================================================\\n\"\n    \"ATTENTION:  This version of DOOM has been modified.  If you would like to\\n\"\n    \"get a copy of the original game, call 1-800-IDGAMES or see the readme file.\\n\"\n    \"        You will not receive technical support for modified games.\\n\"\n    \"                      press enter to continue\\n\"\n    \"===========================================================================\\n\",\n\n    \"===========================================================================\\n\"\n    \"                 Commercial product - do not distribute!\\n\"\n    \"         Please report software piracy to the SPA: 1-800-388-PIR8\\n\"\n    \"===========================================================================\\n\",\n\n    \"===========================================================================\\n\"\n    \"                                Shareware!\\n\"\n    \"===========================================================================\\n\"\n};\n\n// Prints a message only if it has been modified by dehacked.\n\nvoid PrintDehackedBanners(void)\n{\n    size_t i;\n\n    for (i=0; i<arrlen(copyright_banners); ++i)\n    {\n        char *deh_s;\n\n        deh_s = DEH_String(copyright_banners[i]);\n\n        if (deh_s != copyright_banners[i])\n        {\n            printf(\"%s\", deh_s);\n\n            // Make sure the modified banner always ends in a newline character.\n            // If it doesn't, add a newline.  This fixes av.wad.\n\n            if (deh_s[strlen(deh_s) - 1] != '\\n')\n            {\n                printf(\"\\n\");\n            }\n        }\n    }\n}\n\nstatic struct \n{\n    char *description;\n    char *cmdline;\n    GameVersion_t version;\n} gameversions[] = {\n    {\"Doom 1.666\",           \"1.666\",      exe_doom_1_666},\n    {\"Doom 1.7/1.7a\",        \"1.7\",        exe_doom_1_7},\n    {\"Doom 1.8\",             \"1.8\",        exe_doom_1_8},\n    {\"Doom 1.9\",             \"1.9\",        exe_doom_1_9},\n    {\"Hacx\",                 \"hacx\",       exe_hacx},\n    {\"Ultimate Doom\",        \"ultimate\",   exe_ultimate},\n    {\"Final Doom\",           \"final\",      exe_final},\n    {\"Final Doom (alt)\",     \"final2\",     exe_final2},\n    {\"Chex Quest\",           \"chex\",       exe_chex},\n    { NULL,                  NULL,         0},\n};\n\n// Initialize the game version\n\nstatic void InitGameVersion(void)\n{\n    int p;\n    int i;\n\n    //! \n    // @arg <version>\n    // @category compat\n    //\n    // Emulate a specific version of Doom.  Valid values are \"1.9\",\n    // \"ultimate\", \"final\", \"final2\", \"hacx\" and \"chex\".\n    //\n\n    p = M_CheckParmWithArgs(\"-gameversion\", 1);\n\n    if (p)\n    {\n        for (i=0; gameversions[i].description != NULL; ++i)\n        {\n            if (!strcmp(myargv[p+1], gameversions[i].cmdline))\n            {\n                gameversion = gameversions[i].version;\n                break;\n            }\n        }\n        \n        if (gameversions[i].description == NULL) \n        {\n            printf(\"Supported game versions:\\n\");\n\n            for (i=0; gameversions[i].description != NULL; ++i)\n            {\n                printf(\"\\t%s (%s)\\n\", gameversions[i].cmdline,\n                        gameversions[i].description);\n            }\n            \n            I_Error(\"Unknown game version '%s'\", myargv[p+1]);\n        }\n    }\n    else\n    {\n        // Determine automatically\n\n        if (gamemission == pack_chex)\n        {\n            // chex.exe - identified by iwad filename\n\n            gameversion = exe_chex;\n        }\n        else if (gamemission == pack_hacx)\n        {\n            // hacx.exe: identified by iwad filename\n\n            gameversion = exe_hacx;\n        }\n        else if (gamemode == shareware || gamemode == registered)\n        {\n            // original\n\n            gameversion = exe_doom_1_9;\n\n            // TODO: Detect IWADs earlier than Doom v1.9.\n        }\n        else if (gamemode == retail)\n        {\n            gameversion = exe_ultimate;\n        }\n        else if (gamemode == commercial)\n        {\n            if (gamemission == doom2)\n            {\n                gameversion = exe_doom_1_9;\n            }\n            else\n            {\n                // Final Doom: tnt or plutonia\n                // Defaults to emulating the first Final Doom executable,\n                // which has the crash in the demo loop; however, having\n                // this as the default should mean that it plays back\n                // most demos correctly.\n\n                gameversion = exe_final;\n            }\n        }\n    }\n    \n    // The original exe does not support retail - 4th episode not supported\n\n    if (gameversion < exe_ultimate && gamemode == retail)\n    {\n        gamemode = registered;\n    }\n\n    // EXEs prior to the Final Doom exes do not support Final Doom.\n\n    if (gameversion < exe_final && gamemode == commercial\n     && (gamemission == pack_tnt || gamemission == pack_plut))\n    {\n        gamemission = doom2;\n    }\n}\n\nvoid PrintGameVersion(void)\n{\n    int i;\n\n    for (i=0; gameversions[i].description != NULL; ++i)\n    {\n        if (gameversions[i].version == gameversion)\n        {\n            printf(\"Emulating the behavior of the \"\n                   \"'%s' executable.\\n\", gameversions[i].description);\n            break;\n        }\n    }\n}\n\n// Function called at exit to display the ENDOOM screen\n\nstatic void D_Endoom(void)\n{\n    byte *endoom;\n\n    // Don't show ENDOOM if we have it disabled, or we're running\n    // in screensaver or control test mode. Only show it once the\n    // game has actually started.\n\n    if (!show_endoom || !main_loop_started\n     || screensaver_mode || M_CheckParm(\"-testcontrols\") > 0)\n    {\n        return;\n    }\n\n    endoom = W_CacheLumpName(DEH_String(\"ENDOOM\"), PU_STATIC);\n\n    I_Endoom(endoom);\n}\n\n#if ORIGCODE\n// Load dehacked patches needed for certain IWADs.\nstatic void LoadIwadDeh(void)\n{\n    // The Freedoom IWADs have DEHACKED lumps that must be loaded.\n    if (W_CheckNumForName(\"FREEDOOM\") >= 0)\n    {\n        // Old versions of Freedoom (before 2014-09) did not have technically\n        // valid DEHACKED lumps, so ignore errors and just continue if this\n        // is an old IWAD.\n        DEH_LoadLumpByName(\"DEHACKED\", false, true);\n    }\n\n    // If this is the HACX IWAD, we need to load the DEHACKED lump.\n    if (gameversion == exe_hacx)\n    {\n        if (!DEH_LoadLumpByName(\"DEHACKED\", true, false))\n        {\n            I_Error(\"DEHACKED lump not found.  Please check that this is the \"\n                    \"Hacx v1.2 IWAD.\");\n        }\n    }\n\n    // Chex Quest needs a separate Dehacked patch which must be downloaded\n    // and installed next to the IWAD.\n    if (gameversion == exe_chex)\n    {\n        char *chex_deh = NULL;\n        char *sep;\n\n        // Look for chex.deh in the same directory as the IWAD file.\n        sep = strrchr(iwadfile, DIR_SEPARATOR);\n\n        if (sep != NULL)\n        {\n            size_t chex_deh_len = strlen(iwadfile) + 9;\n            chex_deh = malloc(chex_deh_len);\n            M_StringCopy(chex_deh, iwadfile, chex_deh_len);\n            chex_deh[sep - iwadfile + 1] = '\\0';\n            M_StringConcat(chex_deh, \"chex.deh\", chex_deh_len);\n        }\n        else\n        {\n            chex_deh = strdup(\"chex.deh\");\n        }\n\n        // If the dehacked patch isn't found, try searching the WAD\n        // search path instead.  We might find it...\n        if (!M_FileExists(chex_deh))\n        {\n            free(chex_deh);\n            chex_deh = D_FindWADByName(\"chex.deh\");\n        }\n\n        // Still not found?\n        if (chex_deh == NULL)\n        {\n            I_Error(\"Unable to find Chex Quest dehacked file (chex.deh).\\n\"\n                    \"The dehacked file is required in order to emulate\\n\"\n                    \"chex.exe correctly.  It can be found in your nearest\\n\"\n                    \"/idgames repository mirror at:\\n\\n\"\n                    \"   utils/exe_edit/patches/chexdeh.zip\");\n        }\n\n        if (!DEH_LoadFile(chex_deh))\n        {\n            I_Error(\"Failed to load chex.deh needed for emulating chex.exe.\");\n        }\n    }\n}\n#endif\n\n//\n// D_DoomMain\n//\nvoid D_DoomMain (void)\n{\n    int p;\n    char file[256];\n    char demolumpname[9];\n#if ORIGCODE\n    int numiwadlumps;\n#endif\n\n    I_AtExit(D_Endoom, false);\n\n    // print banner\n\n    I_PrintBanner(PACKAGE_STRING);\n\n    DEH_printf(\"Z_Init: Init zone memory allocation daemon. \\n\");\n    Z_Init ();\n\n#ifdef FEATURE_MULTIPLAYER\n    //!\n    // @category net\n    //\n    // Start a dedicated server, routing packets but not participating\n    // in the game itself.\n    //\n\n    if (M_CheckParm(\"-dedicated\") > 0)\n    {\n        printf(\"Dedicated server mode.\\n\");\n        NET_DedicatedServer();\n\n        // Never returns\n    }\n\n    //!\n    // @category net\n    //\n    // Query the Internet master server for a global list of active\n    // servers.\n    //\n\n    if (M_CheckParm(\"-search\"))\n    {\n        NET_MasterQuery();\n        exit(0);\n    }\n\n    //!\n    // @arg <address>\n    // @category net\n    //\n    // Query the status of the server running on the given IP\n    // address.\n    //\n\n    p = M_CheckParmWithArgs(\"-query\", 1);\n\n    if (p)\n    {\n        NET_QueryAddress(myargv[p+1]);\n        exit(0);\n    }\n\n    //!\n    // @category net\n    //\n    // Search the local LAN for running servers.\n    //\n\n    if (M_CheckParm(\"-localsearch\"))\n    {\n        NET_LANQuery();\n        exit(0);\n    }\n\n#endif\n\n    //!\n    // @vanilla\n    //\n    // Disable monsters.\n    //\n\t\n    nomonsters = M_CheckParm (\"-nomonsters\");\n\n    //!\n    // @vanilla\n    //\n    // Monsters respawn after being killed.\n    //\n\n    respawnparm = M_CheckParm (\"-respawn\");\n\n    //!\n    // @vanilla\n    //\n    // Monsters move faster.\n    //\n\n    fastparm = M_CheckParm (\"-fast\");\n\n    //! \n    // @vanilla\n    //\n    // Developer mode.  F1 saves a screenshot in the current working\n    // directory.\n    //\n\n    devparm = M_CheckParm (\"-devparm\");\n\n    I_DisplayFPSDots(devparm);\n\n    //!\n    // @category net\n    // @vanilla\n    //\n    // Start a deathmatch game.\n    //\n\n    if (M_CheckParm (\"-deathmatch\"))\n\tdeathmatch = 1;\n\n    //!\n    // @category net\n    // @vanilla\n    //\n    // Start a deathmatch 2.0 game.  Weapons do not stay in place and\n    // all items respawn after 30 seconds.\n    //\n\n    if (M_CheckParm (\"-altdeath\"))\n\tdeathmatch = 2;\n\n    if (devparm)\n\tDEH_printf(D_DEVSTR);\n    \n    // find which dir to use for config files\n\n#ifdef _WIN32\n\n    //!\n    // @platform windows\n    // @vanilla\n    //\n    // Save configuration data and savegames in c:\\doomdata,\n    // allowing play from CD.\n    //\n\n    if (M_ParmExists(\"-cdrom\"))\n    {\n        printf(D_CDROM);\n\n        M_SetConfigDir(\"c:\\\\doomdata\\\\\");\n    }\n    else\n#endif\n    {\n        // Auto-detect the configuration dir.\n\n        M_SetConfigDir(NULL);\n    }\n\n    //!\n    // @arg <x>\n    // @vanilla\n    //\n    // Turbo mode.  The player's speed is multiplied by x%.  If unspecified,\n    // x defaults to 200.  Values are rounded up to 10 and down to 400.\n    //\n\n    if ( (p=M_CheckParm (\"-turbo\")) )\n    {\n\tint     scale = 200;\n\textern int forwardmove[2];\n\textern int sidemove[2];\n\t\n\tif (p<myargc-1)\n\t    scale = atoi (myargv[p+1]);\n\tif (scale < 10)\n\t    scale = 10;\n\tif (scale > 400)\n\t    scale = 400;\n        DEH_printf(\"turbo scale: %i%%\\n\", scale);\n\tforwardmove[0] = forwardmove[0]*scale/100;\n\tforwardmove[1] = forwardmove[1]*scale/100;\n\tsidemove[0] = sidemove[0]*scale/100;\n\tsidemove[1] = sidemove[1]*scale/100;\n    }\n    \n    // init subsystems\n    DEH_printf(\"V_Init: allocate screens.\\n\");\n    V_Init ();\n\n    // Load configuration files before initialising other subsystems.\n    DEH_printf(\"M_LoadDefaults: Load system defaults.\\n\");\n    M_SetConfigFilenames(\"default.cfg\", PROGRAM_PREFIX \"doom.cfg\");\n    D_BindVariables();\n    M_LoadDefaults();\n\n    // Save configuration at exit.\n    I_AtExit(M_SaveDefaults, false);\n\n    // Find main IWAD file and load it.\n    iwadfile = D_FindIWAD(IWAD_MASK_DOOM, &gamemission);\n\n    // None found?\n\n    if (iwadfile == NULL)\n    {\n        I_Error(\"Game mode indeterminate.  No IWAD file was found.  Try\\n\"\n                \"specifying one with the '-iwad' command line parameter.\\n\");\n    }\n\n    modifiedgame = false;\n\n    DEH_printf(\"W_Init: Init WADfiles.\\n\");\n    D_AddFile(iwadfile);\n#if ORIGCODE\n    numiwadlumps = numlumps;\n#endif\n\n    W_CheckCorrectIWAD(doom);\n\n    // Now that we've loaded the IWAD, we can figure out what gamemission\n    // we're playing and which version of Vanilla Doom we need to emulate.\n    D_IdentifyVersion();\n    InitGameVersion();\n\n#if ORIGCODE\n    //!\n    // @category mod\n    //\n    // Disable automatic loading of Dehacked patches for certain\n    // IWAD files.\n    //\n    if (!M_ParmExists(\"-nodeh\"))\n    {\n        // Some IWADs have dehacked patches that need to be loaded for\n        // them to be played properly.\n        LoadIwadDeh();\n    }\n#endif\n\n    // Doom 3: BFG Edition includes modified versions of the classic\n    // IWADs which can be identified by an additional DMENUPIC lump.\n    // Furthermore, the M_GDHIGH lumps have been modified in a way that\n    // makes them incompatible to Vanilla Doom and the modified version\n    // of doom2.wad is missing the TITLEPIC lump.\n    // We specifically check for DMENUPIC here, before PWADs have been\n    // loaded which could probably include a lump of that name.\n\n    if (W_CheckNumForName(\"dmenupic\") >= 0)\n    {\n        printf(\"BFG Edition: Using workarounds as needed.\\n\");\n        bfgedition = true;\n\n        // BFG Edition changes the names of the secret levels to\n        // censor the Wolfenstein references. It also has an extra\n        // secret level (MAP33). In Vanilla Doom (meaning the DOS\n        // version), MAP33 overflows into the Plutonia level names\n        // array, so HUSTR_33 is actually PHUSTR_1.\n\n        DEH_AddStringReplacement(HUSTR_31, \"level 31: idkfa\");\n        DEH_AddStringReplacement(HUSTR_32, \"level 32: keen\");\n        DEH_AddStringReplacement(PHUSTR_1, \"level 33: betray\");\n\n        // The BFG edition doesn't have the \"low detail\" menu option (fair\n        // enough). But bizarrely, it reuses the M_GDHIGH patch as a label\n        // for the options menu (says \"Fullscreen:\"). Why the perpetrators\n        // couldn't just add a new graphic lump and had to reuse this one,\n        // I don't know.\n        //\n        // The end result is that M_GDHIGH is too wide and causes the game\n        // to crash. As a workaround to get a minimum level of support for\n        // the BFG edition IWADs, use the \"ON\"/\"OFF\" graphics instead.\n\n        DEH_AddStringReplacement(\"M_GDHIGH\", \"M_MSGON\");\n        DEH_AddStringReplacement(\"M_GDLOW\", \"M_MSGOFF\");\n    }\n\n#ifdef FEATURE_DEHACKED\n    // Load Dehacked patches specified on the command line with -deh.\n    // Note that there's a very careful and deliberate ordering to how\n    // Dehacked patches are loaded. The order we use is:\n    //  1. IWAD dehacked patches.\n    //  2. Command line dehacked patches specified with -deh.\n    //  3. PWAD dehacked patches in DEHACKED lumps.\n    DEH_ParseCommandLine();\n#endif\n\n    // Load PWAD files.\n    modifiedgame = W_ParseCommandLine();\n\n    // Debug:\n//    W_PrintDirectory();\n\n    //!\n    // @arg <demo>\n    // @category demo\n    // @vanilla\n    //\n    // Play back the demo named demo.lmp.\n    //\n\n    p = M_CheckParmWithArgs (\"-playdemo\", 1);\n\n    if (!p)\n    {\n        //!\n        // @arg <demo>\n        // @category demo\n        // @vanilla\n        //\n        // Play back the demo named demo.lmp, determining the framerate\n        // of the screen.\n        //\n\tp = M_CheckParmWithArgs(\"-timedemo\", 1);\n\n    }\n\n    if (p)\n    {\n        // With Vanilla you have to specify the file without extension,\n        // but make that optional.\n        if (M_StringEndsWith(myargv[p + 1], \".lmp\"))\n        {\n            M_StringCopy(file, myargv[p + 1], sizeof(file));\n        }\n        else\n        {\n            DEH_snprintf(file, sizeof(file), \"%s.lmp\", myargv[p+1]);\n        }\n\n        if (D_AddFile(file))\n        {\n            M_StringCopy(demolumpname, lumpinfo[numlumps - 1].name,\n                         sizeof(demolumpname));\n        }\n        else\n        {\n            // If file failed to load, still continue trying to play\n            // the demo in the same way as Vanilla Doom.  This makes\n            // tricks like \"-playdemo demo1\" possible.\n\n            M_StringCopy(demolumpname, myargv[p + 1], sizeof(demolumpname));\n        }\n\n        printf(\"Playing demo %s.\\n\", file);\n    }\n\n    I_AtExit((atexit_func_t) G_CheckDemoStatus, true);\n\n    // Generate the WAD hash table.  Speed things up a bit.\n    W_GenerateHashTable();\n\n    // Load DEHACKED lumps from WAD files - but only if we give the right\n    // command line parameter.\n\n#if ORIGCODE\n    //!\n    // @category mod\n    //\n    // Load Dehacked patches from DEHACKED lumps contained in one of the\n    // loaded PWAD files.\n    //\n    if (M_ParmExists(\"-dehlump\"))\n    {\n        int i, loaded = 0;\n\n        for (i = numiwadlumps; i < numlumps; ++i)\n        {\n            if (!strncmp(lumpinfo[i].name, \"DEHACKED\", 8))\n            {\n                DEH_LoadLump(i, false, false);\n                loaded++;\n            }\n        }\n\n        printf(\"  loaded %i DEHACKED lumps from PWAD files.\\n\", loaded);\n    }\n#endif\n\n    // Set the gamedescription string. This is only possible now that\n    // we've finished loading Dehacked patches.\n    D_SetGameDescription();\n\n#ifdef _WIN32\n    // In -cdrom mode, we write savegames to c:\\doomdata as well as configs.\n    if (M_ParmExists(\"-cdrom\"))\n    {\n        savegamedir = configdir;\n    }\n    else\n#endif\n    {\n        savegamedir = M_GetSaveGameDir(D_SaveGameIWADName(gamemission));\n    }\n\n    // Check for -file in shareware\n    if (modifiedgame)\n    {\n\t// These are the lumps that will be checked in IWAD,\n\t// if any one is not present, execution will be aborted.\n\tchar name[23][8]=\n\t{\n\t    \"e2m1\",\"e2m2\",\"e2m3\",\"e2m4\",\"e2m5\",\"e2m6\",\"e2m7\",\"e2m8\",\"e2m9\",\n\t    \"e3m1\",\"e3m3\",\"e3m3\",\"e3m4\",\"e3m5\",\"e3m6\",\"e3m7\",\"e3m8\",\"e3m9\",\n\t    \"dphoof\",\"bfgga0\",\"heada1\",\"cybra1\",\"spida1d1\"\n\t};\n\tint i;\n\t\n\tif ( gamemode == shareware)\n\t    I_Error(DEH_String(\"\\nYou cannot -file with the shareware \"\n\t\t\t       \"version. Register!\"));\n\n\t// Check for fake IWAD with right name,\n\t// but w/o all the lumps of the registered version. \n\tif (gamemode == registered)\n\t    for (i = 0;i < 23; i++)\n\t\tif (W_CheckNumForName(name[i])<0)\n\t\t    I_Error(DEH_String(\"\\nThis is not the registered version.\"));\n    }\n\n    if (W_CheckNumForName(\"SS_START\") >= 0\n     || W_CheckNumForName(\"FF_END\") >= 0)\n    {\n        I_PrintDivider();\n        printf(\" WARNING: The loaded WAD file contains modified sprites or\\n\"\n               \" floor textures.  You may want to use the '-merge' command\\n\"\n               \" line option instead of '-file'.\\n\");\n    }\n\n    I_PrintStartupBanner(gamedescription);\n    PrintDehackedBanners();\n\n    // Freedoom's IWADs are Boom-compatible, which means they usually\n    // don't work in Vanilla (though FreeDM is okay). Show a warning\n    // message and give a link to the website.\n    if (W_CheckNumForName(\"FREEDOOM\") >= 0 && W_CheckNumForName(\"FREEDM\") < 0)\n    {\n        printf(\" WARNING: You are playing using one of the Freedoom IWAD\\n\"\n               \" files, which might not work in this port. See this page\\n\"\n               \" for more information on how to play using Freedoom:\\n\"\n               \"   http://www.chocolate-doom.org/wiki/index.php/Freedoom\\n\");\n        I_PrintDivider();\n    }\n\n    DEH_printf(\"I_Init: Setting up machine state.\\n\");\n    I_CheckIsScreensaver();\n    I_InitTimer();\n    I_InitJoystick();\n    I_InitSound(true);\n    I_InitMusic();\n\n#ifdef FEATURE_MULTIPLAYER\n    printf (\"NET_Init: Init network subsystem.\\n\");\n    NET_Init ();\n#endif\n\n    // Initial netgame startup. Connect to server etc.\n    D_ConnectNetGame();\n\n    // get skill / episode / map from parms\n    startskill = sk_medium;\n    startepisode = 1;\n    startmap = 1;\n    autostart = false;\n\n    //!\n    // @arg <skill>\n    // @vanilla\n    //\n    // Set the game skill, 1-5 (1: easiest, 5: hardest).  A skill of\n    // 0 disables all monsters.\n    //\n\n    p = M_CheckParmWithArgs(\"-skill\", 1);\n\n    if (p)\n    {\n\tstartskill = myargv[p+1][0]-'1';\n\tautostart = true;\n    }\n\n    //!\n    // @arg <n>\n    // @vanilla\n    //\n    // Start playing on episode n (1-4)\n    //\n\n    p = M_CheckParmWithArgs(\"-episode\", 1);\n\n    if (p)\n    {\n\tstartepisode = myargv[p+1][0]-'0';\n\tstartmap = 1;\n\tautostart = true;\n    }\n\t\n    timelimit = 0;\n\n    //! \n    // @arg <n>\n    // @category net\n    // @vanilla\n    //\n    // For multiplayer games: exit each level after n minutes.\n    //\n\n    p = M_CheckParmWithArgs(\"-timer\", 1);\n\n    if (p)\n    {\n\ttimelimit = atoi(myargv[p+1]);\n    }\n\n    //!\n    // @category net\n    // @vanilla\n    //\n    // Austin Virtual Gaming: end levels after 20 minutes.\n    //\n\n    p = M_CheckParm (\"-avg\");\n\n    if (p)\n    {\n\ttimelimit = 20;\n    }\n\n    //!\n    // @arg [<x> <y> | <xy>]\n    // @vanilla\n    //\n    // Start a game immediately, warping to ExMy (Doom 1) or MAPxy\n    // (Doom 2)\n    //\n\n    p = M_CheckParmWithArgs(\"-warp\", 1);\n\n    if (p)\n    {\n        if (gamemode == commercial)\n            startmap = atoi (myargv[p+1]);\n        else\n        {\n            startepisode = myargv[p+1][0]-'0';\n\n            if (p + 2 < myargc)\n            {\n                startmap = myargv[p+2][0]-'0';\n            }\n            else\n            {\n                startmap = 1;\n            }\n        }\n        autostart = true;\n    }\n\n    // Undocumented:\n    // Invoked by setup to test the controls.\n\n    p = M_CheckParm(\"-testcontrols\");\n\n    if (p > 0)\n    {\n        startepisode = 1;\n        startmap = 1;\n        autostart = true;\n        testcontrols = true;\n    }\n\n    // Check for load game parameter\n    // We do this here and save the slot number, so that the network code\n    // can override it or send the load slot to other players.\n\n    //!\n    // @arg <s>\n    // @vanilla\n    //\n    // Load the game in slot s.\n    //\n\n    p = M_CheckParmWithArgs(\"-loadgame\", 1);\n    \n    if (p)\n    {\n        startloadgame = atoi(myargv[p+1]);\n    }\n    else\n    {\n        // Not loading a game\n        startloadgame = -1;\n    }\n\n    DEH_printf(\"M_Init: Init miscellaneous info.\\n\");\n    M_Init ();\n\n    DEH_printf(\"R_Init: Init DOOM refresh daemon - \");\n    R_Init ();\n\n    DEH_printf(\"\\nP_Init: Init Playloop state.\\n\");\n    P_Init ();\n\n    DEH_printf(\"S_Init: Setting up sound.\\n\");\n    S_Init (sfxVolume * 8, musicVolume * 8);\n\n    DEH_printf(\"D_CheckNetGame: Checking network game status.\\n\");\n    D_CheckNetGame ();\n\n    PrintGameVersion();\n\n    DEH_printf(\"HU_Init: Setting up heads up display.\\n\");\n    HU_Init ();\n\n    DEH_printf(\"ST_Init: Init status bar.\\n\");\n    ST_Init ();\n\n    // If Doom II without a MAP01 lump, this is a store demo.\n    // Moved this here so that MAP01 isn't constantly looked up\n    // in the main loop.\n\n    if (gamemode == commercial && W_CheckNumForName(\"map01\") < 0)\n        storedemo = true;\n\n    if (M_CheckParmWithArgs(\"-statdump\", 1))\n    {\n        I_AtExit(StatDump, true);\n        DEH_printf(\"External statistics registered.\\n\");\n    }\n\n    //!\n    // @arg <x>\n    // @category demo\n    // @vanilla\n    //\n    // Record a demo named x.lmp.\n    //\n\n    p = M_CheckParmWithArgs(\"-record\", 1);\n\n    if (p)\n    {\n\t\tG_RecordDemo (myargv[p+1]);\n\t\tautostart = true;\n    }\n\n    p = M_CheckParmWithArgs(\"-playdemo\", 1);\n    if (p)\n    {\n\t\tsingledemo = true;              // quit after one demo\n\t\tG_DeferedPlayDemo (demolumpname);\n\t\tD_DoomLoop ();  // never returns\n    }\n\n    p = M_CheckParmWithArgs(\"-timedemo\", 1);\n    if (p)\n    {\n\t\tG_TimeDemo (demolumpname);\n\t\tD_DoomLoop ();  // never returns\n    }\n\n    if (startloadgame >= 0)\n    {\n        M_StringCopy(file, P_SaveGameFile(startloadgame), sizeof(file));\n        G_LoadGame(file);\n    }\n\n    if (gameaction != ga_loadgame )\n    {\n\t\tif (autostart || netgame)\n\t\t\tG_InitNew (startskill, startepisode, startmap);\n\t\telse\n\t\t\tD_StartTitle ();                // start up intro loop\n    }\n\n    D_DoomLoop ();  // never returns\n}\n\n"
  },
  {
    "path": "fbdoom/d_main.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tSystem specific interface stuff.\n//\n\n\n#ifndef __D_MAIN__\n#define __D_MAIN__\n\n#include \"doomdef.h\"\n\n\n\n\n// Read events from all input devices\n\nvoid D_ProcessEvents (void); \n\t\n\n//\n// BASE LEVEL\n//\nvoid D_PageTicker (void);\nvoid D_PageDrawer (void);\nvoid D_AdvanceDemo (void);\nvoid D_DoAdvanceDemo (void);\nvoid D_StartTitle (void);\n \n//\n// GLOBAL VARIABLES\n//\n\nextern  gameaction_t    gameaction;\n\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/d_mode.c",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n//\n// DESCRIPTION:\n//   Functions and definitions relating to the game type and operational\n//   mode.\n//\n\n#include \"doomtype.h\"\n#include \"d_mode.h\"\n\n// Valid game mode/mission combinations, with the number of\n// episodes/maps for each.\n\nstatic struct\n{\n    GameMission_t mission;\n    GameMode_t mode;\n    int episode;\n    int map;\n} valid_modes[] = {\n    { pack_chex, shareware,  1, 5 },\n    { doom,      shareware,  1, 9 },\n    { doom,      registered, 3, 9 },\n    { doom,      retail,     4, 9 },\n    { doom2,     commercial, 1, 32 },\n    { pack_tnt,  commercial, 1, 32 },\n    { pack_plut, commercial, 1, 32 },\n    { pack_hacx, commercial, 1, 32 },\n    { heretic,   shareware,  1, 9 },\n    { heretic,   registered, 3, 9 },\n    { heretic,   retail,     5, 9 },\n    { hexen,     commercial, 1, 60 },\n    { strife,    commercial, 1, 34 },\n};\n\n// Check that a gamemode+gamemission received over the network is valid.\n\nboolean D_ValidGameMode(GameMission_t mission, GameMode_t mode)\n{\n    int i;\n\n    for (i=0; i<arrlen(valid_modes); ++i)\n    {\n        if (valid_modes[i].mode == mode && valid_modes[i].mission == mission)\n        {\n            return true;\n        }\n    }\n\n    return false;\n}\n\nboolean D_ValidEpisodeMap(GameMission_t mission, GameMode_t mode,\n                          int episode, int map)\n{\n    int i;\n\n    // Hacks for Heretic secret episodes\n\n    if (mission == heretic)\n    {\n        if (mode == retail && episode == 6)\n        {\n            return map >= 1 && map <= 3;\n        }\n        else if (mode == registered && episode == 4)\n        {\n            return map == 1;\n        }\n    }\n\n    // Find the table entry for this mission/mode combination.\n\n    for (i=0; i<arrlen(valid_modes); ++i) \n    {\n        if (mission == valid_modes[i].mission\n         && mode == valid_modes[i].mode)\n        {\n            return episode >= 1 && episode <= valid_modes[i].episode\n                && map >= 1 && map <= valid_modes[i].map;\n        }\n    }\n\n    // Unknown mode/mission combination\n\n    return false;\n}\n\n// Get the number of valid episodes for the specified mission/mode.\n\nint D_GetNumEpisodes(GameMission_t mission, GameMode_t mode)\n{\n    int episode;\n\n    episode = 1;\n\n    while (D_ValidEpisodeMap(mission, mode, episode, 1))\n    {\n        ++episode;\n    }\n\n    return episode - 1;\n}\n\n// Table of valid versions\n\nstatic struct {\n    GameMission_t mission;\n    GameVersion_t version;\n} valid_versions[] = {\n    { doom,     exe_doom_1_9 },\n    { doom,     exe_hacx },\n    { doom,     exe_ultimate },\n    { doom,     exe_final },\n    { doom,     exe_final2 },\n    { doom,     exe_chex },\n    { heretic,  exe_heretic_1_3 },\n    { hexen,    exe_hexen_1_1 },\n    { strife,   exe_strife_1_2 },\n    { strife,   exe_strife_1_31 },\n};\n\nboolean D_ValidGameVersion(GameMission_t mission, GameVersion_t version)\n{\n    int i;\n\n    // All Doom variants can use the Doom versions.\n\n    if (mission == doom2 || mission == pack_plut || mission == pack_tnt\n     || mission == pack_hacx || mission == pack_chex)\n    {\n        mission = doom;\n    }\n\n    for (i=0; i<arrlen(valid_versions); ++i) \n    {\n        if (valid_versions[i].mission == mission \n         && valid_versions[i].version == version)\n        {\n            return true;\n        }\n    }\n\n    return false;\n}\n\n// Does this mission type use ExMy form, rather than MAPxy form?\n\nboolean D_IsEpisodeMap(GameMission_t mission)\n{\n    switch (mission)\n    {\n        case doom:\n        case heretic:\n        case pack_chex:\n            return true;\n\n        case none:\n        case hexen:\n        case doom2:\n        case pack_hacx:\n        case pack_tnt:\n        case pack_plut:\n        case strife:\n        default:\n            return false;\n    }\n}\n\nchar *D_GameMissionString(GameMission_t mission)\n{\n    switch (mission)\n    {\n        case none:\n        default:\n            return \"none\";\n        case doom:\n            return \"doom\";\n        case doom2:\n            return \"doom2\";\n        case pack_tnt:\n            return \"tnt\";\n        case pack_plut:\n            return \"plutonia\";\n        case pack_hacx:\n            return \"hacx\";\n        case pack_chex:\n            return \"chex\";\n        case heretic:\n            return \"heretic\";\n        case hexen:\n            return \"hexen\";\n        case strife:\n            return \"strife\";\n    }\n}\n\n"
  },
  {
    "path": "fbdoom/d_mode.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//   Functions and definitions relating to the game type and operational\n//   mode.\n//\n\n#ifndef __D_MODE__\n#define __D_MODE__\n\n#include \"doomtype.h\"\n\n// The \"mission\" controls what game we are playing.\n\ntypedef enum\n{\n    doom,            // Doom 1\n    doom2,           // Doom 2\n    pack_tnt,        // Final Doom: TNT: Evilution\n    pack_plut,       // Final Doom: The Plutonia Experiment\n    pack_chex,       // Chex Quest (modded doom)\n    pack_hacx,       // Hacx (modded doom2)\n    heretic,         // Heretic\n    hexen,           // Hexen\n    strife,          // Strife\n\n    none\n} GameMission_t;\n\n// The \"mode\" allows more accurate specification of the game mode we are\n// in: eg. shareware vs. registered.  So doom1.wad and doom.wad are the\n// same mission, but a different mode.\n\ntypedef enum\n{\n    shareware,       // Doom/Heretic shareware\n    registered,      // Doom/Heretic registered\n    commercial,      // Doom II/Hexen\n    retail,          // Ultimate Doom\n    indetermined     // Unknown.\n} GameMode_t;\n\n// What version are we emulating?\n\ntypedef enum\n{\n    exe_doom_1_2,    // Doom 1.2: shareware and registered\n    exe_doom_1_666,  // Doom 1.666: for shareware, registered and commercial\n    exe_doom_1_7,    // Doom 1.7/1.7a: \"\n    exe_doom_1_8,    // Doom 1.8: \"\n    exe_doom_1_9,    // Doom 1.9: \"\n    exe_hacx,        // Hacx\n    exe_ultimate,    // Ultimate Doom (retail)\n    exe_final,       // Final Doom\n    exe_final2,      // Final Doom (alternate exe)\n    exe_chex,        // Chex Quest executable (based on Final Doom)\n\n    exe_heretic_1_3, // Heretic 1.3\n\n    exe_hexen_1_1,   // Hexen 1.1\n    exe_strife_1_2,  // Strife v1.2\n    exe_strife_1_31  // Strife v1.31\n} GameVersion_t;\n\n// Skill level.\n\ntypedef enum\n{\n    sk_noitems = -1,        // the \"-skill 0\" hack\n    sk_baby = 0,\n    sk_easy,\n    sk_medium,\n    sk_hard,\n    sk_nightmare\n} skill_t;\n\nboolean D_ValidGameMode(GameMission_t mission, GameMode_t mode);\nboolean D_ValidGameVersion(GameMission_t mission, GameVersion_t version);\nboolean D_ValidEpisodeMap(GameMission_t mission, GameMode_t mode,\n                          int episode, int map);\nint D_GetNumEpisodes(GameMission_t mission, GameMode_t mode);\nboolean D_IsEpisodeMap(GameMission_t mission);\nchar *D_GameMissionString(GameMission_t mission);\n\n#endif /* #ifndef __D_MODE__ */\n\n"
  },
  {
    "path": "fbdoom/d_net.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tDOOM Network game communication and protocol,\n//\tall OS independend parts.\n//\n\n#include <stdlib.h>\n\n#include \"doomfeatures.h\"\n\n#include \"d_main.h\"\n#include \"m_argv.h\"\n#include \"m_menu.h\"\n#include \"m_misc.h\"\n#include \"i_system.h\"\n#include \"i_timer.h\"\n#include \"i_video.h\"\n#include \"g_game.h\"\n#include \"doomdef.h\"\n#include \"doomstat.h\"\n#include \"w_checksum.h\"\n#include \"w_wad.h\"\n\n#include \"deh_main.h\"\n\n#include \"d_loop.h\"\n\nticcmd_t *netcmds;\n\n// Called when a player leaves the game\n\nstatic void PlayerQuitGame(player_t *player)\n{\n    static char exitmsg[80];\n    unsigned int player_num;\n\n    player_num = player - players;\n\n    // Do this the same way as Vanilla Doom does, to allow dehacked\n    // replacements of this message\n\n    M_StringCopy(exitmsg, DEH_String(\"Player 1 left the game\"),\n                 sizeof(exitmsg));\n\n    exitmsg[7] += player_num;\n\n    playeringame[player_num] = false;\n    players[consoleplayer].message = exitmsg;\n\n    // TODO: check if it is sensible to do this:\n\n    if (demorecording) \n    {\n        G_CheckDemoStatus ();\n    }\n}\n\nstatic void RunTic(ticcmd_t *cmds, boolean *ingame)\n{\n    extern boolean advancedemo;\n    unsigned int i;\n\n    // Check for player quits.\n\n    for (i = 0; i < MAXPLAYERS; ++i)\n    {\n        if (!demoplayback && playeringame[i] && !ingame[i])\n        {\n            PlayerQuitGame(&players[i]);\n        }\n    }\n\n    netcmds = cmds;\n\n    // check that there are players in the game.  if not, we cannot\n    // run a tic.\n\n    if (advancedemo)\n        D_DoAdvanceDemo ();\n\n    G_Ticker ();\n}\n\nstatic loop_interface_t doom_loop_interface = {\n    D_ProcessEvents,\n    G_BuildTiccmd,\n    RunTic,\n    M_Ticker\n};\n\n\n// Load game settings from the specified structure and\n// set global variables.\n\nstatic void LoadGameSettings(net_gamesettings_t *settings)\n{\n    unsigned int i;\n\n    deathmatch = settings->deathmatch;\n    startepisode = settings->episode;\n    startmap = settings->map;\n    startskill = settings->skill;\n    startloadgame = settings->loadgame;\n    lowres_turn = settings->lowres_turn;\n    nomonsters = settings->nomonsters;\n    fastparm = settings->fast_monsters;\n    respawnparm = settings->respawn_monsters;\n    timelimit = settings->timelimit;\n    consoleplayer = settings->consoleplayer;\n\n    if (lowres_turn)\n    {\n        printf(\"NOTE: Turning resolution is reduced; this is probably \"\n               \"because there is a client recording a Vanilla demo.\\n\");\n    }\n\n    for (i = 0; i < MAXPLAYERS; ++i)\n    {\n        playeringame[i] = i < settings->num_players;\n    }\n}\n\n// Save the game settings from global variables to the specified\n// game settings structure.\n\nstatic void SaveGameSettings(net_gamesettings_t *settings)\n{\n    // Fill in game settings structure with appropriate parameters\n    // for the new game\n\n    settings->deathmatch = deathmatch;\n    settings->episode = startepisode;\n    settings->map = startmap;\n    settings->skill = startskill;\n    settings->loadgame = startloadgame;\n    settings->gameversion = gameversion;\n    settings->nomonsters = nomonsters;\n    settings->fast_monsters = fastparm;\n    settings->respawn_monsters = respawnparm;\n    settings->timelimit = timelimit;\n\n    settings->lowres_turn = M_CheckParm(\"-record\") > 0\n                         && M_CheckParm(\"-longtics\") == 0;\n}\n\nstatic void InitConnectData(net_connect_data_t *connect_data)\n{\n    connect_data->max_players = MAXPLAYERS;\n    connect_data->drone = false;\n\n    //!\n    // @category net\n    //\n    // Run as the left screen in three screen mode.\n    //\n\n    if (M_CheckParm(\"-left\") > 0)\n    {\n        viewangleoffset = ANG90;\n        connect_data->drone = true;\n    }\n\n    //! \n    // @category net\n    //\n    // Run as the right screen in three screen mode.\n    //\n\n    if (M_CheckParm(\"-right\") > 0)\n    {\n        viewangleoffset = ANG270;\n        connect_data->drone = true;\n    }\n\n    //\n    // Connect data\n    //\n\n    // Game type fields:\n\n    connect_data->gamemode = gamemode;\n    connect_data->gamemission = gamemission;\n\n    // Are we recording a demo? Possibly set lowres turn mode\n\n    connect_data->lowres_turn = M_CheckParm(\"-record\") > 0\n                             && M_CheckParm(\"-longtics\") == 0;\n\n    // Read checksums of our WAD directory and dehacked information\n\n    W_Checksum(connect_data->wad_sha1sum);\n\n#if ORIGCODE\n    DEH_Checksum(connect_data->deh_sha1sum);\n#endif\n\n    // Are we playing with the Freedoom IWAD?\n\n    connect_data->is_freedoom = W_CheckNumForName(\"FREEDOOM\") >= 0;\n}\n\nvoid D_ConnectNetGame(void)\n{\n    net_connect_data_t connect_data;\n\n    InitConnectData(&connect_data);\n    netgame = D_InitNetGame(&connect_data);\n\n    //!\n    // @category net\n    //\n    // Start the game playing as though in a netgame with a single\n    // player.  This can also be used to play back single player netgame\n    // demos.\n    //\n\n    if (M_CheckParm(\"-solo-net\") > 0)\n    {\n        netgame = true;\n    }\n}\n\n//\n// D_CheckNetGame\n// Works out player numbers among the net participants\n//\nvoid D_CheckNetGame (void)\n{\n    net_gamesettings_t settings;\n\n    if (netgame)\n    {\n        autostart = true;\n    }\n\n    D_RegisterLoopCallbacks(&doom_loop_interface);\n\n    SaveGameSettings(&settings);\n    D_StartNetGame(&settings, NULL);\n    LoadGameSettings(&settings);\n\n    DEH_printf(\"startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\\n\",\n               startskill, deathmatch, startmap, startepisode);\n\n    DEH_printf(\"player %i of %i (%i nodes)\\n\",\n               consoleplayer+1, settings.num_players, settings.num_players);\n\n    // Show players here; the server might have specified a time limit\n\n    if (timelimit > 0 && deathmatch)\n    {\n        // Gross hack to work like Vanilla:\n\n        if (timelimit == 20 && M_CheckParm(\"-avg\"))\n        {\n            DEH_printf(\"Austin Virtual Gaming: Levels will end \"\n                           \"after 20 minutes\\n\");\n        }\n        else\n        {\n            DEH_printf(\"Levels will end after %d minute\", timelimit);\n            if (timelimit > 1)\n                printf(\"s\");\n            printf(\".\\n\");\n        }\n    }\n}\n\n"
  },
  {
    "path": "fbdoom/d_player.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\n//\n\n\n#ifndef __D_PLAYER__\n#define __D_PLAYER__\n\n\n// The player data structure depends on a number\n// of other structs: items (internal inventory),\n// animation states (closely tied to the sprites\n// used to represent them, unfortunately).\n#include \"d_items.h\"\n#include \"p_pspr.h\"\n\n// In addition, the player is just a special\n// case of the generic moving object/actor.\n#include \"p_mobj.h\"\n\n// Finally, for odd reasons, the player input\n// is buffered within the player data struct,\n// as commands per game tick.\n#include \"d_ticcmd.h\"\n\n#include \"net_defs.h\"\n\n\n\n\n//\n// Player states.\n//\ntypedef enum\n{\n    // Playing or camping.\n    PST_LIVE,\n    // Dead on the ground, view follows killer.\n    PST_DEAD,\n    // Ready to restart/respawn???\n    PST_REBORN\t\t\n\n} playerstate_t;\n\n\n//\n// Player internal flags, for cheats and debug.\n//\ntypedef enum\n{\n    // No clipping, walk through barriers.\n    CF_NOCLIP\t\t= 1,\n    // No damage, no health loss.\n    CF_GODMODE\t\t= 2,\n    // Not really a cheat, just a debug aid.\n    CF_NOMOMENTUM\t= 4\n\n} cheat_t;\n\n\n//\n// Extended player object info: player_t\n//\ntypedef struct player_s\n{\n    mobj_t*\t\tmo;\n    playerstate_t\tplayerstate;\n    ticcmd_t\t\tcmd;\n\n    // Determine POV,\n    //  including viewpoint bobbing during movement.\n    // Focal origin above r.z\n    fixed_t\t\tviewz;\n    // Base height above floor for viewz.\n    fixed_t\t\tviewheight;\n    // Bob/squat speed.\n    fixed_t         \tdeltaviewheight;\n    // bounded/scaled total momentum.\n    fixed_t         \tbob;\t\n\n    // This is only used between levels,\n    // mo->health is used during levels.\n    int\t\t\thealth;\t\n    int\t\t\tarmorpoints;\n    // Armor type is 0-2.\n    int\t\t\tarmortype;\t\n\n    // Power ups. invinc and invis are tic counters.\n    int\t\t\tpowers[NUMPOWERS];\n    boolean\t\tcards[NUMCARDS];\n    boolean\t\tbackpack;\n    \n    // Frags, kills of other players.\n    int\t\t\tfrags[MAXPLAYERS];\n    weapontype_t\treadyweapon;\n    \n    // Is wp_nochange if not changing.\n    weapontype_t\tpendingweapon;\n\n    boolean\t\tweaponowned[NUMWEAPONS];\n    int\t\t\tammo[NUMAMMO];\n    int\t\t\tmaxammo[NUMAMMO];\n\n    // True if button down last tic.\n    int\t\t\tattackdown;\n    int\t\t\tusedown;\n\n    // Bit flags, for cheats and debug.\n    // See cheat_t, above.\n    int\t\t\tcheats;\t\t\n\n    // Refired shots are less accurate.\n    int\t\t\trefire;\t\t\n\n     // For intermission stats.\n    int\t\t\tkillcount;\n    int\t\t\titemcount;\n    int\t\t\tsecretcount;\n\n    // Hint messages.\n    char*\t\tmessage;\t\n    \n    // For screen flashing (red or bright).\n    int\t\t\tdamagecount;\n    int\t\t\tbonuscount;\n\n    // Who did damage (NULL for floors/ceilings).\n    mobj_t*\t\tattacker;\n    \n    // So gun flashes light up areas.\n    int\t\t\textralight;\n\n    // Current PLAYPAL, ???\n    //  can be set to REDCOLORMAP for pain, etc.\n    int\t\t\tfixedcolormap;\n\n    // Player skin colorshift,\n    //  0-3 for which color to draw player.\n    int\t\t\tcolormap;\t\n\n    // Overlay view sprites (gun, etc).\n    pspdef_t\t\tpsprites[NUMPSPRITES];\n\n    // True if secret level has been done.\n    boolean\t\tdidsecret;\t\n\n} player_t;\n\n\n//\n// INTERMISSION\n// Structure passed e.g. to WI_Start(wb)\n//\ntypedef struct\n{\n    boolean\tin;\t// whether the player is in game\n    \n    // Player stats, kills, collected items etc.\n    int\t\tskills;\n    int\t\tsitems;\n    int\t\tssecret;\n    int\t\tstime; \n    int\t\tfrags[4];\n    int\t\tscore;\t// current score on entry, modified on return\n  \n} wbplayerstruct_t;\n\ntypedef struct\n{\n    int\t\tepsd;\t// episode # (0-2)\n\n    // if true, splash the secret level\n    boolean\tdidsecret;\n    \n    // previous and next levels, origin 0\n    int\t\tlast;\n    int\t\tnext;\t\n    \n    int\t\tmaxkills;\n    int\t\tmaxitems;\n    int\t\tmaxsecret;\n    int\t\tmaxfrags;\n\n    // the par time\n    int\t\tpartime;\n    \n    // index of this player in game\n    int\t\tpnum;\t\n\n    wbplayerstruct_t\tplyr[MAXPLAYERS];\n\n} wbstartstruct_t;\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/d_textur.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tTypedefs related to to textures etc.,\n//\t isolated here to make it easier separating modules.\n//    \n\n\n#ifndef __D_TEXTUR__\n#define __D_TEXTUR__\n\n#include \"doomtype.h\"\n\n\n\n\n//\n// Flats?\n//\n// a pic is an unmasked block of pixels\ntypedef struct\n{\n    byte\t\twidth;\n    byte\t\theight;\n    byte\t\tdata;\n} pic_t;\n\n\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/d_think.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//  MapObj data. Map Objects or mobjs are actors, entities,\n//  thinker, take-your-pick... anything that moves, acts, or\n//  suffers state changes of more or less violent nature.\n//\n\n\n#ifndef __D_THINK__\n#define __D_THINK__\n\n\n\n\n\n//\n// Experimental stuff.\n// To compile this as \"ANSI C with classes\"\n//  we will need to handle the various\n//  action functions cleanly.\n//\ntypedef  void (*actionf_v)();\ntypedef  void (*actionf_p1)( void* );\ntypedef  void (*actionf_p2)( void*, void* );\n\ntypedef union\n{\n  actionf_v\tacv;\n  actionf_p1\tacp1;\n  actionf_p2\tacp2;\n\n} actionf_t;\n\n\n\n\n\n// Historically, \"think_t\" is yet another\n//  function pointer to a routine to handle\n//  an actor.\ntypedef actionf_t  think_t;\n\n\n// Doubly linked list of actors.\ntypedef struct thinker_s\n{\n    struct thinker_s*\tprev;\n    struct thinker_s*\tnext;\n    think_t\t\tfunction;\n    \n} thinker_t;\n\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/d_ticcmd.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 1993-2008 Raven Software\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tSystem specific interface stuff.\n//\n\n\n#ifndef __D_TICCMD__\n#define __D_TICCMD__\n\n#include \"doomtype.h\"\n\n\n// The data sampled per tick (single player)\n// and transmitted to other peers (multiplayer).\n// Mainly movements/button commands per game tick,\n// plus a checksum for internal state consistency.\n\ntypedef struct\n{\n    signed char\tforwardmove;\t// *2048 for move\n    signed char\tsidemove;\t// *2048 for move\n    short angleturn;            // <<16 for angle delta\n    byte chatchar;\n    byte buttons;\n    // villsa [STRIFE] according to the asm,\n    // consistancy is a short, not a byte\n    byte consistancy;           // checks for net game\n\n    // villsa - Strife specific:\n\n    byte buttons2;\n    int inventory;\n   \n    // Heretic/Hexen specific:\n\n    byte lookfly;               // look/fly up/down/centering\n    byte arti;                  // artitype_t to use\n} ticcmd_t;\n\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/deh_main.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n//\n// Dehacked entrypoint and common code\n//\n\n#ifndef DEH_MAIN_H\n#define DEH_MAIN_H\n\n#include \"doomtype.h\"\n#include \"doomfeatures.h\"\n#include \"deh_str.h\"\n#include \"sha1.h\"\n\n// These are the limits that dehacked uses (from dheinit.h in the dehacked\n// source).  If these limits are exceeded, it does not generate an error, but\n// a warning is displayed.\n\n#define DEH_VANILLA_NUMSTATES 966\n#define DEH_VANILLA_NUMSFX 107\n\nvoid DEH_ParseCommandLine(void);\nint DEH_LoadFile(char *filename);\nint DEH_LoadLump(int lumpnum, boolean allow_long, boolean allow_error);\nint DEH_LoadLumpByName(char *name, boolean allow_long, boolean allow_error);\n\nboolean DEH_ParseAssignment(char *line, char **variable_name, char **value);\n\nvoid DEH_Checksum(sha1_digest_t digest);\n\nextern boolean deh_allow_extended_strings;\nextern boolean deh_allow_long_strings;\nextern boolean deh_allow_long_cheats;\nextern boolean deh_apply_cheats;\n\n#endif /* #ifndef DEH_MAIN_H */\n\n"
  },
  {
    "path": "fbdoom/deh_misc.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n//\n// Parses \"Misc\" sections in dehacked files\n//\n\n#ifndef DEH_MISC_H\n#define DEH_MISC_H\n\n#include \"doomfeatures.h\"\n\n#define DEH_DEFAULT_INITIAL_HEALTH 100\n#define DEH_DEFAULT_INITIAL_BULLETS 50\n#define DEH_DEFAULT_MAX_HEALTH 200\n#define DEH_DEFAULT_MAX_ARMOR 200\n#define DEH_DEFAULT_GREEN_ARMOR_CLASS 1\n#define DEH_DEFAULT_BLUE_ARMOR_CLASS 2\n#define DEH_DEFAULT_MAX_SOULSPHERE 200\n#define DEH_DEFAULT_SOULSPHERE_HEALTH 100\n#define DEH_DEFAULT_MEGASPHERE_HEALTH 200\n#define DEH_DEFAULT_GOD_MODE_HEALTH 100\n#define DEH_DEFAULT_IDFA_ARMOR 200\n#define DEH_DEFAULT_IDFA_ARMOR_CLASS 2\n#define DEH_DEFAULT_IDKFA_ARMOR 200\n#define DEH_DEFAULT_IDKFA_ARMOR_CLASS 2\n#define DEH_DEFAULT_BFG_CELLS_PER_SHOT 40\n#define DEH_DEFAULT_SPECIES_INFIGHTING 0\n\n#ifdef FEATURE_DEHACKED\n\nextern int deh_initial_health;             \nextern int deh_initial_bullets;            \nextern int deh_max_health;                 \nextern int deh_max_armor;                  \nextern int deh_green_armor_class;          \nextern int deh_blue_armor_class;           \nextern int deh_max_soulsphere;             \nextern int deh_soulsphere_health;          \nextern int deh_megasphere_health;          \nextern int deh_god_mode_health;            \nextern int deh_idfa_armor;                 \nextern int deh_idfa_armor_class;           \nextern int deh_idkfa_armor;                \nextern int deh_idkfa_armor_class;          \nextern int deh_bfg_cells_per_shot;         \nextern int deh_species_infighting;           \n\n#else\n\n// If dehacked is disabled, hard coded values\n\n#define deh_initial_health      DEH_DEFAULT_INITIAL_HEALTH\n#define deh_initial_bullets     DEH_DEFAULT_INITIAL_BULLETS\n#define deh_max_health          DEH_DEFAULT_MAX_HEALTH\n#define deh_max_armor           DEH_DEFAULT_MAX_ARMOR\n#define deh_green_armor_class   DEH_DEFAULT_GREEN_ARMOR_CLASS\n#define deh_blue_armor_class    DEH_DEFAULT_BLUE_ARMOR_CLASS\n#define deh_max_soulsphere      DEH_DEFAULT_MAX_SOULSPHERE\n#define deh_soulsphere_health   DEH_DEFAULT_SOULSPHERE_HEALTH\n#define deh_megasphere_health   DEH_DEFAULT_MEGASPHERE_HEALTH\n#define deh_god_mode_health     DEH_DEFAULT_GOD_MODE_HEALTH\n#define deh_idfa_armor          DEH_DEFAULT_IDFA_ARMOR\n#define deh_idfa_armor_class    DEH_DEFAULT_IDFA_ARMOR_CLASS\n#define deh_idkfa_armor         DEH_DEFAULT_IDKFA_ARMOR\n#define deh_idkfa_armor_class   DEH_DEFAULT_IDKFA_ARMOR_CLASS\n#define deh_bfg_cells_per_shot  DEH_DEFAULT_BFG_CELLS_PER_SHOT\n#define deh_species_infighting  DEH_DEFAULT_SPECIES_INFIGHTING\n\n#endif\n\n#endif /* #ifndef DEH_MISC_H */\n\n"
  },
  {
    "path": "fbdoom/deh_str.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n//\n// Dehacked string replacements\n//\n\n#ifndef DEH_STR_H\n#define DEH_STR_H\n\n#include <stdio.h>\n\n#include \"doomfeatures.h\"\n\n// Used to do dehacked text substitutions throughout the program\n\n#ifdef FEATURE_DEHACKED\n\nchar *DEH_String(char *s);\nvoid DEH_printf(char *fmt, ...);\nvoid DEH_fprintf(FILE *fstream, char *fmt, ...);\nvoid DEH_snprintf(char *buffer, size_t len, char *fmt, ...);\nvoid DEH_AddStringReplacement(char *from_text, char *to_text);\n\n\n#else\n\n#define DEH_String(x) (x)\n#define DEH_printf printf\n#define DEH_fprintf fprintf\n#define DEH_snprintf snprintf\n#define DEH_AddStringReplacement(x, y)\n\n#endif\n\n#endif /* #ifndef DEH_STR_H */\n\n"
  },
  {
    "path": "fbdoom/doom.h",
    "content": "/*\n * doom.h\n *\n *  Created on: 18.02.2015\n *      Author: Florian\n */\n\n\n#ifndef SRC_CHOCDOOM_DOOM_H_\n#define SRC_CHOCDOOM_DOOM_H_\n\n/*---------------------------------------------------------------------*\n *  additional includes                                                *\n *---------------------------------------------------------------------*/\n\n/*---------------------------------------------------------------------*\n *  global definitions                                                 *\n *---------------------------------------------------------------------*/\n\n/*---------------------------------------------------------------------*\n *  type declarations                                                  *\n *---------------------------------------------------------------------*/\n\n/*---------------------------------------------------------------------*\n *  function prototypes                                                *\n *---------------------------------------------------------------------*/\n\nvoid D_DoomMain (void);\n\n/*---------------------------------------------------------------------*\n *  global data                                                        *\n *---------------------------------------------------------------------*/\n\n/*---------------------------------------------------------------------*\n *  inline functions and function-like macros                          *\n *---------------------------------------------------------------------*/\n\n/*---------------------------------------------------------------------*\n *  eof                                                                *\n *---------------------------------------------------------------------*/\n\n#endif /* SRC_CHOCDOOM_DOOM_H_ */\n"
  },
  {
    "path": "fbdoom/doomdata.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//  all external data is defined here\n//  most of the data is loaded into different structures at run time\n//  some internal structures shared by many modules are here\n//\n\n#ifndef __DOOMDATA__\n#define __DOOMDATA__\n\n// The most basic types we use, portability.\n#include \"doomtype.h\"\n\n// Some global defines, that configure the game.\n#include \"doomdef.h\"\n\n\n\n//\n// Map level types.\n// The following data structures define the persistent format\n// used in the lumps of the WAD files.\n//\n\n// Lump order in a map WAD: each map needs a couple of lumps\n// to provide a complete scene geometry description.\nenum\n{\n  ML_LABEL,\t\t// A separator, name, ExMx or MAPxx\n  ML_THINGS,\t\t// Monsters, items..\n  ML_LINEDEFS,\t\t// LineDefs, from editing\n  ML_SIDEDEFS,\t\t// SideDefs, from editing\n  ML_VERTEXES,\t\t// Vertices, edited and BSP splits generated\n  ML_SEGS,\t\t// LineSegs, from LineDefs split by BSP\n  ML_SSECTORS,\t\t// SubSectors, list of LineSegs\n  ML_NODES,\t\t// BSP nodes\n  ML_SECTORS,\t\t// Sectors, from editing\n  ML_REJECT,\t\t// LUT, sector-sector visibility\t\n  ML_BLOCKMAP\t\t// LUT, motion clipping, walls/grid element\n};\n\n\n// A single Vertex.\ntypedef struct\n{\n  short\t\tx;\n  short\t\ty;\n} PACKEDATTR mapvertex_t;\n\n\n// A SideDef, defining the visual appearance of a wall,\n// by setting textures and offsets.\ntypedef struct\n{\n  short\t\ttextureoffset;\n  short\t\trowoffset;\n  char\t\ttoptexture[8];\n  char\t\tbottomtexture[8];\n  char\t\tmidtexture[8];\n  // Front sector, towards viewer.\n  short\t\tsector;\n} PACKEDATTR mapsidedef_t;\n\n\n\n// A LineDef, as used for editing, and as input\n// to the BSP builder.\ntypedef struct\n{\n  short\t\tv1;\n  short\t\tv2;\n  short\t\tflags;\n  short\t\tspecial;\n  short\t\ttag;\n  // sidenum[1] will be -1 if one sided\n  short\t\tsidenum[2];\t\t\n} PACKEDATTR maplinedef_t;\n\n\n//\n// LineDef attributes.\n//\n\n// Solid, is an obstacle.\n#define ML_BLOCKING\t\t1\n\n// Blocks monsters only.\n#define ML_BLOCKMONSTERS\t2\n\n// Backside will not be present at all\n//  if not two sided.\n#define ML_TWOSIDED\t\t4\n\n// If a texture is pegged, the texture will have\n// the end exposed to air held constant at the\n// top or bottom of the texture (stairs or pulled\n// down things) and will move with a height change\n// of one of the neighbor sectors.\n// Unpegged textures allways have the first row of\n// the texture at the top pixel of the line for both\n// top and bottom textures (use next to windows).\n\n// upper texture unpegged\n#define ML_DONTPEGTOP\t\t8\n\n// lower texture unpegged\n#define ML_DONTPEGBOTTOM\t16\t\n\n// In AutoMap: don't map as two sided: IT'S A SECRET!\n#define ML_SECRET\t\t32\n\n// Sound rendering: don't let sound cross two of these.\n#define ML_SOUNDBLOCK\t\t64\n\n// Don't draw on the automap at all.\n#define ML_DONTDRAW\t\t128\n\n// Set if already seen, thus drawn in automap.\n#define ML_MAPPED\t\t256\n\n\n\n\n// Sector definition, from editing.\ntypedef\tstruct\n{\n  short\t\tfloorheight;\n  short\t\tceilingheight;\n  char\t\tfloorpic[8];\n  char\t\tceilingpic[8];\n  short\t\tlightlevel;\n  short\t\tspecial;\n  short\t\ttag;\n} PACKEDATTR mapsector_t;\n\n// SubSector, as generated by BSP.\ntypedef struct\n{\n  short\t\tnumsegs;\n  // Index of first one, segs are stored sequentially.\n  short\t\tfirstseg;\t\n} PACKEDATTR mapsubsector_t;\n\n\n// LineSeg, generated by splitting LineDefs\n// using partition lines selected by BSP builder.\ntypedef struct\n{\n  short\t\tv1;\n  short\t\tv2;\n  short\t\tangle;\t\t\n  short\t\tlinedef;\n  short\t\tside;\n  short\t\toffset;\n} PACKEDATTR mapseg_t;\n\n\n\n// BSP node structure.\n\n// Indicate a leaf.\n#define\tNF_SUBSECTOR\t0x8000\n\ntypedef struct\n{\n  // Partition line from (x,y) to x+dx,y+dy)\n  short\t\tx;\n  short\t\ty;\n  short\t\tdx;\n  short\t\tdy;\n\n  // Bounding box for each child,\n  // clip against view frustum.\n  short\t\tbbox[2][4];\n\n  // If NF_SUBSECTOR its a subsector,\n  // else it's a node of another subtree.\n  unsigned short\tchildren[2];\n\n} PACKEDATTR mapnode_t;\n\n\n\n\n// Thing definition, position, orientation and type,\n// plus skill/visibility flags and attributes.\ntypedef struct\n{\n    short\t\tx;\n    short\t\ty;\n    short\t\tangle;\n    short\t\ttype;\n    short\t\toptions;\n} PACKEDATTR mapthing_t;\n\n\n\n\n\n#endif\t\t\t// __DOOMDATA__\n"
  },
  {
    "path": "fbdoom/doomdef.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//  DoomDef - basic defines for DOOM, e.g. Version, game mode\n//   and skill level, and display parameters.\n//\n\n\n\n#include \"doomdef.h\"\n\n// Location for any defines turned variables.\n\n// None.\n\n\n"
  },
  {
    "path": "fbdoom/doomdef.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//  Internally used data structures for virtually everything,\n//   lots of other stuff.\n//\n\n#ifndef __DOOMDEF__\n#define __DOOMDEF__\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"doomtype.h\"\n#include \"i_timer.h\"\n#include \"d_mode.h\"\n\n//\n// Global parameters/defines.\n//\n// DOOM version\n#define DOOM_VERSION 109\n\n// Version code for cph's longtics hack (\"v1.91\")\n#define DOOM_191_VERSION 111\n\n\n// If rangecheck is undefined,\n// most parameter validation debugging code will not be compiled\n#define RANGECHECK\n\n// The maximum number of players, multiplayer/networking.\n#define MAXPLAYERS 4\n\n// The current state of the game: whether we are\n// playing, gazing at the intermission screen,\n// the game final animation, or a demo. \ntypedef enum\n{\n    GS_LEVEL,\n    GS_INTERMISSION,\n    GS_FINALE,\n    GS_DEMOSCREEN,\n} gamestate_t;\n\ntypedef enum\n{\n    ga_nothing,\n    ga_loadlevel,\n    ga_newgame,\n    ga_loadgame,\n    ga_savegame,\n    ga_playdemo,\n    ga_completed,\n    ga_victory,\n    ga_worlddone,\n    ga_screenshot\n} gameaction_t;\n\n//\n// Difficulty/skill settings/filters.\n//\n\n// Skill flags.\n#define\tMTF_EASY\t\t1\n#define\tMTF_NORMAL\t\t2\n#define\tMTF_HARD\t\t4\n\n// Deaf monsters/do not react to sound.\n#define\tMTF_AMBUSH\t\t8\n\n\n//\n// Key cards.\n//\ntypedef enum\n{\n    it_bluecard,\n    it_yellowcard,\n    it_redcard,\n    it_blueskull,\n    it_yellowskull,\n    it_redskull,\n    \n    NUMCARDS\n    \n} card_t;\n\n\n\n// The defined weapons,\n//  including a marker indicating\n//  user has not changed weapon.\ntypedef enum\n{\n    wp_fist,\n    wp_pistol,\n    wp_shotgun,\n    wp_chaingun,\n    wp_missile,\n    wp_plasma,\n    wp_bfg,\n    wp_chainsaw,\n    wp_supershotgun,\n\n    NUMWEAPONS,\n    \n    // No pending weapon change.\n    wp_nochange\n\n} weapontype_t;\n\n\n// Ammunition types defined.\ntypedef enum\n{\n    am_clip,\t// Pistol / chaingun ammo.\n    am_shell,\t// Shotgun / double barreled shotgun.\n    am_cell,\t// Plasma rifle, BFG.\n    am_misl,\t// Missile launcher.\n    NUMAMMO,\n    am_noammo\t// Unlimited for chainsaw / fist.\t\n\n} ammotype_t;\n\n\n// Power up artifacts.\ntypedef enum\n{\n    pw_invulnerability,\n    pw_strength,\n    pw_invisibility,\n    pw_ironfeet,\n    pw_allmap,\n    pw_infrared,\n    NUMPOWERS\n    \n} powertype_t;\n\n\n\n//\n// Power up durations,\n//  how many seconds till expiration,\n//  assuming TICRATE is 35 ticks/second.\n//\ntypedef enum\n{\n    INVULNTICS\t= (30*TICRATE),\n    INVISTICS\t= (60*TICRATE),\n    INFRATICS\t= (120*TICRATE),\n    IRONTICS\t= (60*TICRATE)\n    \n} powerduration_t;\n\n#endif          // __DOOMDEF__\n"
  },
  {
    "path": "fbdoom/doomfeatures.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     List of features which can be enabled/disabled to slim down the\n//     program.\n//\n\n#ifndef DOOM_FEATURES_H\n#define DOOM_FEATURES_H\n\n// Enables wad merging (the '-merge' command line parameter)\n\n#undef FEATURE_WAD_MERGE\n\n// Enables dehacked support ('-deh')\n\n#undef FEATURE_DEHACKED\n\n// Enables multiplayer support (network games)\n\n#undef FEATURE_MULTIPLAYER\n\n// Enables sound output\n\n#undef FEATURE_SOUND\n\n#endif /* #ifndef DOOM_FEATURES_H */\n\n\n"
  },
  {
    "path": "fbdoom/doomkeys.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//       Key definitions\n//\n\n#ifndef __DOOMKEYS__\n#define __DOOMKEYS__\n\n//\n// DOOM keyboard definition.\n// This is the stuff configured by Setup.Exe.\n// Most key data are simple ascii (uppercased).\n//\n#define KEY_RIGHTARROW\t0xae\n#define KEY_LEFTARROW\t0xac\n#define KEY_UPARROW\t\t0xad\n#define KEY_DOWNARROW\t0xaf\n#define KEY_STRAFE_L\t0xa0\n#define KEY_STRAFE_R\t0xa1\n#define KEY_USE\t\t\t0xa2\n#define KEY_FIRE\t\t0xa3\n#define KEY_ESCAPE\t\t27\n#define KEY_ENTER\t\t13\n#define KEY_TAB\t\t\t9\n#define KEY_F1\t\t\t(0x80+0x3b)\n#define KEY_F2\t\t\t(0x80+0x3c)\n#define KEY_F3\t\t\t(0x80+0x3d)\n#define KEY_F4\t\t\t(0x80+0x3e)\n#define KEY_F5\t\t\t(0x80+0x3f)\n#define KEY_F6\t\t\t(0x80+0x40)\n#define KEY_F7\t\t\t(0x80+0x41)\n#define KEY_F8\t\t\t(0x80+0x42)\n#define KEY_F9\t\t\t(0x80+0x43)\n#define KEY_F10\t\t\t(0x80+0x44)\n#define KEY_F11\t\t\t(0x80+0x57)\n#define KEY_F12\t\t\t(0x80+0x58)\n\n#define KEY_BACKSPACE\t0x7f\n#define KEY_PAUSE\t0xff\n\n#define KEY_EQUALS\t0x3d\n#define KEY_MINUS\t0x2d\n\n#define KEY_RSHIFT\t(0x80+0x36)\n#define KEY_RCTRL\t(0x80+0x1d)\n#define KEY_RALT\t(0x80+0x38)\n\n#define KEY_LALT\tKEY_RALT\n\n// new keys:\n\n#define KEY_CAPSLOCK    (0x80+0x3a)\n#define KEY_NUMLOCK     (0x80+0x45)\n#define KEY_SCRLCK      (0x80+0x46)\n#define KEY_PRTSCR      (0x80+0x59)\n\n#define KEY_HOME        (0x80+0x47)\n#define KEY_END         (0x80+0x4f)\n#define KEY_PGUP        (0x80+0x49)\n#define KEY_PGDN        (0x80+0x51)\n#define KEY_INS         (0x80+0x52)\n#define KEY_DEL         (0x80+0x53)\n\n#define KEYP_0          0\n#define KEYP_1          KEY_END\n#define KEYP_2          KEY_DOWNARROW\n#define KEYP_3          KEY_PGDN\n#define KEYP_4          KEY_LEFTARROW\n#define KEYP_5          '5'\n#define KEYP_6          KEY_RIGHTARROW\n#define KEYP_7          KEY_HOME\n#define KEYP_8          KEY_UPARROW\n#define KEYP_9          KEY_PGUP\n\n#define KEYP_DIVIDE     '/'\n#define KEYP_PLUS       '+'\n#define KEYP_MINUS      '-'\n#define KEYP_MULTIPLY   '*'\n#define KEYP_PERIOD     0\n#define KEYP_EQUALS     KEY_EQUALS\n#define KEYP_ENTER      KEY_ENTER\n\n#endif          // __DOOMKEYS__\n\n"
  },
  {
    "path": "fbdoom/doomstat.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tPut all global tate variables here.\n//\n\n#include <stdio.h>\n\n#include \"doomstat.h\"\n\n\n// Game Mode - identify IWAD as shareware, retail etc.\nGameMode_t gamemode = indetermined;\nGameMission_t\tgamemission = doom;\nGameVersion_t   gameversion = exe_final2;\nchar *gamedescription;\n\n// Set if homebrew PWAD stuff has been added.\nboolean\tmodifiedgame;\n\n\n\n\n"
  },
  {
    "path": "fbdoom/doomstat.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//   All the global variables that store the internal state.\n//   Theoretically speaking, the internal state of the engine\n//    should be found by looking at the variables collected\n//    here, and every relevant module will have to include\n//    this header file.\n//   In practice, things are a bit messy.\n//\n\n\n#ifndef __D_STATE__\n#define __D_STATE__\n\n// We need globally shared data structures,\n//  for defining the global state variables.\n#include \"doomdata.h\"\n#include \"d_loop.h\"\n\n// We need the playr data structure as well.\n#include \"d_player.h\"\n\n// Game mode/mission\n#include \"d_mode.h\"\n\n#include \"net_defs.h\"\n\n\n\n// ------------------------\n// Command line parameters.\n//\nextern  boolean\tnomonsters;\t// checkparm of -nomonsters\nextern  boolean\trespawnparm;\t// checkparm of -respawn\nextern  boolean\tfastparm;\t// checkparm of -fast\n\nextern  boolean\tdevparm;\t// DEBUG: launched with -devparm\n\n\n// -----------------------------------------------------\n// Game Mode - identify IWAD as shareware, retail etc.\n//\nextern GameMode_t\tgamemode;\nextern GameMission_t\tgamemission;\nextern GameVersion_t    gameversion;\nextern char            *gamedescription;\n\n// If true, we're using one of the mangled BFG edition IWADs.\nextern boolean bfgedition;\n\n// Convenience macro.\n// 'gamemission' can be equal to pack_chex or pack_hacx, but these are\n// just modified versions of doom and doom2, and should be interpreted\n// as the same most of the time.\n\n#define logical_gamemission                             \\\n    (gamemission == pack_chex ? doom :                  \\\n     gamemission == pack_hacx ? doom2 : gamemission)\n\n// Set if homebrew PWAD stuff has been added.\nextern  boolean\tmodifiedgame;\n\n\n// -------------------------------------------\n// Selected skill type, map etc.\n//\n\n// Defaults for menu, methinks.\nextern  skill_t\t\tstartskill;\nextern  int             startepisode;\nextern\tint\t\tstartmap;\n\n// Savegame slot to load on startup.  This is the value provided to\n// the -loadgame option.  If this has not been provided, this is -1.\n\nextern  int             startloadgame;\n\nextern  boolean\t\tautostart;\n\n// Selected by user. \nextern  skill_t         gameskill;\nextern  int\t\tgameepisode;\nextern  int\t\tgamemap;\n\n// If non-zero, exit the level after this number of minutes\nextern  int             timelimit;\n\n// Nightmare mode flag, single player.\nextern  boolean         respawnmonsters;\n\n// Netgame? Only true if >1 player.\nextern  boolean\tnetgame;\n\n// 0=Cooperative; 1=Deathmatch; 2=Altdeath\nextern int deathmatch;\n\n// -------------------------\n// Internal parameters for sound rendering.\n// These have been taken from the DOS version,\n//  but are not (yet) supported with Linux\n//  (e.g. no sound volume adjustment with menu.\n\n// From m_menu.c:\n//  Sound FX volume has default, 0 - 15\n//  Music volume has default, 0 - 15\n// These are multiplied by 8.\nextern int sfxVolume;\nextern int musicVolume;\n\n// Current music/sfx card - index useless\n//  w/o a reference LUT in a sound module.\n// Ideally, this would use indices found\n//  in: /usr/include/linux/soundcard.h\nextern int snd_MusicDevice;\nextern int snd_SfxDevice;\n// Config file? Same disclaimer as above.\nextern int snd_DesiredMusicDevice;\nextern int snd_DesiredSfxDevice;\n\n\n// -------------------------\n// Status flags for refresh.\n//\n\n// Depending on view size - no status bar?\n// Note that there is no way to disable the\n//  status bar explicitely.\nextern  boolean statusbaractive;\n\nextern  boolean automapactive;\t// In AutoMap mode?\nextern  boolean\tmenuactive;\t// Menu overlayed?\nextern  boolean\tpaused;\t\t// Game Pause?\n\n\nextern  boolean\t\tviewactive;\n\nextern  boolean\t\tnodrawers;\n\n\nextern  boolean         testcontrols;\nextern  int             testcontrols_mousespeed;\n\n\n\n\n// This one is related to the 3-screen display mode.\n// ANG90 = left side, ANG270 = right\nextern  int\tviewangleoffset;\n\n// Player taking events, and displaying.\nextern  int\tconsoleplayer;\t\nextern  int\tdisplayplayer;\n\n\n// -------------------------------------\n// Scores, rating.\n// Statistics on a given map, for intermission.\n//\nextern  int\ttotalkills;\nextern\tint\ttotalitems;\nextern\tint\ttotalsecret;\n\n// Timer, for scores.\nextern  int\tlevelstarttic;\t// gametic at level start\nextern  int\tleveltime;\t// tics in game play for par\n\n\n\n// --------------------------------------\n// DEMO playback/recording related stuff.\n// No demo, there is a human player in charge?\n// Disable save/end game?\nextern  boolean\tusergame;\n\n//?\nextern  boolean\tdemoplayback;\nextern  boolean\tdemorecording;\n\n// Round angleturn in ticcmds to the nearest 256.  This is used when\n// recording Vanilla demos in netgames.\n\nextern boolean lowres_turn;\n\n// Quit after playing a demo from cmdline.\nextern  boolean\t\tsingledemo;\t\n\n\n\n\n//?\nextern  gamestate_t     gamestate;\n\n\n\n\n\n\n//-----------------------------\n// Internal parameters, fixed.\n// These are set by the engine, and not changed\n//  according to user inputs. Partly load from\n//  WAD, partly set at startup time.\n\n\n\n// Bookkeeping on players - state.\nextern\tplayer_t\tplayers[MAXPLAYERS];\n\n// Alive? Disconnected?\nextern  boolean\t\tplayeringame[MAXPLAYERS];\n\n\n// Player spawn spots for deathmatch.\n#define MAX_DM_STARTS   10\nextern  mapthing_t      deathmatchstarts[MAX_DM_STARTS];\nextern  mapthing_t*\tdeathmatch_p;\n\n// Player spawn spots.\nextern  mapthing_t      playerstarts[MAXPLAYERS];\n\n// Intermission stats.\n// Parameters for world map / intermission.\nextern  wbstartstruct_t\t\twminfo;\t\n\n\n\n\n\n\n\n//-----------------------------------------\n// Internal parameters, used for engine.\n//\n\n// File handling stuff.\nextern  char *          savegamedir;\nextern\tchar\t\tbasedefault[1024];\n\n// if true, load all graphics at level load\nextern  boolean         precache;\n\n\n// wipegamestate can be set to -1\n//  to force a wipe on the next draw\nextern  gamestate_t     wipegamestate;\n\nextern  int             mouseSensitivity;\n\nextern  int             bodyqueslot;\n\n\n\n// Needed to store the number of the dummy sky flat.\n// Used for rendering,\n//  as well as tracking projectiles etc.\nextern int\t\tskyflatnum;\n\n\n\n// Netgame stuff (buffers and pointers, i.e. indices).\n\n\nextern\tint\t\trndindex;\n\nextern  ticcmd_t       *netcmds;\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/doomtype.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tSimple basic typedefs, isolated here to make it easier\n//\t separating modules.\n//    \n\n\n#ifndef __DOOMTYPE__\n#define __DOOMTYPE__\n\n// #define macros to provide functions missing in Windows.\n// Outside Windows, we use strings.h for str[n]casecmp.\n\n\n#ifdef _WIN32\n\n#define strcasecmp stricmp\n#define strncasecmp strnicmp\n\n#else\n\n#include <strings.h>\n\n#endif\n\n\n//\n// The packed attribute forces structures to be packed into the minimum \n// space necessary.  If this is not done, the compiler may align structure\n// fields differently to optimize memory access, inflating the overall\n// structure size.  It is important to use the packed attribute on certain\n// structures where alignment is important, particularly data read/written\n// to disk.\n//\n\n#ifdef __GNUC__\n#define PACKEDATTR __attribute__((packed))\n#else\n#define PACKEDATTR\n#endif\n\n// C99 integer types; with gcc we just use this.  Other compilers \n// should add conditional statements that define the C99 types.\n\n// What is really wanted here is stdint.h; however, some old versions\n// of Solaris don't have stdint.h and only have inttypes.h (the \n// pre-standardisation version).  inttypes.h is also in the C99 \n// standard and defined to include stdint.h, so include this. \n\n#include <inttypes.h>\n\n#ifdef __cplusplus\n\n// Use builtin bool type with C++.\n\ntypedef bool boolean;\n\n#else\n\ntypedef enum \n{\n    false\t= 0,\n    true\t= 1,\n\tundef\t= 0xFFFFFFFF\n} boolean;\n\n#endif\n\ntypedef uint8_t byte;\n\n#include <limits.h>\n\n#ifdef _WIN32\n\n#define DIR_SEPARATOR '\\\\'\n#define DIR_SEPARATOR_S \"\\\\\"\n#define PATH_SEPARATOR ';'\n\n#else\n\n#define DIR_SEPARATOR '/'\n#define DIR_SEPARATOR_S \"/\"\n#define PATH_SEPARATOR ':'\n\n#endif\n\n#define arrlen(array) (sizeof(array) / sizeof(*array))\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/dstrings.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tGlobally defined strings.\n// \n\n\n\n#include \"dstrings.h\"\n\nchar *doom1_endmsg[] =\n{\n  \"are you sure you want to\\nquit this great game?\",\n  \"please don't leave, there's more\\ndemons to toast!\",\n  \"let's beat it -- this is turning\\ninto a bloodbath!\",\n  \"i wouldn't leave if i were you.\\ndos is much worse.\",\n  \"you're trying to say you like dos\\nbetter than me, right?\",\n  \"don't leave yet -- there's a\\ndemon around that corner!\",\n  \"ya know, next time you come in here\\ni'm gonna toast ya.\",\n  \"go ahead and leave. see if i care.\",\n};\n\nchar *doom2_endmsg[] =\n{\n  // QuitDOOM II messages\n  \"are you sure you want to\\nquit this great game?\",\n  \"you want to quit?\\nthen, thou hast lost an eighth!\",\n  \"don't go now, there's a \\ndimensional shambler waiting\\nat the dos prompt!\",\n  \"get outta here and go back\\nto your boring programs.\",\n  \"if i were your boss, i'd \\n deathmatch ya in a minute!\",\n  \"look, bud. you leave now\\nand you forfeit your body count!\",\n  \"just leave. when you come\\nback, i'll be waiting with a bat.\",\n  \"you're lucky i don't smack\\nyou for thinking about leaving.\",\n};\n\n#if 0\n\n// UNUSED messages included in the source release\n\nchar* endmsg[] =\n{\n  // DOOM1\n  QUITMSG,\n  // FinalDOOM?\n  \"fuck you, pussy!\\nget the fuck out!\",\n  \"you quit and i'll jizz\\nin your cystholes!\",\n  \"if you leave, i'll make\\nthe lord drink my jizz.\",\n  \"hey, ron! can we say\\n'fuck' in the game?\",\n  \"i'd leave: this is just\\nmore monsters and levels.\\nwhat a load.\",\n  \"suck it down, asshole!\\nyou're a fucking wimp!\",\n  \"don't quit now! we're \\nstill spending your money!\",\n\n  // Internal debug. Different style, too.\n  \"THIS IS NO MESSAGE!\\nPage intentionally left blank.\"\n};\n\n#endif\n\n  \n\n\n"
  },
  {
    "path": "fbdoom/dstrings.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n//\n// DESCRIPTION:\n//\tDOOM strings, by language.\n//\n\n\n#ifndef __DSTRINGS__\n#define __DSTRINGS__\n\n\n// All important printed strings.\n\n#include \"d_englsh.h\"\n\n// Misc. other strings.\n#define SAVEGAMENAME\t\"doomsav\"\n\n\n// QuitDOOM messages\n// 8 per each game type\n#define NUM_QUITMESSAGES   8\n\nextern char *doom1_endmsg[];\nextern char *doom2_endmsg[];\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/dummy.c",
    "content": "/*\n * dummy.c\n *\n *  Created on: 16.02.2015\n *      Author: Florian\n */\n\n\n/*---------------------------------------------------------------------*\n *  include files                                                      *\n *---------------------------------------------------------------------*/\n\n#include \"doomtype.h\"\n\n/*---------------------------------------------------------------------*\n *  local definitions                                                  *\n *---------------------------------------------------------------------*/\n\n/*---------------------------------------------------------------------*\n *  external declarations                                              *\n *---------------------------------------------------------------------*/\n\n/*---------------------------------------------------------------------*\n *  public data                                                        *\n *---------------------------------------------------------------------*/\n\nboolean net_client_connected = false;\n\nboolean drone = false;\n\n/*---------------------------------------------------------------------*\n *  private data                                                       *\n *---------------------------------------------------------------------*/\n\n/*---------------------------------------------------------------------*\n *  private functions                                                  *\n *---------------------------------------------------------------------*/\n\n/*---------------------------------------------------------------------*\n *  public functions                                                   *\n *---------------------------------------------------------------------*/\n\nvoid I_InitTimidityConfig(void)\n{\n}\n\n/*---------------------------------------------------------------------*\n *  eof                                                                *\n *---------------------------------------------------------------------*/\n"
  },
  {
    "path": "fbdoom/f_finale.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tGame completion, final screen animation.\n//\n\n\n#include <stdio.h>\n#include <ctype.h>\n\n// Functions.\n#include \"deh_main.h\"\n#include \"i_system.h\"\n#include \"i_swap.h\"\n#include \"z_zone.h\"\n#include \"v_video.h\"\n#include \"w_wad.h\"\n#include \"s_sound.h\"\n\n// Data.\n#include \"d_main.h\"\n#include \"dstrings.h\"\n#include \"sounds.h\"\n\n#include \"doomstat.h\"\n#include \"r_state.h\"\n\ntypedef enum\n{\n    F_STAGE_TEXT,\n    F_STAGE_ARTSCREEN,\n    F_STAGE_CAST,\n} finalestage_t;\n\n// ?\n//#include \"doomstat.h\"\n//#include \"r_local.h\"\n//#include \"f_finale.h\"\n\n// Stage of animation:\nfinalestage_t finalestage;\n\nunsigned int finalecount;\n\n#define\tTEXTSPEED\t3\n#define\tTEXTWAIT\t250\n\ntypedef struct\n{\n    GameMission_t mission;\n    int episode, level;\n    char *background;\n    char *text;\n} textscreen_t;\n\nstatic textscreen_t textscreens[] =\n{\n    { doom,      1, 8,  \"FLOOR4_8\",  E1TEXT},\n    { doom,      2, 8,  \"SFLR6_1\",   E2TEXT},\n    { doom,      3, 8,  \"MFLR8_4\",   E3TEXT},\n    { doom,      4, 8,  \"MFLR8_3\",   E4TEXT},\n\n    { doom2,     1, 6,  \"SLIME16\",   C1TEXT},\n    { doom2,     1, 11, \"RROCK14\",   C2TEXT},\n    { doom2,     1, 20, \"RROCK07\",   C3TEXT},\n    { doom2,     1, 30, \"RROCK17\",   C4TEXT},\n    { doom2,     1, 15, \"RROCK13\",   C5TEXT},\n    { doom2,     1, 31, \"RROCK19\",   C6TEXT},\n\n    { pack_tnt,  1, 6,  \"SLIME16\",   T1TEXT},\n    { pack_tnt,  1, 11, \"RROCK14\",   T2TEXT},\n    { pack_tnt,  1, 20, \"RROCK07\",   T3TEXT},\n    { pack_tnt,  1, 30, \"RROCK17\",   T4TEXT},\n    { pack_tnt,  1, 15, \"RROCK13\",   T5TEXT},\n    { pack_tnt,  1, 31, \"RROCK19\",   T6TEXT},\n\n    { pack_plut, 1, 6,  \"SLIME16\",   P1TEXT},\n    { pack_plut, 1, 11, \"RROCK14\",   P2TEXT},\n    { pack_plut, 1, 20, \"RROCK07\",   P3TEXT},\n    { pack_plut, 1, 30, \"RROCK17\",   P4TEXT},\n    { pack_plut, 1, 15, \"RROCK13\",   P5TEXT},\n    { pack_plut, 1, 31, \"RROCK19\",   P6TEXT},\n};\n\nchar*\tfinaletext;\nchar*\tfinaleflat;\n\nvoid\tF_StartCast (void);\nvoid\tF_CastTicker (void);\nboolean F_CastResponder (event_t *ev);\nvoid\tF_CastDrawer (void);\n\n//\n// F_StartFinale\n//\nvoid F_StartFinale (void)\n{\n    size_t i;\n\n    gameaction = ga_nothing;\n    gamestate = GS_FINALE;\n    viewactive = false;\n    automapactive = false;\n\n    if (logical_gamemission == doom)\n    {\n        S_ChangeMusic(mus_victor, true);\n    }\n    else\n    {\n        S_ChangeMusic(mus_read_m, true);\n    }\n\n    // Find the right screen and set the text and background\n\n    for (i=0; i<arrlen(textscreens); ++i)\n    {\n        textscreen_t *screen = &textscreens[i];\n\n        // Hack for Chex Quest\n\n        if (gameversion == exe_chex && screen->mission == doom)\n        {\n            screen->level = 5;\n        }\n\n        if (logical_gamemission == screen->mission\n         && (logical_gamemission != doom || gameepisode == screen->episode)\n         && gamemap == screen->level)\n        {\n            finaletext = screen->text;\n            finaleflat = screen->background;\n        }\n    }\n\n    // Do dehacked substitutions of strings\n  \n    finaletext = DEH_String(finaletext);\n    finaleflat = DEH_String(finaleflat);\n    \n    finalestage = F_STAGE_TEXT;\n    finalecount = 0;\n\t\n}\n\n\n\nboolean F_Responder (event_t *event)\n{\n    if (finalestage == F_STAGE_CAST)\n\treturn F_CastResponder (event);\n\t\n    return false;\n}\n\n\n//\n// F_Ticker\n//\nvoid F_Ticker (void)\n{\n    size_t\t\ti;\n    \n    // check for skipping\n    if ( (gamemode == commercial)\n      && ( finalecount > 50) )\n    {\n      // go on to the next level\n      for (i=0 ; i<MAXPLAYERS ; i++)\n\tif (players[i].cmd.buttons)\n\t  break;\n\t\t\t\t\n      if (i < MAXPLAYERS)\n      {\t\n\tif (gamemap == 30)\n\t  F_StartCast ();\n\telse\n\t  gameaction = ga_worlddone;\n      }\n    }\n    \n    // advance animation\n    finalecount++;\n\t\n    if (finalestage == F_STAGE_CAST)\n    {\n\tF_CastTicker ();\n\treturn;\n    }\n\t\n    if ( gamemode == commercial)\n\treturn;\n\t\t\n    if (finalestage == F_STAGE_TEXT\n     && finalecount>strlen (finaletext)*TEXTSPEED + TEXTWAIT)\n    {\n\tfinalecount = 0;\n\tfinalestage = F_STAGE_ARTSCREEN;\n\twipegamestate = -1;\t\t// force a wipe\n\tif (gameepisode == 3)\n\t    S_StartMusic (mus_bunny);\n    }\n}\n\n\n\n//\n// F_TextWrite\n//\n\n#include \"hu_stuff.h\"\nextern\tpatch_t *hu_font[HU_FONTSIZE];\n\n\nvoid F_TextWrite (void)\n{\n    byte*\tsrc;\n    byte*\tdest;\n    \n    int\t\tx,y,w;\n    signed int\tcount;\n    char*\tch;\n    int\t\tc;\n    int\t\tcx;\n    int\t\tcy;\n    \n    // erase the entire screen to a tiled background\n    src = W_CacheLumpName ( finaleflat , PU_CACHE);\n    dest = I_VideoBuffer;\n\t\n    for (y=0 ; y<SCREENHEIGHT ; y++)\n    {\n\tfor (x=0 ; x<SCREENWIDTH/64 ; x++)\n\t{\n\t    memcpy (dest, src+((y&63)<<6), 64);\n\t    dest += 64;\n\t}\n\tif (SCREENWIDTH&63)\n\t{\n\t    memcpy (dest, src+((y&63)<<6), SCREENWIDTH&63);\n\t    dest += (SCREENWIDTH&63);\n\t}\n    }\n\n    V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);\n    \n    // draw some of the text onto the screen\n    cx = 10;\n    cy = 10;\n    ch = finaletext;\n\t\n    count = ((signed int) finalecount - 10) / TEXTSPEED;\n    if (count < 0)\n\tcount = 0;\n    for ( ; count ; count-- )\n    {\n\tc = *ch++;\n\tif (!c)\n\t    break;\n\tif (c == '\\n')\n\t{\n\t    cx = 10;\n\t    cy += 11;\n\t    continue;\n\t}\n\t\t\n\tc = toupper(c) - HU_FONTSTART;\n\tif (c < 0 || c> HU_FONTSIZE)\n\t{\n\t    cx += 4;\n\t    continue;\n\t}\n\t\t\n\tw = SHORT (hu_font[c]->width);\n\tif (cx+w > SCREENWIDTH)\n\t    break;\n\tV_DrawPatch(cx, cy, hu_font[c]);\n\tcx+=w;\n    }\n\t\n}\n\n//\n// Final DOOM 2 animation\n// Casting by id Software.\n//   in order of appearance\n//\ntypedef struct\n{\n    char\t\t*name;\n    mobjtype_t\ttype;\n} castinfo_t;\n\ncastinfo_t\tcastorder[] = {\n    {CC_ZOMBIE, MT_POSSESSED},\n    {CC_SHOTGUN, MT_SHOTGUY},\n    {CC_HEAVY, MT_CHAINGUY},\n    {CC_IMP, MT_TROOP},\n    {CC_DEMON, MT_SERGEANT},\n    {CC_LOST, MT_SKULL},\n    {CC_CACO, MT_HEAD},\n    {CC_HELL, MT_KNIGHT},\n    {CC_BARON, MT_BRUISER},\n    {CC_ARACH, MT_BABY},\n    {CC_PAIN, MT_PAIN},\n    {CC_REVEN, MT_UNDEAD},\n    {CC_MANCU, MT_FATSO},\n    {CC_ARCH, MT_VILE},\n    {CC_SPIDER, MT_SPIDER},\n    {CC_CYBER, MT_CYBORG},\n    {CC_HERO, MT_PLAYER},\n\n    {NULL,0}\n};\n\nint\t\tcastnum;\nint\t\tcasttics;\nstate_t*\tcaststate;\nboolean\t\tcastdeath;\nint\t\tcastframes;\nint\t\tcastonmelee;\nboolean\t\tcastattacking;\n\n\n//\n// F_StartCast\n//\nvoid F_StartCast (void)\n{\n    wipegamestate = -1;\t\t// force a screen wipe\n    castnum = 0;\n    caststate = &states[mobjinfo[castorder[castnum].type].seestate];\n    casttics = caststate->tics;\n    castdeath = false;\n    finalestage = F_STAGE_CAST;\n    castframes = 0;\n    castonmelee = 0;\n    castattacking = false;\n    S_ChangeMusic(mus_evil, true);\n}\n\n\n//\n// F_CastTicker\n//\nvoid F_CastTicker (void)\n{\n    int\t\tst;\n    int\t\tsfx;\n\t\n    if (--casttics > 0)\n\treturn;\t\t\t// not time to change state yet\n\t\t\n    if (caststate->tics == -1 || caststate->nextstate == S_NULL)\n    {\n\t// switch from deathstate to next monster\n\tcastnum++;\n\tcastdeath = false;\n\tif (castorder[castnum].name == NULL)\n\t    castnum = 0;\n\tif (mobjinfo[castorder[castnum].type].seesound)\n\t    S_StartSound (NULL, mobjinfo[castorder[castnum].type].seesound);\n\tcaststate = &states[mobjinfo[castorder[castnum].type].seestate];\n\tcastframes = 0;\n    }\n    else\n    {\n\t// just advance to next state in animation\n\tif (caststate == &states[S_PLAY_ATK1])\n\t    goto stopattack;\t// Oh, gross hack!\n\tst = caststate->nextstate;\n\tcaststate = &states[st];\n\tcastframes++;\n\t\n\t// sound hacks....\n\tswitch (st)\n\t{\n\t  case S_PLAY_ATK1:\tsfx = sfx_dshtgn; break;\n\t  case S_POSS_ATK2:\tsfx = sfx_pistol; break;\n\t  case S_SPOS_ATK2:\tsfx = sfx_shotgn; break;\n\t  case S_VILE_ATK2:\tsfx = sfx_vilatk; break;\n\t  case S_SKEL_FIST2:\tsfx = sfx_skeswg; break;\n\t  case S_SKEL_FIST4:\tsfx = sfx_skepch; break;\n\t  case S_SKEL_MISS2:\tsfx = sfx_skeatk; break;\n\t  case S_FATT_ATK8:\n\t  case S_FATT_ATK5:\n\t  case S_FATT_ATK2:\tsfx = sfx_firsht; break;\n\t  case S_CPOS_ATK2:\n\t  case S_CPOS_ATK3:\n\t  case S_CPOS_ATK4:\tsfx = sfx_shotgn; break;\n\t  case S_TROO_ATK3:\tsfx = sfx_claw; break;\n\t  case S_SARG_ATK2:\tsfx = sfx_sgtatk; break;\n\t  case S_BOSS_ATK2:\n\t  case S_BOS2_ATK2:\n\t  case S_HEAD_ATK2:\tsfx = sfx_firsht; break;\n\t  case S_SKULL_ATK2:\tsfx = sfx_sklatk; break;\n\t  case S_SPID_ATK2:\n\t  case S_SPID_ATK3:\tsfx = sfx_shotgn; break;\n\t  case S_BSPI_ATK2:\tsfx = sfx_plasma; break;\n\t  case S_CYBER_ATK2:\n\t  case S_CYBER_ATK4:\n\t  case S_CYBER_ATK6:\tsfx = sfx_rlaunc; break;\n\t  case S_PAIN_ATK3:\tsfx = sfx_sklatk; break;\n\t  default: sfx = 0; break;\n\t}\n\t\t\n\tif (sfx)\n\t    S_StartSound (NULL, sfx);\n    }\n\t\n    if (castframes == 12)\n    {\n\t// go into attack frame\n\tcastattacking = true;\n\tif (castonmelee)\n\t    caststate=&states[mobjinfo[castorder[castnum].type].meleestate];\n\telse\n\t    caststate=&states[mobjinfo[castorder[castnum].type].missilestate];\n\tcastonmelee ^= 1;\n\tif (caststate == &states[S_NULL])\n\t{\n\t    if (castonmelee)\n\t\tcaststate=\n\t\t    &states[mobjinfo[castorder[castnum].type].meleestate];\n\t    else\n\t\tcaststate=\n\t\t    &states[mobjinfo[castorder[castnum].type].missilestate];\n\t}\n    }\n\t\n    if (castattacking)\n    {\n\tif (castframes == 24\n\t    ||\tcaststate == &states[mobjinfo[castorder[castnum].type].seestate] )\n\t{\n\t  stopattack:\n\t    castattacking = false;\n\t    castframes = 0;\n\t    caststate = &states[mobjinfo[castorder[castnum].type].seestate];\n\t}\n    }\n\t\n    casttics = caststate->tics;\n    if (casttics == -1)\n\tcasttics = 15;\n}\n\n\n//\n// F_CastResponder\n//\n\nboolean F_CastResponder (event_t* ev)\n{\n    if (ev->type != ev_keydown)\n\treturn false;\n\t\t\n    if (castdeath)\n\treturn true;\t\t\t// already in dying frames\n\t\t\n    // go into death frame\n    castdeath = true;\n    caststate = &states[mobjinfo[castorder[castnum].type].deathstate];\n    casttics = caststate->tics;\n    castframes = 0;\n    castattacking = false;\n    if (mobjinfo[castorder[castnum].type].deathsound)\n\tS_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound);\n\t\n    return true;\n}\n\n\nvoid F_CastPrint (char* text)\n{\n    char*\tch;\n    int\t\tc;\n    int\t\tcx;\n    int\t\tw;\n    int\t\twidth;\n    \n    // find width\n    ch = text;\n    width = 0;\n\t\n    while (ch)\n    {\n\tc = *ch++;\n\tif (!c)\n\t    break;\n\tc = toupper(c) - HU_FONTSTART;\n\tif (c < 0 || c> HU_FONTSIZE)\n\t{\n\t    width += 4;\n\t    continue;\n\t}\n\t\t\n\tw = SHORT (hu_font[c]->width);\n\twidth += w;\n    }\n    \n    // draw it\n    cx = 160-width/2;\n    ch = text;\n    while (ch)\n    {\n\tc = *ch++;\n\tif (!c)\n\t    break;\n\tc = toupper(c) - HU_FONTSTART;\n\tif (c < 0 || c> HU_FONTSIZE)\n\t{\n\t    cx += 4;\n\t    continue;\n\t}\n\t\t\n\tw = SHORT (hu_font[c]->width);\n\tV_DrawPatch(cx, 180, hu_font[c]);\n\tcx+=w;\n    }\n\t\n}\n\n\n//\n// F_CastDrawer\n//\n\nvoid F_CastDrawer (void)\n{\n    spritedef_t*\tsprdef;\n    spriteframe_t*\tsprframe;\n    int\t\t\tlump;\n    boolean\t\tflip;\n    patch_t*\t\tpatch;\n    \n    // erase the entire screen to a background\n    V_DrawPatch (0, 0, W_CacheLumpName (DEH_String(\"BOSSBACK\"), PU_CACHE));\n\n    F_CastPrint (DEH_String(castorder[castnum].name));\n    \n    // draw the current frame in the middle of the screen\n    sprdef = &sprites[caststate->sprite];\n    sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK];\n    lump = sprframe->lump[0];\n    flip = (boolean)sprframe->flip[0];\n\t\t\t\n    patch = W_CacheLumpNum (lump+firstspritelump, PU_CACHE);\n    if (flip)\n\tV_DrawPatchFlipped(160, 170, patch);\n    else\n\tV_DrawPatch(160, 170, patch);\n}\n\n\n//\n// F_DrawPatchCol\n//\nvoid\nF_DrawPatchCol\n( int\t\tx,\n  patch_t*\tpatch,\n  int\t\tcol )\n{\n    column_t*\tcolumn;\n    byte*\tsource;\n    byte*\tdest;\n    byte*\tdesttop;\n    int\t\tcount;\n\t\n    column = (column_t *)((byte *)patch + LONG(patch->columnofs[col]));\n    desttop = I_VideoBuffer + x;\n\n    // step through the posts in a column\n    while (column->topdelta != 0xff )\n    {\n\tsource = (byte *)column + 3;\n\tdest = desttop + column->topdelta*SCREENWIDTH;\n\tcount = column->length;\n\t\t\n\twhile (count--)\n\t{\n\t    *dest = *source++;\n\t    dest += SCREENWIDTH;\n\t}\n\tcolumn = (column_t *)(  (byte *)column + column->length + 4 );\n    }\n}\n\n\n//\n// F_BunnyScroll\n//\nvoid F_BunnyScroll (void)\n{\n    signed int  scrolled;\n    int\t\tx;\n    patch_t*\tp1;\n    patch_t*\tp2;\n    char\tname[10];\n    int\t\tstage;\n    static int\tlaststage;\n\t\t\n    p1 = W_CacheLumpName (DEH_String(\"PFUB2\"), PU_LEVEL);\n    p2 = W_CacheLumpName (DEH_String(\"PFUB1\"), PU_LEVEL);\n\n    V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);\n\t\n    scrolled = (320 - ((signed int) finalecount-230)/2);\n    if (scrolled > 320)\n\tscrolled = 320;\n    if (scrolled < 0)\n\tscrolled = 0;\n\t\t\n    for ( x=0 ; x<SCREENWIDTH ; x++)\n    {\n\tif (x+scrolled < 320)\n\t    F_DrawPatchCol (x, p1, x+scrolled);\n\telse\n\t    F_DrawPatchCol (x, p2, x+scrolled - 320);\t\t\n    }\n\t\n    if (finalecount < 1130)\n\treturn;\n    if (finalecount < 1180)\n    {\n        V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,\n                    (SCREENHEIGHT - 8 * 8) / 2, \n                    W_CacheLumpName(DEH_String(\"END0\"), PU_CACHE));\n\tlaststage = 0;\n\treturn;\n    }\n\t\n    stage = (finalecount-1180) / 5;\n    if (stage > 6)\n\tstage = 6;\n    if (stage > laststage)\n    {\n\tS_StartSound (NULL, sfx_pistol);\n\tlaststage = stage;\n    }\n\t\n    DEH_snprintf(name, 10, \"END%i\", stage);\n    V_DrawPatch((SCREENWIDTH - 13 * 8) / 2, \n                (SCREENHEIGHT - 8 * 8) / 2, \n                W_CacheLumpName (name,PU_CACHE));\n}\n\nstatic void F_ArtScreenDrawer(void)\n{\n    char *lumpname;\n    \n    if (gameepisode == 3)\n    {\n        F_BunnyScroll();\n    }\n    else\n    {\n        switch (gameepisode)\n        {\n            case 1:\n                if (gamemode == retail)\n                {\n                    lumpname = \"CREDIT\";\n                }\n                else\n                {\n                    lumpname = \"HELP2\";\n                }\n                break;\n            case 2:\n                lumpname = \"VICTORY2\";\n                break;\n            case 4:\n                lumpname = \"ENDPIC\";\n                break;\n            default:\n                return;\n        }\n\n        lumpname = DEH_String(lumpname);\n\n        V_DrawPatch (0, 0, W_CacheLumpName(lumpname, PU_CACHE));\n    }\n}\n\n//\n// F_Drawer\n//\nvoid F_Drawer (void)\n{\n    switch (finalestage)\n    {\n        case F_STAGE_CAST:\n            F_CastDrawer();\n            break;\n        case F_STAGE_TEXT:\n            F_TextWrite();\n            break;\n        case F_STAGE_ARTSCREEN:\n            F_ArtScreenDrawer();\n            break;\n    }\n}\n\n\n"
  },
  {
    "path": "fbdoom/f_finale.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\n//    \n\n\n#ifndef __F_FINALE__\n#define __F_FINALE__\n\n\n#include \"doomtype.h\"\n#include \"d_event.h\"\n//\n// FINALE\n//\n\n// Called by main loop.\nboolean F_Responder (event_t* ev);\n\n// Called by main loop.\nvoid F_Ticker (void);\n\n// Called by main loop.\nvoid F_Drawer (void);\n\n\nvoid F_StartFinale (void);\n\n\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/f_wipe.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tMission begin melt/wipe screen special effect.\n//\n\n#include <string.h>\n\n#include \"z_zone.h\"\n#include \"i_video.h\"\n#include \"v_video.h\"\n#include \"m_random.h\"\n\n#include \"doomtype.h\"\n\n#include \"f_wipe.h\"\n\n//\n//                       SCREEN WIPE PACKAGE\n//\n\n// when zero, stop the wipe\nstatic boolean\tgo = 0;\n\nstatic byte*\twipe_scr_start;\nstatic byte*\twipe_scr_end;\nstatic byte*\twipe_scr;\n\n\nvoid\nwipe_shittyColMajorXform\n( short*\tarray,\n  int\t\twidth,\n  int\t\theight )\n{\n    int\t\tx;\n    int\t\ty;\n    short*\tdest;\n\n    dest = (short*) Z_Malloc(width*height*2, PU_STATIC, 0);\n\n    for(y=0;y<height;y++)\n\tfor(x=0;x<width;x++)\n\t    dest[x*height+y] = array[y*width+x];\n\n    memcpy(array, dest, width*height*2);\n\n    Z_Free(dest);\n\n}\n\nint\nwipe_initColorXForm\n( int\twidth,\n  int\theight,\n  int\tticks )\n{\n    memcpy(wipe_scr, wipe_scr_start, width*height);\n    return 0;\n}\n\nint\nwipe_doColorXForm\n( int\twidth,\n  int\theight,\n  int\tticks )\n{\n    boolean\tchanged;\n    byte*\tw;\n    byte*\te;\n    int\t\tnewval;\n\n    changed = false;\n    w = wipe_scr;\n    e = wipe_scr_end;\n    \n    while (w!=wipe_scr+width*height)\n    {\n\tif (*w != *e)\n\t{\n\t    if (*w > *e)\n\t    {\n\t\tnewval = *w - ticks;\n\t\tif (newval < *e)\n\t\t    *w = *e;\n\t\telse\n\t\t    *w = newval;\n\t\tchanged = true;\n\t    }\n\t    else if (*w < *e)\n\t    {\n\t\tnewval = *w + ticks;\n\t\tif (newval > *e)\n\t\t    *w = *e;\n\t\telse\n\t\t    *w = newval;\n\t\tchanged = true;\n\t    }\n\t}\n\tw++;\n\te++;\n    }\n\n    return !changed;\n\n}\n\nint\nwipe_exitColorXForm\n( int\twidth,\n  int\theight,\n  int\tticks )\n{\n    return 0;\n}\n\n\nstatic int*\ty;\n\nint\nwipe_initMelt\n( int\twidth,\n  int\theight,\n  int\tticks )\n{\n    int i, r;\n    \n    // copy start screen to main screen\n    memcpy(wipe_scr, wipe_scr_start, width*height);\n    \n    // makes this wipe faster (in theory)\n    // to have stuff in column-major format\n    wipe_shittyColMajorXform((short*)wipe_scr_start, width/2, height);\n    wipe_shittyColMajorXform((short*)wipe_scr_end, width/2, height);\n    \n    // setup initial column positions\n    // (y<0 => not ready to scroll yet)\n    y = (int *) Z_Malloc(width*sizeof(int), PU_STATIC, 0);\n    y[0] = -(M_Random()%16);\n    for (i=1;i<width;i++)\n    {\n\tr = (M_Random()%3) - 1;\n\ty[i] = y[i-1] + r;\n\tif (y[i] > 0) y[i] = 0;\n\telse if (y[i] == -16) y[i] = -15;\n    }\n\n    return 0;\n}\n\nint\nwipe_doMelt\n( int\twidth,\n  int\theight,\n  int\tticks )\n{\n    int\t\ti;\n    int\t\tj;\n    int\t\tdy;\n    int\t\tidx;\n    \n    short*\ts;\n    short*\td;\n    boolean\tdone = true;\n\n    width/=2;\n\n    while (ticks--)\n    {\n\tfor (i=0;i<width;i++)\n\t{\n\t    if (y[i]<0)\n\t    {\n\t\ty[i]++; done = false;\n\t    }\n\t    else if (y[i] < height)\n\t    {\n\t\tdy = (y[i] < 16) ? y[i]+1 : 8;\n\t\tif (y[i]+dy >= height) dy = height - y[i];\n\t\ts = &((short *)wipe_scr_end)[i*height+y[i]];\n\t\td = &((short *)wipe_scr)[y[i]*width+i];\n\t\tidx = 0;\n\t\tfor (j=dy;j;j--)\n\t\t{\n\t\t    d[idx] = *(s++);\n\t\t    idx += width;\n\t\t}\n\t\ty[i] += dy;\n\t\ts = &((short *)wipe_scr_start)[i*height];\n\t\td = &((short *)wipe_scr)[y[i]*width+i];\n\t\tidx = 0;\n\t\tfor (j=height-y[i];j;j--)\n\t\t{\n\t\t    d[idx] = *(s++);\n\t\t    idx += width;\n\t\t}\n\t\tdone = false;\n\t    }\n\t}\n    }\n\n    return done;\n\n}\n\nint\nwipe_exitMelt\n( int\twidth,\n  int\theight,\n  int\tticks )\n{\n    Z_Free(y);\n    Z_Free(wipe_scr_start);\n    Z_Free(wipe_scr_end);\n    return 0;\n}\n\nint\nwipe_StartScreen\n( int\tx,\n  int\ty,\n  int\twidth,\n  int\theight )\n{\n    wipe_scr_start = Z_Malloc(SCREENWIDTH * SCREENHEIGHT, PU_STATIC, NULL);\n    I_ReadScreen(wipe_scr_start);\n    return 0;\n}\n\nint\nwipe_EndScreen\n( int\tx,\n  int\ty,\n  int\twidth,\n  int\theight )\n{\n    wipe_scr_end = Z_Malloc(SCREENWIDTH * SCREENHEIGHT, PU_STATIC, NULL);\n    I_ReadScreen(wipe_scr_end);\n    V_DrawBlock(x, y, width, height, wipe_scr_start); // restore start scr.\n    return 0;\n}\n\nint\nwipe_ScreenWipe\n( int\twipeno,\n  int\tx,\n  int\ty,\n  int\twidth,\n  int\theight,\n  int\tticks )\n{\n    int rc;\n    static int (*wipes[])(int, int, int) =\n    {\n\twipe_initColorXForm, wipe_doColorXForm, wipe_exitColorXForm,\n\twipe_initMelt, wipe_doMelt, wipe_exitMelt\n    };\n\n    // initial stuff\n    if (!go)\n    {\n\tgo = 1;\n\t// wipe_scr = (byte *) Z_Malloc(width*height, PU_STATIC, 0); // DEBUG\n\twipe_scr = I_VideoBuffer;\n\t(*wipes[wipeno*3])(width, height, ticks);\n    }\n\n    // do a piece of wipe-in\n    V_MarkRect(0, 0, width, height);\n    rc = (*wipes[wipeno*3+1])(width, height, ticks);\n    //  V_DrawBlock(x, y, 0, width, height, wipe_scr); // DEBUG\n\n    // final stuff\n    if (rc)\n    {\n\tgo = 0;\n\t(*wipes[wipeno*3+2])(width, height, ticks);\n    }\n\n    return !go;\n}\n\n"
  },
  {
    "path": "fbdoom/f_wipe.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tMission start screen wipe/melt, special effects.\n//\t\n\n\n#ifndef __F_WIPE_H__\n#define __F_WIPE_H__\n\n//\n//                       SCREEN WIPE PACKAGE\n//\n\nenum\n{\n    // simple gradual pixel change for 8-bit only\n    wipe_ColorXForm,\n    \n    // weird screen melt\n    wipe_Melt,\t\n\n    wipe_NUMWIPES\n};\n\nint\nwipe_StartScreen\n( int\t\tx,\n  int\t\ty,\n  int\t\twidth,\n  int\t\theight );\n\n\nint\nwipe_EndScreen\n( int\t\tx,\n  int\t\ty,\n  int\t\twidth,\n  int\t\theight );\n\n\nint\nwipe_ScreenWipe\n( int\t\twipeno,\n  int\t\tx,\n  int\t\ty,\n  int\t\twidth,\n  int\t\theight,\n  int\t\tticks );\n\n#endif\n"
  },
  {
    "path": "fbdoom/g_game.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:  none\n//\n\n\n\n#include <string.h>\n#include <stdlib.h>\n#include <math.h>\n\n#include \"doomdef.h\" \n#include \"doomkeys.h\"\n#include \"doomstat.h\"\n\n#include \"deh_main.h\"\n#include \"deh_misc.h\"\n\n#include \"z_zone.h\"\n#include \"f_finale.h\"\n#include \"m_argv.h\"\n#include \"m_controls.h\"\n#include \"m_misc.h\"\n#include \"m_menu.h\"\n#include \"m_random.h\"\n#include \"i_system.h\"\n#include \"i_timer.h\"\n#include \"i_video.h\"\n\n#include \"p_setup.h\"\n#include \"p_saveg.h\"\n#include \"p_tick.h\"\n\n#include \"d_main.h\"\n\n#include \"wi_stuff.h\"\n#include \"hu_stuff.h\"\n#include \"st_stuff.h\"\n#include \"am_map.h\"\n#include \"statdump.h\"\n\n// Needs access to LFB.\n#include \"v_video.h\"\n\n#include \"w_wad.h\"\n\n#include \"p_local.h\" \n\n#include \"s_sound.h\"\n\n// Data.\n#include \"dstrings.h\"\n#include \"sounds.h\"\n\n// SKY handling - still the wrong place.\n#include \"r_data.h\"\n#include \"r_sky.h\"\n\n\n\n#include \"g_game.h\"\n\n\n#define SAVEGAMESIZE\t0x2c000\n\nvoid\tG_ReadDemoTiccmd (ticcmd_t* cmd); \nvoid\tG_WriteDemoTiccmd (ticcmd_t* cmd); \nvoid\tG_PlayerReborn (int player); \n \nvoid\tG_DoReborn (int playernum); \n \nvoid\tG_DoLoadLevel (void); \nvoid\tG_DoNewGame (void); \nvoid\tG_DoPlayDemo (void); \nvoid\tG_DoCompleted (void); \nvoid\tG_DoVictory (void); \nvoid\tG_DoWorldDone (void); \nvoid\tG_DoSaveGame (void); \n \n// Gamestate the last time G_Ticker was called.\n\ngamestate_t     oldgamestate; \n \ngameaction_t    gameaction; \ngamestate_t     gamestate; \nskill_t         gameskill; \nboolean\t\trespawnmonsters;\nint             gameepisode; \nint             gamemap; \n\n// If non-zero, exit the level after this number of minutes.\n\nint             timelimit;\n\nboolean         paused; \nboolean         sendpause;             \t// send a pause event next tic \nboolean         sendsave;             \t// send a save event next tic \nboolean         usergame;               // ok to save / end game \n \nboolean         timingdemo;             // if true, exit with report on completion \nboolean         nodrawers;              // for comparative timing purposes \nint             starttime;          \t// for comparative timing purposes  \t \n \nboolean         viewactive; \n \nint             deathmatch;           \t// only if started as net death \nboolean         netgame;                // only true if packets are broadcast \nboolean         playeringame[MAXPLAYERS]; \nplayer_t        players[MAXPLAYERS]; \n\nboolean         turbodetected[MAXPLAYERS];\n \nint             consoleplayer;          // player taking events and displaying \nint             displayplayer;          // view being displayed \nint             levelstarttic;          // gametic at level start \nint             totalkills, totalitems, totalsecret;    // for intermission \n \nchar           *demoname;\nboolean         demorecording; \nboolean         longtics;               // cph's doom 1.91 longtics hack\nboolean         lowres_turn;            // low resolution turning for longtics\nboolean         demoplayback; \nboolean\t\tnetdemo; \nbyte*\t\tdemobuffer;\nbyte*\t\tdemo_p;\nbyte*\t\tdemoend; \nboolean         singledemo;            \t// quit after playing a demo from cmdline \n \nboolean         precache = true;        // if true, load all graphics at start \n\nboolean         testcontrols = false;    // Invoked by setup to test controls\nint             testcontrols_mousespeed;\n \n\n \nwbstartstruct_t wminfo;               \t// parms for world map / intermission \n \nbyte\t\tconsistancy[MAXPLAYERS][BACKUPTICS]; \n \n#define MAXPLMOVE\t\t(forwardmove[1]) \n \n#define TURBOTHRESHOLD\t0x32\n\nfixed_t         forwardmove[2] = {0x19, 0x32}; \nfixed_t         sidemove[2] = {0x18, 0x28}; \nfixed_t         angleturn[3] = {640, 1280, 320};    // + slow turn \n\nstatic int *weapon_keys[] = {\n    &key_weapon1,\n    &key_weapon2,\n    &key_weapon3,\n    &key_weapon4,\n    &key_weapon5,\n    &key_weapon6,\n    &key_weapon7,\n    &key_weapon8\n};\n\n// Set to -1 or +1 to switch to the previous or next weapon.\n\nstatic int next_weapon = 0;\n\n// Used for prev/next weapon keys.\n\nstatic const struct\n{\n    weapontype_t weapon;\n    weapontype_t weapon_num;\n} weapon_order_table[] = {\n    { wp_fist,            wp_fist },\n    { wp_chainsaw,        wp_fist },\n    { wp_pistol,          wp_pistol },\n    { wp_shotgun,         wp_shotgun },\n    { wp_supershotgun,    wp_shotgun },\n    { wp_chaingun,        wp_chaingun },\n    { wp_missile,         wp_missile },\n    { wp_plasma,          wp_plasma },\n    { wp_bfg,             wp_bfg }\n};\n\n#define SLOWTURNTICS\t6 \n \n#define NUMKEYS\t\t256 \n#define MAX_JOY_BUTTONS 20\n\nstatic boolean  gamekeydown[NUMKEYS]; \nstatic int      turnheld;\t\t// for accelerative turning \n \nstatic boolean  mousearray[MAX_MOUSE_BUTTONS + 1];\nstatic boolean *mousebuttons = &mousearray[1];  // allow [-1]\n\n// mouse values are used once \nint             mousex;\nint             mousey;         \n\nstatic int      dclicktime;\nstatic boolean  dclickstate;\nstatic int      dclicks; \nstatic int      dclicktime2;\nstatic boolean  dclickstate2;\nstatic int      dclicks2;\n\n// joystick values are repeated \nstatic int      joyxmove;\nstatic int      joyymove;\nstatic int      joystrafemove;\nstatic boolean  joyarray[MAX_JOY_BUTTONS + 1]; \nstatic boolean *joybuttons = &joyarray[1];\t\t// allow [-1] \n \nstatic int      savegameslot; \nstatic char     savedescription[32]; \n \n#define\tBODYQUESIZE\t32\n\nmobj_t*\t\tbodyque[BODYQUESIZE]; \nint\t\tbodyqueslot; \n \nint             vanilla_savegame_limit = 1;\nint             vanilla_demo_limit = 1;\n \nint G_CmdChecksum (ticcmd_t* cmd) \n{ \n    size_t\t\ti;\n    int\t\tsum = 0; \n\t \n    for (i=0 ; i< sizeof(*cmd)/4 - 1 ; i++) \n\tsum += ((int *)cmd)[i]; \n\t\t \n    return sum; \n} \n\nstatic boolean WeaponSelectable(weapontype_t weapon)\n{\n    // Can't select the super shotgun in Doom 1.\n\n    if (weapon == wp_supershotgun && logical_gamemission == doom)\n    {\n        return false;\n    }\n\n    // These weapons aren't available in shareware.\n\n    if ((weapon == wp_plasma || weapon == wp_bfg)\n     && gamemission == doom && gamemode == shareware)\n    {\n        return false;\n    }\n\n    // Can't select a weapon if we don't own it.\n\n    if (!players[consoleplayer].weaponowned[weapon])\n    {\n        return false;\n    }\n\n    // Can't select the fist if we have the chainsaw, unless\n    // we also have the berserk pack.\n\n    if (weapon == wp_fist\n     && players[consoleplayer].weaponowned[wp_chainsaw]\n     && !players[consoleplayer].powers[pw_strength])\n    {\n        return false;\n    }\n\n    return true;\n}\n\nstatic int G_NextWeapon(int direction)\n{\n    weapontype_t weapon;\n    int start_i, i;\n\n    // Find index in the table.\n\n    if (players[consoleplayer].pendingweapon == wp_nochange)\n    {\n        weapon = players[consoleplayer].readyweapon;\n    }\n    else\n    {\n        weapon = players[consoleplayer].pendingweapon;\n    }\n\n    for (i=0; i<arrlen(weapon_order_table); ++i)\n    {\n        if (weapon_order_table[i].weapon == weapon)\n        {\n            break;\n        }\n    }\n\n    // Switch weapon. Don't loop forever.\n    start_i = i;\n    do\n    {\n        i += direction;\n        i = (i + arrlen(weapon_order_table)) % arrlen(weapon_order_table);\n    } while (i != start_i && !WeaponSelectable(weapon_order_table[i].weapon));\n\n    return weapon_order_table[i].weapon_num;\n}\n\n//\n// G_BuildTiccmd\n// Builds a ticcmd from all of the available inputs\n// or reads it from the demo buffer. \n// If recording a demo, write it out \n// \nvoid G_BuildTiccmd (ticcmd_t* cmd, int maketic) \n{ \n    int\t\ti; \n    boolean\tstrafe;\n    boolean\tbstrafe; \n    int\t\tspeed;\n    int\t\ttspeed; \n    int\t\tforward;\n    int\t\tside;\n\n    memset(cmd, 0, sizeof(ticcmd_t));\n\n    cmd->consistancy = \n\tconsistancy[consoleplayer][maketic%BACKUPTICS]; \n \n    strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] \n\t|| joybuttons[joybstrafe]; \n\n    // fraggle: support the old \"joyb_speed = 31\" hack which\n    // allowed an autorun effect\n\n    speed = key_speed >= NUMKEYS\n         || joybspeed >= MAX_JOY_BUTTONS\n         || gamekeydown[key_speed] \n         || joybuttons[joybspeed];\n \n    forward = side = 0;\n    \n    // use two stage accelerative turning\n    // on the keyboard and joystick\n    if (joyxmove < 0\n\t|| joyxmove > 0  \n\t|| gamekeydown[key_right]\n\t|| gamekeydown[key_left]) \n\tturnheld += ticdup; \n    else \n\tturnheld = 0; \n\n    if (turnheld < SLOWTURNTICS) \n\ttspeed = 2;             // slow turn \n    else \n\ttspeed = speed;\n    \n    // let movement keys cancel each other out\n    if (strafe) \n    { \n\tif (gamekeydown[key_right]) \n\t{\n\t    // fprintf(stderr, \"strafe right\\n\");\n\t    side += sidemove[speed]; \n\t}\n\tif (gamekeydown[key_left]) \n\t{\n\t    //\tfprintf(stderr, \"strafe left\\n\");\n\t    side -= sidemove[speed]; \n\t}\n\tif (joyxmove > 0) \n\t    side += sidemove[speed]; \n\tif (joyxmove < 0) \n\t    side -= sidemove[speed]; \n \n    } \n    else \n    { \n\tif (gamekeydown[key_right]) \n\t    cmd->angleturn -= angleturn[tspeed]; \n\tif (gamekeydown[key_left]) \n\t    cmd->angleturn += angleturn[tspeed]; \n\tif (joyxmove > 0) \n\t    cmd->angleturn -= angleturn[tspeed]; \n\tif (joyxmove < 0) \n\t    cmd->angleturn += angleturn[tspeed]; \n    } \n \n    if (gamekeydown[key_up]) \n    {\n\t// fprintf(stderr, \"up\\n\");\n\tforward += forwardmove[speed]; \n    }\n    if (gamekeydown[key_down]) \n    {\n\t// fprintf(stderr, \"down\\n\");\n\tforward -= forwardmove[speed]; \n    }\n\n    if (joyymove < 0) \n        forward += forwardmove[speed]; \n    if (joyymove > 0) \n        forward -= forwardmove[speed]; \n\n    if (gamekeydown[key_strafeleft]\n     || joybuttons[joybstrafeleft]\n     || mousebuttons[mousebstrafeleft]\n     || joystrafemove < 0)\n    {\n        side -= sidemove[speed];\n    }\n\n    if (gamekeydown[key_straferight]\n     || joybuttons[joybstraferight]\n     || mousebuttons[mousebstraferight]\n     || joystrafemove > 0)\n    {\n        side += sidemove[speed]; \n    }\n\n    // buttons\n    cmd->chatchar = HU_dequeueChatChar(); \n \n    if (gamekeydown[key_fire] || mousebuttons[mousebfire] \n\t|| joybuttons[joybfire]) \n\tcmd->buttons |= BT_ATTACK; \n \n    if (gamekeydown[key_use]\n     || joybuttons[joybuse]\n     || mousebuttons[mousebuse])\n    { \n\tcmd->buttons |= BT_USE;\n\t// clear double clicks if hit use button \n\tdclicks = 0;                   \n    } \n\n    // If the previous or next weapon button is pressed, the\n    // next_weapon variable is set to change weapons when\n    // we generate a ticcmd.  Choose a new weapon.\n\n    if (gamestate == GS_LEVEL && next_weapon != 0)\n    {\n        i = G_NextWeapon(next_weapon);\n        cmd->buttons |= BT_CHANGE;\n        cmd->buttons |= i << BT_WEAPONSHIFT;\n    }\n    else\n    {\n        // Check weapon keys.\n\n        for (i=0; i<arrlen(weapon_keys); ++i)\n        {\n            int key = *weapon_keys[i];\n\n            if (gamekeydown[key])\n            {\n                cmd->buttons |= BT_CHANGE;\n                cmd->buttons |= i<<BT_WEAPONSHIFT;\n                break;\n            }\n        }\n    }\n\n    next_weapon = 0;\n\n    // mouse\n    if (mousebuttons[mousebforward]) \n    {\n\tforward += forwardmove[speed];\n    }\n    if (mousebuttons[mousebbackward])\n    {\n        forward -= forwardmove[speed];\n    }\n\n    if (dclick_use)\n    {\n        // forward double click\n        if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1 ) \n        { \n            dclickstate = mousebuttons[mousebforward]; \n            if (dclickstate) \n                dclicks++; \n            if (dclicks == 2) \n            { \n                cmd->buttons |= BT_USE; \n                dclicks = 0; \n            } \n            else \n                dclicktime = 0; \n        } \n        else \n        { \n            dclicktime += ticdup; \n            if (dclicktime > 20) \n            { \n                dclicks = 0; \n                dclickstate = 0; \n            } \n        }\n        \n        // strafe double click\n        bstrafe =\n            mousebuttons[mousebstrafe] \n            || joybuttons[joybstrafe]; \n        if (bstrafe != dclickstate2 && dclicktime2 > 1 ) \n        { \n            dclickstate2 = bstrafe; \n            if (dclickstate2) \n                dclicks2++; \n            if (dclicks2 == 2) \n            { \n                cmd->buttons |= BT_USE; \n                dclicks2 = 0; \n            } \n            else \n                dclicktime2 = 0; \n        } \n        else \n        { \n            dclicktime2 += ticdup; \n            if (dclicktime2 > 20) \n            { \n                dclicks2 = 0; \n                dclickstate2 = 0; \n            } \n        } \n    }\n\n    forward += mousey; \n\n    if (strafe) \n\tside += mousex*2; \n    else \n\tcmd->angleturn -= mousex*0x8; \n\n    if (mousex == 0)\n    {\n        // No movement in the previous frame\n\n        testcontrols_mousespeed = 0;\n    }\n    \n    mousex = mousey = 0; \n\t \n    if (forward > MAXPLMOVE) \n\tforward = MAXPLMOVE; \n    else if (forward < -MAXPLMOVE) \n\tforward = -MAXPLMOVE; \n    if (side > MAXPLMOVE) \n\tside = MAXPLMOVE; \n    else if (side < -MAXPLMOVE) \n\tside = -MAXPLMOVE; \n \n    cmd->forwardmove += forward; \n    cmd->sidemove += side;\n    \n    // special buttons\n    if (sendpause) \n    { \n\tsendpause = false; \n\tcmd->buttons = BT_SPECIAL | BTS_PAUSE; \n    } \n \n    if (sendsave) \n    { \n\tsendsave = false; \n\tcmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot<<BTS_SAVESHIFT); \n    } \n\n    // low-res turning\n\n    if (lowres_turn)\n    {\n        static signed short carry = 0;\n        signed short desired_angleturn;\n\n        desired_angleturn = cmd->angleturn + carry;\n\n        // round angleturn to the nearest 256 unit boundary\n        // for recording demos with single byte values for turn\n\n        cmd->angleturn = (desired_angleturn + 128) & 0xff00;\n\n        // Carry forward the error from the reduced resolution to the\n        // next tic, so that successive small movements can accumulate.\n\n        carry = desired_angleturn - cmd->angleturn;\n    }\n} \n \n\n//\n// G_DoLoadLevel \n//\nvoid G_DoLoadLevel (void) \n{ \n    int             i; \n\n    // Set the sky map.\n    // First thing, we have a dummy sky texture name,\n    //  a flat. The data is in the WAD only because\n    //  we look for an actual index, instead of simply\n    //  setting one.\n\n    skyflatnum = R_FlatNumForName(DEH_String(SKYFLATNAME));\n\n    // The \"Sky never changes in Doom II\" bug was fixed in\n    // the id Anthology version of doom2.exe for Final Doom.\n    if ((gamemode == commercial)\n     && (gameversion == exe_final2 || gameversion == exe_chex))\n    {\n        char *skytexturename;\n\n        if (gamemap < 12)\n        {\n            skytexturename = \"SKY1\";\n        }\n        else if (gamemap < 21)\n        {\n            skytexturename = \"SKY2\";\n        }\n        else\n        {\n            skytexturename = \"SKY3\";\n        }\n\n        skytexturename = DEH_String(skytexturename);\n\n        skytexture = R_TextureNumForName(skytexturename);\n    }\n\n    levelstarttic = gametic;        // for time calculation\n    \n    if (wipegamestate == GS_LEVEL) \n\twipegamestate = -1;             // force a wipe \n\n    gamestate = GS_LEVEL; \n\n    for (i=0 ; i<MAXPLAYERS ; i++) \n    { \n\tturbodetected[i] = false;\n\tif (playeringame[i] && players[i].playerstate == PST_DEAD) \n\t    players[i].playerstate = PST_REBORN; \n\tmemset (players[i].frags,0,sizeof(players[i].frags)); \n    } \n\t\t \n    P_SetupLevel (gameepisode, gamemap, 0, gameskill);    \n    displayplayer = consoleplayer;\t\t// view the guy you are playing    \n    gameaction = ga_nothing; \n    Z_CheckHeap ();\n    \n    // clear cmd building stuff\n\n    memset (gamekeydown, 0, sizeof(gamekeydown));\n    joyxmove = joyymove = joystrafemove = 0;\n    mousex = mousey = 0;\n    sendpause = sendsave = paused = false;\n    memset(mousearray, 0, sizeof(mousearray));\n    memset(joyarray, 0, sizeof(joyarray));\n\n    if (testcontrols)\n    {\n        players[consoleplayer].message = \"Press escape to quit.\";\n    }\n} \n\nstatic void SetJoyButtons(unsigned int buttons_mask)\n{\n    int i;\n\n    for (i=0; i<MAX_JOY_BUTTONS; ++i)\n    {\n        int button_on = (buttons_mask & (1 << i)) != 0;\n\n        // Detect button press:\n\n        if (!joybuttons[i] && button_on)\n        {\n            // Weapon cycling:\n\n            if (i == joybprevweapon)\n            {\n                next_weapon = -1;\n            }\n            else if (i == joybnextweapon)\n            {\n                next_weapon = 1;\n            }\n        }\n\n        joybuttons[i] = button_on;\n    }\n}\n\nstatic void SetMouseButtons(unsigned int buttons_mask)\n{\n    int i;\n\n    for (i=0; i<MAX_MOUSE_BUTTONS; ++i)\n    {\n        unsigned int button_on = (buttons_mask & (1 << i)) != 0;\n\n        // Detect button press:\n\n        if (!mousebuttons[i] && button_on)\n        {\n            if (i == mousebprevweapon)\n            {\n                next_weapon = -1;\n            }\n            else if (i == mousebnextweapon)\n            {\n                next_weapon = 1;\n            }\n        }\n\n\tmousebuttons[i] = button_on;\n    }\n}\n\n//\n// G_Responder  \n// Get info needed to make ticcmd_ts for the players.\n// \nboolean G_Responder (event_t* ev) \n{ \n    // allow spy mode changes even during the demo\n    if (gamestate == GS_LEVEL && ev->type == ev_keydown \n     && ev->data1 == key_spy && (singledemo || !deathmatch) )\n    {\n\t// spy mode \n\tdo \n\t{ \n\t    displayplayer++; \n\t    if (displayplayer == MAXPLAYERS) \n\t\tdisplayplayer = 0; \n\t} while (!playeringame[displayplayer] && displayplayer != consoleplayer); \n\treturn true; \n    }\n    \n    // any other key pops up menu if in demos\n    if (gameaction == ga_nothing && !singledemo && \n\t(demoplayback || gamestate == GS_DEMOSCREEN) \n\t) \n    { \n\tif (ev->type == ev_keydown ||  \n\t    (ev->type == ev_mouse && ev->data1) || \n\t    (ev->type == ev_joystick && ev->data1) ) \n\t{ \n\t    M_StartControlPanel (); \n\t    return true; \n\t} \n\treturn false; \n    } \n\n    if (gamestate == GS_LEVEL) \n    { \n#if 0 \n\tif (devparm && ev->type == ev_keydown && ev->data1 == ';') \n\t{ \n\t    G_DeathMatchSpawnPlayer (0); \n\t    return true; \n\t} \n#endif \n\tif (HU_Responder (ev)) \n\t    return true;\t// chat ate the event \n\tif (ST_Responder (ev)) \n\t    return true;\t// status window ate it \n\tif (AM_Responder (ev)) \n\t    return true;\t// automap ate it \n    } \n\t \n    if (gamestate == GS_FINALE) \n    { \n\tif (F_Responder (ev)) \n\t    return true;\t// finale ate the event \n    } \n\n    if (testcontrols && ev->type == ev_mouse)\n    {\n        // If we are invoked by setup to test the controls, save the \n        // mouse speed so that we can display it on-screen.\n        // Perform a low pass filter on this so that the thermometer \n        // appears to move smoothly.\n\n        testcontrols_mousespeed = abs(ev->data2);\n    }\n\n    // If the next/previous weapon keys are pressed, set the next_weapon\n    // variable to change weapons when the next ticcmd is generated.\n\n    if (ev->type == ev_keydown && ev->data1 == key_prevweapon)\n    {\n        next_weapon = -1;\n    }\n    else if (ev->type == ev_keydown && ev->data1 == key_nextweapon)\n    {\n        next_weapon = 1;\n    }\n\n    switch (ev->type) \n    { \n      case ev_keydown: \n\tif (ev->data1 == key_pause) \n\t{ \n\t    sendpause = true; \n\t}\n        else if (ev->data1 <NUMKEYS) \n        {\n\t    gamekeydown[ev->data1] = true; \n        }\n\n\treturn true;    // eat key down events \n \n      case ev_keyup: \n\tif (ev->data1 <NUMKEYS) \n\t    gamekeydown[ev->data1] = false; \n\treturn false;   // always let key up events filter down \n\t\t \n      case ev_mouse: \n        SetMouseButtons(ev->data1);\n\tmousex = ev->data2*(mouseSensitivity+5)/10; \n\tmousey = ev->data3*(mouseSensitivity+5)/10; \n\treturn true;    // eat events \n \n      case ev_joystick: \n        SetJoyButtons(ev->data1);\n\tjoyxmove = ev->data2; \n\tjoyymove = ev->data3; \n        joystrafemove = ev->data4;\n\treturn true;    // eat events \n \n      default: \n\tbreak; \n    } \n \n    return false; \n} \n \n \n \n//\n// G_Ticker\n// Make ticcmd_ts for the players.\n//\nvoid G_Ticker (void) \n{ \n    int\t\ti;\n    int\t\tbuf; \n    ticcmd_t*\tcmd;\n    \n    // do player reborns if needed\n    for (i=0 ; i<MAXPLAYERS ; i++) \n\tif (playeringame[i] && players[i].playerstate == PST_REBORN) \n\t    G_DoReborn (i);\n    \n    // do things to change the game state\n    while (gameaction != ga_nothing) \n    { \n\tswitch (gameaction) \n\t{ \n\t  case ga_loadlevel: \n\t    G_DoLoadLevel (); \n\t    break; \n\t  case ga_newgame: \n\t    G_DoNewGame (); \n\t    break; \n\t  case ga_loadgame: \n\t    G_DoLoadGame (); \n\t    break; \n\t  case ga_savegame: \n\t    G_DoSaveGame (); \n\t    break; \n\t  case ga_playdemo: \n\t    G_DoPlayDemo (); \n\t    break; \n\t  case ga_completed: \n\t    G_DoCompleted (); \n\t    break; \n\t  case ga_victory: \n\t    F_StartFinale (); \n\t    break; \n\t  case ga_worlddone: \n\t    G_DoWorldDone (); \n\t    break; \n\t  case ga_screenshot: \n\t    V_ScreenShot(\"DOOM%02i.%s\"); \n            players[consoleplayer].message = DEH_String(\"screen shot\");\n\t    gameaction = ga_nothing; \n\t    break; \n\t  case ga_nothing: \n\t    break; \n\t} \n    }\n    \n    // get commands, check consistancy,\n    // and build new consistancy check\n    buf = (gametic/ticdup)%BACKUPTICS; \n \n    for (i=0 ; i<MAXPLAYERS ; i++)\n    {\n\tif (playeringame[i]) \n\t{ \n\t    cmd = &players[i].cmd; \n\n\t    memcpy(cmd, &netcmds[i], sizeof(ticcmd_t));\n\n\t    if (demoplayback) \n\t\tG_ReadDemoTiccmd (cmd); \n\t    if (demorecording) \n\t\tG_WriteDemoTiccmd (cmd);\n\t    \n\t    // check for turbo cheats\n\n            // check ~ 4 seconds whether to display the turbo message. \n            // store if the turbo threshold was exceeded in any tics\n            // over the past 4 seconds.  offset the checking period\n            // for each player so messages are not displayed at the\n            // same time.\n\n            if (cmd->forwardmove > TURBOTHRESHOLD)\n            {\n                turbodetected[i] = true;\n            }\n\n            if ((gametic & 31) == 0 \n             && ((gametic >> 5) % MAXPLAYERS) == i\n             && turbodetected[i])\n            {\n                static char turbomessage[80];\n                extern char *player_names[4];\n                M_snprintf(turbomessage, sizeof(turbomessage),\n                           \"%s is turbo!\", player_names[i]);\n                players[consoleplayer].message = turbomessage;\n                turbodetected[i] = false;\n            }\n\n\t    if (netgame && !netdemo && !(gametic%ticdup) ) \n\t    { \n\t\tif (gametic > BACKUPTICS \n\t\t    && consistancy[i][buf] != cmd->consistancy) \n\t\t{ \n\t\t    I_Error (\"consistency failure (%i should be %i)\",\n\t\t\t     cmd->consistancy, consistancy[i][buf]); \n\t\t} \n\t\tif (players[i].mo) \n\t\t    consistancy[i][buf] = players[i].mo->x; \n\t\telse \n\t\t    consistancy[i][buf] = rndindex; \n\t    } \n\t}\n    }\n    \n    // check for special buttons\n    for (i=0 ; i<MAXPLAYERS ; i++)\n    {\n\tif (playeringame[i]) \n\t{ \n\t    if (players[i].cmd.buttons & BT_SPECIAL) \n\t    { \n\t\tswitch (players[i].cmd.buttons & BT_SPECIALMASK) \n\t\t{ \n\t\t  case BTS_PAUSE: \n\t\t    paused ^= 1; \n\t\t    if (paused) \n\t\t\tS_PauseSound (); \n\t\t    else \n\t\t\tS_ResumeSound (); \n\t\t    break; \n\t\t\t\t\t \n\t\t  case BTS_SAVEGAME:\n\t\t    if (!savedescription[0]) \n                    {\n                        M_StringCopy(savedescription, \"NET GAME\",\n                                     sizeof(savedescription));\n                    }\n\n\t\t    savegameslot =  \n\t\t\t(players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT; \n\t\t    gameaction = ga_savegame; \n\t\t    break; \n\t\t} \n\t    } \n\t}\n    }\n\n    // Have we just finished displaying an intermission screen?\n\n    if (oldgamestate == GS_INTERMISSION && gamestate != GS_INTERMISSION)\n    {\n        WI_End();\n    }\n\n    oldgamestate = gamestate;\n    \n    // do main actions\n    switch (gamestate) \n    { \n      case GS_LEVEL: \n\tP_Ticker (); \n\tST_Ticker (); \n\tAM_Ticker (); \n\tHU_Ticker ();            \n\tbreak; \n\t \n      case GS_INTERMISSION: \n\tWI_Ticker (); \n\tbreak; \n\t\t\t \n      case GS_FINALE: \n\tF_Ticker (); \n\tbreak; \n \n      case GS_DEMOSCREEN: \n\tD_PageTicker (); \n\tbreak;\n    }        \n} \n \n \n//\n// PLAYER STRUCTURE FUNCTIONS\n// also see P_SpawnPlayer in P_Things\n//\n\n//\n// G_InitPlayer \n// Called at the start.\n// Called by the game initialization functions.\n//\nvoid G_InitPlayer (int player) \n{\n    // clear everything else to defaults\n    G_PlayerReborn (player); \n}\n \n \n\n//\n// G_PlayerFinishLevel\n// Can when a player completes a level.\n//\nvoid G_PlayerFinishLevel (int player) \n{ \n    player_t*\tp; \n\t \n    p = &players[player]; \n\t \n    memset (p->powers, 0, sizeof (p->powers)); \n    memset (p->cards, 0, sizeof (p->cards)); \n    p->mo->flags &= ~MF_SHADOW;\t\t// cancel invisibility \n    p->extralight = 0;\t\t\t// cancel gun flashes \n    p->fixedcolormap = 0;\t\t// cancel ir gogles \n    p->damagecount = 0;\t\t\t// no palette changes \n    p->bonuscount = 0; \n} \n \n\n//\n// G_PlayerReborn\n// Called after a player dies \n// almost everything is cleared and initialized \n//\nvoid G_PlayerReborn (int player) \n{ \n    player_t*\tp; \n    int\t\ti; \n    int\t\tfrags[MAXPLAYERS]; \n    int\t\tkillcount;\n    int\t\titemcount;\n    int\t\tsecretcount; \n\t \n    memcpy (frags,players[player].frags,sizeof(frags)); \n    killcount = players[player].killcount; \n    itemcount = players[player].itemcount; \n    secretcount = players[player].secretcount; \n\t \n    p = &players[player]; \n    memset (p, 0, sizeof(*p)); \n \n    memcpy (players[player].frags, frags, sizeof(players[player].frags)); \n    players[player].killcount = killcount; \n    players[player].itemcount = itemcount; \n    players[player].secretcount = secretcount; \n \n    p->usedown = p->attackdown = true;\t// don't do anything immediately \n    p->playerstate = PST_LIVE;       \n    p->health = deh_initial_health;     // Use dehacked value\n    p->readyweapon = p->pendingweapon = wp_pistol; \n    p->weaponowned[wp_fist] = true; \n    p->weaponowned[wp_pistol] = true; \n    p->ammo[am_clip] = deh_initial_bullets; \n\t \n    for (i=0 ; i<NUMAMMO ; i++) \n\tp->maxammo[i] = maxammo[i]; \n\t\t \n}\n\n//\n// G_CheckSpot  \n// Returns false if the player cannot be respawned\n// at the given mapthing_t spot  \n// because something is occupying it \n//\nvoid P_SpawnPlayer (mapthing_t* mthing); \n \nboolean\nG_CheckSpot\n( int\t\tplayernum,\n  mapthing_t*\tmthing ) \n{ \n    fixed_t\t\tx;\n    fixed_t\t\ty; \n    subsector_t*\tss; \n    mobj_t*\t\tmo; \n    int\t\t\ti;\n\t\n    if (!players[playernum].mo)\n    {\n\t// first spawn of level, before corpses\n\tfor (i=0 ; i<playernum ; i++)\n\t    if (players[i].mo->x == mthing->x << FRACBITS\n\t\t&& players[i].mo->y == mthing->y << FRACBITS)\n\t\treturn false;\t\n\treturn true;\n    }\n\t\t\n    x = mthing->x << FRACBITS; \n    y = mthing->y << FRACBITS; \n\t \n    if (!P_CheckPosition (players[playernum].mo, x, y) ) \n\treturn false; \n \n    // flush an old corpse if needed \n    if (bodyqueslot >= BODYQUESIZE) \n\tP_RemoveMobj (bodyque[bodyqueslot%BODYQUESIZE]); \n    bodyque[bodyqueslot%BODYQUESIZE] = players[playernum].mo; \n    bodyqueslot++; \n\n    // spawn a teleport fog\n    ss = R_PointInSubsector (x,y);\n\n\n    // The code in the released source looks like this:\n    //\n    //    an = ( ANG45 * (((unsigned int) mthing->angle)/45) )\n    //         >> ANGLETOFINESHIFT;\n    //    mo = P_SpawnMobj (x+20*finecosine[an], y+20*finesine[an]\n    //                     , ss->sector->floorheight\n    //                     , MT_TFOG);\n    //\n    // But 'an' can be a signed value in the DOS version. This means that\n    // we get a negative index and the lookups into finecosine/finesine\n    // end up dereferencing values in finetangent[].\n    // A player spawning on a deathmatch start facing directly west spawns\n    // \"silently\" with no spawn fog. Emulate this.\n    //\n    // This code is imported from PrBoom+.\n\n    {\n        fixed_t xa, ya;\n        signed int an;\n\n        // This calculation overflows in Vanilla Doom, but here we deliberately\n        // avoid integer overflow as it is undefined behavior, so the value of\n        // 'an' will always be positive.\n        an = (ANG45 >> ANGLETOFINESHIFT) * ((signed int) mthing->angle / 45);\n\n        switch (an)\n        {\n            case 4096:  // -4096:\n                xa = finetangent[2048];    // finecosine[-4096]\n                ya = finetangent[0];       // finesine[-4096]\n                break;\n            case 5120:  // -3072:\n                xa = finetangent[3072];    // finecosine[-3072]\n                ya = finetangent[1024];    // finesine[-3072]\n                break;\n            case 6144:  // -2048:\n                xa = finesine[0];          // finecosine[-2048]\n                ya = finetangent[2048];    // finesine[-2048]\n                break;\n            case 7168:  // -1024:\n                xa = finesine[1024];       // finecosine[-1024]\n                ya = finetangent[3072];    // finesine[-1024]\n                break;\n            case 0:\n            case 1024:\n            case 2048:\n            case 3072:\n                xa = finecosine[an];\n                ya = finesine[an];\n                break;\n            default:\n                I_Error(\"G_CheckSpot: unexpected angle %d\\n\", an);\n                xa = ya = 0;\n                break;\n        }\n        mo = P_SpawnMobj(x + 20 * xa, y + 20 * ya,\n                         ss->sector->floorheight, MT_TFOG);\n    }\n\n    if (players[consoleplayer].viewz != 1) \n\tS_StartSound (mo, sfx_telept);\t// don't start sound on first frame \n \n    return true; \n} \n\n\n//\n// G_DeathMatchSpawnPlayer \n// Spawns a player at one of the random death match spots \n// called at level load and each death \n//\nvoid G_DeathMatchSpawnPlayer (int playernum) \n{ \n    int             i,j; \n    int\t\t\t\tselections; \n\t \n    selections = deathmatch_p - deathmatchstarts; \n    if (selections < 4) \n\tI_Error (\"Only %i deathmatch spots, 4 required\", selections); \n \n    for (j=0 ; j<20 ; j++) \n    { \n\ti = P_Random() % selections; \n\tif (G_CheckSpot (playernum, &deathmatchstarts[i]) ) \n\t{ \n\t    deathmatchstarts[i].type = playernum+1; \n\t    P_SpawnPlayer (&deathmatchstarts[i]); \n\t    return; \n\t} \n    } \n \n    // no good spot, so the player will probably get stuck \n    P_SpawnPlayer (&playerstarts[playernum]); \n} \n\n//\n// G_DoReborn \n// \nvoid G_DoReborn (int playernum) \n{ \n    int                             i; \n\t \n    if (!netgame)\n    {\n\t// reload the level from scratch\n\tgameaction = ga_loadlevel;  \n    }\n    else \n    {\n\t// respawn at the start\n\n\t// first dissasociate the corpse \n\tplayers[playernum].mo->player = NULL;   \n\t\t \n\t// spawn at random spot if in death match \n\tif (deathmatch) \n\t{ \n\t    G_DeathMatchSpawnPlayer (playernum); \n\t    return; \n\t} \n\t\t \n\tif (G_CheckSpot (playernum, &playerstarts[playernum]) ) \n\t{ \n\t    P_SpawnPlayer (&playerstarts[playernum]); \n\t    return; \n\t}\n\t\n\t// try to spawn at one of the other players spots \n\tfor (i=0 ; i<MAXPLAYERS ; i++)\n\t{\n\t    if (G_CheckSpot (playernum, &playerstarts[i]) ) \n\t    { \n\t\tplayerstarts[i].type = playernum+1;\t// fake as other player \n\t\tP_SpawnPlayer (&playerstarts[i]); \n\t\tplayerstarts[i].type = i+1;\t\t// restore \n\t\treturn; \n\t    }\t    \n\t    // he's going to be inside something.  Too bad.\n\t}\n\tP_SpawnPlayer (&playerstarts[playernum]); \n    } \n} \n \n \nvoid G_ScreenShot (void) \n{ \n    gameaction = ga_screenshot; \n} \n \n\n\n// DOOM Par Times\nint pars[4][10] = \n{ \n    {0}, \n    {0,30,75,120,90,165,180,180,30,165}, \n    {0,90,90,90,120,90,360,240,30,170}, \n    {0,90,45,90,150,90,90,165,30,135} \n}; \n\n// DOOM II Par Times\nint cpars[32] =\n{\n    30,90,120,120,90,150,120,120,270,90,\t//  1-10\n    210,150,150,150,210,150,420,150,210,150,\t// 11-20\n    240,150,180,150,150,300,330,420,300,180,\t// 21-30\n    120,30\t\t\t\t\t// 31-32\n};\n \n\n//\n// G_DoCompleted \n//\nboolean\t\tsecretexit; \nextern char*\tpagename; \n \nvoid G_ExitLevel (void) \n{ \n    secretexit = false; \n    gameaction = ga_completed; \n} \n\n// Here's for the german edition.\nvoid G_SecretExitLevel (void) \n{ \n    // IF NO WOLF3D LEVELS, NO SECRET EXIT!\n    if ( (gamemode == commercial)\n      && (W_CheckNumForName(\"map31\")<0))\n\tsecretexit = false;\n    else\n\tsecretexit = true; \n    gameaction = ga_completed; \n} \n \nvoid G_DoCompleted (void) \n{ \n    int             i; \n\t \n    gameaction = ga_nothing; \n \n    for (i=0 ; i<MAXPLAYERS ; i++) \n\tif (playeringame[i]) \n\t    G_PlayerFinishLevel (i);        // take away cards and stuff \n\t \n    if (automapactive) \n\tAM_Stop (); \n\t\n    if (gamemode != commercial)\n    {\n        // Chex Quest ends after 5 levels, rather than 8.\n\n        if (gameversion == exe_chex)\n        {\n            if (gamemap == 5)\n            {\n                gameaction = ga_victory;\n                return;\n            }\n        }\n        else\n        {\n            switch(gamemap)\n            {\n              case 8:\n                gameaction = ga_victory;\n                return;\n              case 9: \n                for (i=0 ; i<MAXPLAYERS ; i++) \n                    players[i].didsecret = true; \n                break;\n            }\n        }\n    }\n\n//#if 0  Hmmm - why?\n    if ( (gamemap == 8)\n\t && (gamemode != commercial) ) \n    {\n\t// victory \n\tgameaction = ga_victory; \n\treturn; \n    } \n\t \n    if ( (gamemap == 9)\n\t && (gamemode != commercial) ) \n    {\n\t// exit secret level \n\tfor (i=0 ; i<MAXPLAYERS ; i++) \n\t    players[i].didsecret = true; \n    } \n//#endif\n    \n\t \n    wminfo.didsecret = players[consoleplayer].didsecret; \n    wminfo.epsd = gameepisode -1; \n    wminfo.last = gamemap -1;\n    \n    // wminfo.next is 0 biased, unlike gamemap\n    if ( gamemode == commercial)\n    {\n\tif (secretexit)\n\t    switch(gamemap)\n\t    {\n\t      case 15: wminfo.next = 30; break;\n\t      case 31: wminfo.next = 31; break;\n\t    }\n\telse\n\t    switch(gamemap)\n\t    {\n\t      case 31:\n\t      case 32: wminfo.next = 15; break;\n\t      default: wminfo.next = gamemap;\n\t    }\n    }\n    else\n    {\n\tif (secretexit) \n\t    wminfo.next = 8; \t// go to secret level \n\telse if (gamemap == 9) \n\t{\n\t    // returning from secret level \n\t    switch (gameepisode) \n\t    { \n\t      case 1: \n\t\twminfo.next = 3; \n\t\tbreak; \n\t      case 2: \n\t\twminfo.next = 5; \n\t\tbreak; \n\t      case 3: \n\t\twminfo.next = 6; \n\t\tbreak; \n\t      case 4:\n\t\twminfo.next = 2;\n\t\tbreak;\n\t    }                \n\t} \n\telse \n\t    wminfo.next = gamemap;          // go to next level \n    }\n\t\t \n    wminfo.maxkills = totalkills; \n    wminfo.maxitems = totalitems; \n    wminfo.maxsecret = totalsecret; \n    wminfo.maxfrags = 0; \n\n    // Set par time. Doom episode 4 doesn't have a par time, so this\n    // overflows into the cpars array. It's necessary to emulate this\n    // for statcheck regression testing.\n    if (gamemode == commercial)\n\twminfo.partime = TICRATE*cpars[gamemap-1];\n    else if (gameepisode < 4)\n\twminfo.partime = TICRATE*pars[gameepisode][gamemap];\n    else\n        wminfo.partime = TICRATE*cpars[gamemap];\n\n    wminfo.pnum = consoleplayer; \n \n    for (i=0 ; i<MAXPLAYERS ; i++) \n    { \n\twminfo.plyr[i].in = playeringame[i]; \n\twminfo.plyr[i].skills = players[i].killcount; \n\twminfo.plyr[i].sitems = players[i].itemcount; \n\twminfo.plyr[i].ssecret = players[i].secretcount; \n\twminfo.plyr[i].stime = leveltime; \n\tmemcpy (wminfo.plyr[i].frags, players[i].frags \n\t\t, sizeof(wminfo.plyr[i].frags)); \n    } \n \n    gamestate = GS_INTERMISSION; \n    viewactive = false; \n    automapactive = false; \n\n    StatCopy(&wminfo);\n \n    WI_Start (&wminfo); \n} \n\n\n//\n// G_WorldDone \n//\nvoid G_WorldDone (void) \n{ \n    gameaction = ga_worlddone; \n\n    if (secretexit) \n\tplayers[consoleplayer].didsecret = true; \n\n    if ( gamemode == commercial )\n    {\n\tswitch (gamemap)\n\t{\n\t  case 15:\n\t  case 31:\n\t    if (!secretexit)\n\t\tbreak;\n\t  case 6:\n\t  case 11:\n\t  case 20:\n\t  case 30:\n\t    F_StartFinale ();\n\t    break;\n\t}\n    }\n} \n \nvoid G_DoWorldDone (void) \n{        \n    gamestate = GS_LEVEL; \n    gamemap = wminfo.next+1; \n    G_DoLoadLevel (); \n    gameaction = ga_nothing; \n    viewactive = true; \n} \n \n\n\n//\n// G_InitFromSavegame\n// Can be called by the startup code or the menu task. \n//\nextern boolean setsizeneeded;\nvoid R_ExecuteSetViewSize (void);\n\nchar\tsavename[256];\n\nvoid G_LoadGame (char* name) \n{ \n    M_StringCopy(savename, name, sizeof(savename));\n    gameaction = ga_loadgame; \n} \n \n#define VERSIONSIZE\t\t16 \n\n\nvoid G_DoLoadGame (void) \n{\n    int savedleveltime;\n\t \n    gameaction = ga_nothing; \n\t \n    save_stream = fopen(savename, \"rb\");\n\n    if (save_stream == NULL)\n    {\n    \treturn;\n    }\n\n    savegame_error = false;\n\n    if (!P_ReadSaveGameHeader())\n    {\n        fclose(save_stream);\n        return;\n    }\n\n    savedleveltime = leveltime;\n    \n    // load a base level \n    G_InitNew (gameskill, gameepisode, gamemap); \n \n    leveltime = savedleveltime;\n\n    // dearchive all the modifications\n    P_UnArchivePlayers (); \n    P_UnArchiveWorld (); \n    P_UnArchiveThinkers (); \n    P_UnArchiveSpecials (); \n \n    if (!P_ReadSaveGameEOF())\n\tI_Error (\"Bad savegame\");\n\n    fclose(save_stream);\n    \n    if (setsizeneeded)\n    \tR_ExecuteSetViewSize ();\n    \n    // draw the pattern into the back screen\n    R_FillBackScreen (); \n} \n \n\n//\n// G_SaveGame\n// Called by the menu task.\n// Description is a 24 byte text string \n//\nvoid\nG_SaveGame\n( int\tslot,\n  char*\tdescription )\n{\n    savegameslot = slot;\n    M_StringCopy(savedescription, description, sizeof(savedescription));\n    sendsave = true;\n}\n\nvoid G_DoSaveGame (void) \n{ \n    char *savegame_file;\n    char *temp_savegame_file;\n    char *recovery_savegame_file;\n\n    recovery_savegame_file = NULL;\n    temp_savegame_file = P_TempSaveGameFile();\n    savegame_file = P_SaveGameFile(savegameslot);\n\n    // Open the savegame file for writing.  We write to a temporary file\n    // and then rename it at the end if it was successfully written.\n    // This prevents an existing savegame from being overwritten by \n    // a corrupted one, or if a savegame buffer overrun occurs.\n    save_stream = fopen(temp_savegame_file, \"wb\");\n\n    if (save_stream == NULL)\n    {\n        // Failed to save the game, so we're going to have to abort. But\n        // to be nice, save to somewhere else before we call I_Error().\n        recovery_savegame_file = M_TempFile(\"recovery.dsg\");\n        save_stream = fopen(recovery_savegame_file, \"wb\");\n        if (save_stream == NULL)\n        {\n            I_Error(\"Failed to open either '%s' or '%s' to write savegame.\",\n                    temp_savegame_file, recovery_savegame_file);\n        }\n    }\n\n    savegame_error = false;\n\n    P_WriteSaveGameHeader(savedescription);\n \n    P_ArchivePlayers (); \n    P_ArchiveWorld (); \n    P_ArchiveThinkers (); \n    P_ArchiveSpecials (); \n\t \n    P_WriteSaveGameEOF();\n\t \n    // Enforce the same savegame size limit as in Vanilla Doom, \n    // except if the vanilla_savegame_limit setting is turned off.\n\n    if (vanilla_savegame_limit && ftell(save_stream) > SAVEGAMESIZE)\n    {\n        I_Error (\"Savegame buffer overrun\");\n    }\n    \n    // Finish up, close the savegame file.\n\n    fclose(save_stream);\n\n    if (recovery_savegame_file != NULL)\n    {\n        // We failed to save to the normal location, but we wrote a\n        // recovery file to the temp directory. Now we can bomb out\n        // with an error.\n        I_Error(\"Failed to open savegame file '%s' for writing.\\n\"\n                \"But your game has been saved to '%s' for recovery.\",\n                temp_savegame_file, recovery_savegame_file);\n    }\n\n    // Now rename the temporary savegame file to the actual savegame\n    // file, overwriting the old savegame if there was one there.\n\n    remove(savegame_file);\n    rename(temp_savegame_file, savegame_file);\n    \n    gameaction = ga_nothing;\n    M_StringCopy(savedescription, \"\", sizeof(savedescription));\n\n    players[consoleplayer].message = DEH_String(GGSAVED);\n\n    // draw the pattern into the back screen\n    R_FillBackScreen ();\t\n} \n \n\n//\n// G_InitNew\n// Can be called by the startup code or the menu task,\n// consoleplayer, displayplayer, playeringame[] should be set. \n//\nskill_t\td_skill; \nint     d_episode; \nint     d_map; \n \nvoid\nG_DeferedInitNew\n( skill_t\tskill,\n  int\t\tepisode,\n  int\t\tmap) \n{ \n    d_skill = skill; \n    d_episode = episode; \n    d_map = map; \n    gameaction = ga_newgame; \n} \n\n\nvoid G_DoNewGame (void) \n{\n    demoplayback = false; \n    netdemo = false;\n    netgame = false;\n    deathmatch = false;\n    playeringame[1] = playeringame[2] = playeringame[3] = 0;\n    respawnparm = false;\n    fastparm = false;\n    nomonsters = false;\n    consoleplayer = 0;\n    G_InitNew (d_skill, d_episode, d_map); \n    gameaction = ga_nothing; \n} \n\n\nvoid\nG_InitNew\n( skill_t\tskill,\n  int\t\tepisode,\n  int\t\tmap )\n{\n    char *skytexturename;\n    int             i;\n\n    if (paused)\n    {\n\tpaused = false;\n\tS_ResumeSound ();\n    }\n\n    /*\n    // Note: This commented-out block of code was added at some point\n    // between the DOS version(s) and the Doom source release. It isn't\n    // found in disassemblies of the DOS version and causes IDCLEV and\n    // the -warp command line parameter to behave differently.\n    // This is left here for posterity.\n\n    // This was quite messy with SPECIAL and commented parts.\n    // Supposedly hacks to make the latest edition work.\n    // It might not work properly.\n    if (episode < 1)\n      episode = 1;\n\n    if ( gamemode == retail )\n    {\n      if (episode > 4)\n\tepisode = 4;\n    }\n    else if ( gamemode == shareware )\n    {\n      if (episode > 1)\n\t   episode = 1;\t// only start episode 1 on shareware\n    }\n    else\n    {\n      if (episode > 3)\n\tepisode = 3;\n    }\n    */\n\n    if (skill > sk_nightmare)\n\tskill = sk_nightmare;\n\n    if (gameversion >= exe_ultimate)\n    {\n        if (episode == 0)\n        {\n            episode = 4;\n        }\n    }\n    else\n    {\n        if (episode < 1)\n        {\n            episode = 1;\n        }\n        if (episode > 3)\n        {\n            episode = 3;\n        }\n    }\n\n    if (episode > 1 && gamemode == shareware)\n    {\n        episode = 1;\n    }\n\n    if (map < 1)\n\tmap = 1;\n\n    if ( (map > 9)\n\t && ( gamemode != commercial) )\n      map = 9;\n\n    M_ClearRandom ();\n\n    if (skill == sk_nightmare || respawnparm )\n\trespawnmonsters = true;\n    else\n\trespawnmonsters = false;\n\n    if (fastparm || (skill == sk_nightmare && gameskill != sk_nightmare) )\n    {\n\tfor (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++)\n\t    states[i].tics >>= 1;\n\tmobjinfo[MT_BRUISERSHOT].speed = 20*FRACUNIT;\n\tmobjinfo[MT_HEADSHOT].speed = 20*FRACUNIT;\n\tmobjinfo[MT_TROOPSHOT].speed = 20*FRACUNIT;\n    }\n    else if (skill != sk_nightmare && gameskill == sk_nightmare)\n    {\n\tfor (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++)\n\t    states[i].tics <<= 1;\n\tmobjinfo[MT_BRUISERSHOT].speed = 15*FRACUNIT;\n\tmobjinfo[MT_HEADSHOT].speed = 10*FRACUNIT;\n\tmobjinfo[MT_TROOPSHOT].speed = 10*FRACUNIT;\n    }\n\n    // force players to be initialized upon first level load\n    for (i=0 ; i<MAXPLAYERS ; i++)\n\tplayers[i].playerstate = PST_REBORN;\n\n    usergame = true;                // will be set false if a demo\n    paused = false;\n    demoplayback = false;\n    automapactive = false;\n    viewactive = true;\n    gameepisode = episode;\n    gamemap = map;\n    gameskill = skill;\n\n    viewactive = true;\n\n    // Set the sky to use.\n    //\n    // Note: This IS broken, but it is how Vanilla Doom behaves.\n    // See http://doomwiki.org/wiki/Sky_never_changes_in_Doom_II.\n    //\n    // Because we set the sky here at the start of a game, not at the\n    // start of a level, the sky texture never changes unless we\n    // restore from a saved game.  This was fixed before the Doom\n    // source release, but this IS the way Vanilla DOS Doom behaves.\n\n    if (gamemode == commercial)\n    {\n        if (gamemap < 12)\n            skytexturename = \"SKY1\";\n        else if (gamemap < 21)\n            skytexturename = \"SKY2\";\n        else\n            skytexturename = \"SKY3\";\n    }\n    else\n    {\n        switch (gameepisode)\n        {\n          default:\n          case 1:\n            skytexturename = \"SKY1\";\n            break;\n          case 2:\n            skytexturename = \"SKY2\";\n            break;\n          case 3:\n            skytexturename = \"SKY3\";\n            break;\n          case 4:        // Special Edition sky\n            skytexturename = \"SKY4\";\n            break;\n        }\n    }\n\n    skytexturename = DEH_String(skytexturename);\n\n    skytexture = R_TextureNumForName(skytexturename);\n\n\n    G_DoLoadLevel ();\n}\n\n\n//\n// DEMO RECORDING \n// \n#define DEMOMARKER\t\t0x80\n\n\nvoid G_ReadDemoTiccmd (ticcmd_t* cmd) \n{ \n    if (*demo_p == DEMOMARKER) \n    {\n\t// end of demo data stream \n\tG_CheckDemoStatus (); \n\treturn; \n    } \n    cmd->forwardmove = ((signed char)*demo_p++); \n    cmd->sidemove = ((signed char)*demo_p++); \n\n    // If this is a longtics demo, read back in higher resolution\n\n    if (longtics)\n    {\n        cmd->angleturn = *demo_p++;\n        cmd->angleturn |= (*demo_p++) << 8;\n    }\n    else\n    {\n        cmd->angleturn = ((unsigned char) *demo_p++)<<8; \n    }\n\n    cmd->buttons = (unsigned char)*demo_p++; \n} \n\n// Increase the size of the demo buffer to allow unlimited demos\n\nstatic void IncreaseDemoBuffer(void)\n{\n    int current_length;\n    byte *new_demobuffer;\n    byte *new_demop;\n    int new_length;\n\n    // Find the current size\n\n    current_length = demoend - demobuffer;\n    \n    // Generate a new buffer twice the size\n    new_length = current_length * 2;\n    \n    new_demobuffer = Z_Malloc(new_length, PU_STATIC, 0);\n    new_demop = new_demobuffer + (demo_p - demobuffer);\n\n    // Copy over the old data\n\n    memcpy(new_demobuffer, demobuffer, current_length);\n\n    // Free the old buffer and point the demo pointers at the new buffer.\n\n    Z_Free(demobuffer);\n\n    demobuffer = new_demobuffer;\n    demo_p = new_demop;\n    demoend = demobuffer + new_length;\n}\n\nvoid G_WriteDemoTiccmd (ticcmd_t* cmd) \n{ \n    byte *demo_start;\n\n    if (gamekeydown[key_demo_quit])           // press q to end demo recording \n\tG_CheckDemoStatus (); \n\n    demo_start = demo_p;\n\n    *demo_p++ = cmd->forwardmove; \n    *demo_p++ = cmd->sidemove; \n\n    // If this is a longtics demo, record in higher resolution\n \n    if (longtics)\n    {\n        *demo_p++ = (cmd->angleturn & 0xff);\n        *demo_p++ = (cmd->angleturn >> 8) & 0xff;\n    }\n    else\n    {\n        *demo_p++ = cmd->angleturn >> 8; \n    }\n\n    *demo_p++ = cmd->buttons; \n\n    // reset demo pointer back\n    demo_p = demo_start;\n\n    if (demo_p > demoend - 16)\n    {\n        if (vanilla_demo_limit)\n        {\n            // no more space \n            G_CheckDemoStatus (); \n            return; \n        }\n        else\n        {\n            // Vanilla demo limit disabled: unlimited\n            // demo lengths!\n\n            IncreaseDemoBuffer();\n        }\n    } \n\t\n    G_ReadDemoTiccmd (cmd);         // make SURE it is exactly the same \n} \n \n \n \n//\n// G_RecordDemo\n//\nvoid G_RecordDemo (char *name)\n{\n    size_t demoname_size;\n    int i;\n    int maxsize;\n\n    usergame = false;\n    demoname_size = strlen(name) + 5;\n    demoname = Z_Malloc(demoname_size, PU_STATIC, NULL);\n    M_snprintf(demoname, demoname_size, \"%s.lmp\", name);\n    maxsize = 0x20000;\n\n    //!\n    // @arg <size>\n    // @category demo\n    // @vanilla\n    //\n    // Specify the demo buffer size (KiB)\n    //\n\n    i = M_CheckParmWithArgs(\"-maxdemo\", 1);\n    if (i)\n\tmaxsize = atoi(myargv[i+1])*1024;\n    demobuffer = Z_Malloc (maxsize,PU_STATIC,NULL); \n    demoend = demobuffer + maxsize;\n\t\n    demorecording = true; \n} \n\n// Get the demo version code appropriate for the version set in gameversion.\nint G_VanillaVersionCode(void)\n{\n    switch (gameversion)\n    {\n        case exe_doom_1_2:\n            I_Error(\"Doom 1.2 does not have a version code!\");\n        case exe_doom_1_666:\n            return 106;\n        case exe_doom_1_7:\n            return 107;\n        case exe_doom_1_8:\n            return 108;\n        case exe_doom_1_9:\n        default:  // All other versions are variants on v1.9:\n            return 109;\n    }\n}\n\nvoid G_BeginRecording (void) \n{ \n    int             i; \n\n    //!\n    // @category demo\n    //\n    // Record a high resolution \"Doom 1.91\" demo.\n    //\n\n    longtics = M_CheckParm(\"-longtics\") != 0;\n\n    // If not recording a longtics demo, record in low res\n\n    lowres_turn = !longtics;\n    \n    demo_p = demobuffer;\n\t\n    // Save the right version code for this demo\n \n    if (longtics)\n    {\n        *demo_p++ = DOOM_191_VERSION;\n    }\n    else\n    {\n        *demo_p++ = G_VanillaVersionCode();\n    }\n\n    *demo_p++ = gameskill; \n    *demo_p++ = gameepisode; \n    *demo_p++ = gamemap; \n    *demo_p++ = deathmatch; \n    *demo_p++ = respawnparm;\n    *demo_p++ = fastparm;\n    *demo_p++ = nomonsters;\n    *demo_p++ = consoleplayer;\n\t \n    for (i=0 ; i<MAXPLAYERS ; i++) \n\t*demo_p++ = playeringame[i]; \t\t \n} \n \n\n//\n// G_PlayDemo \n//\n\nchar*\tdefdemoname; \n \nvoid G_DeferedPlayDemo (char* name) \n{ \n    defdemoname = name; \n    gameaction = ga_playdemo; \n} \n\n// Generate a string describing a demo version\n\nstatic char *DemoVersionDescription(int version)\n{\n    static char resultbuf[16];\n\n    switch (version)\n    {\n        case 104:\n            return \"v1.4\";\n        case 105:\n            return \"v1.5\";\n        case 106:\n            return \"v1.6/v1.666\";\n        case 107:\n            return \"v1.7/v1.7a\";\n        case 108:\n            return \"v1.8\";\n        case 109:\n            return \"v1.9\";\n        default:\n            break;\n    }\n\n    // Unknown version.  Perhaps this is a pre-v1.4 IWAD?  If the version\n    // byte is in the range 0-4 then it can be a v1.0-v1.2 demo.\n\n    if (version >= 0 && version <= 4)\n    {\n        return \"v1.0/v1.1/v1.2\";\n    }\n    else\n    {\n        M_snprintf(resultbuf, sizeof(resultbuf),\n                   \"%i.%i (unknown)\", version / 100, version % 100);\n        return resultbuf;\n    }\n}\n\nvoid G_DoPlayDemo (void) \n{ \n    skill_t skill; \n    int             i, episode, map; \n    int demoversion;\n\t \n    gameaction = ga_nothing; \n    demobuffer = demo_p = W_CacheLumpName (defdemoname, PU_STATIC); \n\n    demoversion = *demo_p++;\n\n    if (demoversion == G_VanillaVersionCode())\n    {\n        longtics = false;\n    }\n    else if (demoversion == DOOM_191_VERSION)\n    {\n        // demo recorded with cph's modified \"v1.91\" doom exe\n        longtics = true;\n    }\n    else\n    {\n        char *message = \"Demo is from a different game version!\\n\"\n                        \"(read %i, should be %i)\\n\"\n                        \"\\n\"\n                        \"*** You may need to upgrade your version \"\n                            \"of Doom to v1.9. ***\\n\"\n                        \"    See: https://www.doomworld.com/classicdoom\"\n                                  \"/info/patches.php\\n\"\n                        \"    This appears to be %s.\";\n\n        //I_Error(message, demoversion, G_VanillaVersionCode(),\n        printf(message, demoversion, G_VanillaVersionCode(),\n                         DemoVersionDescription(demoversion));\n    }\n    \n    skill = *demo_p++; \n    episode = *demo_p++; \n    map = *demo_p++; \n    deathmatch = *demo_p++;\n    respawnparm = *demo_p++;\n    fastparm = *demo_p++;\n    nomonsters = *demo_p++;\n    consoleplayer = *demo_p++;\n\t\n    for (i=0 ; i<MAXPLAYERS ; i++) \n\tplayeringame[i] = *demo_p++; \n\n    if (playeringame[1] || M_CheckParm(\"-solo-net\") > 0\n                        || M_CheckParm(\"-netdemo\") > 0)\n    {\n\tnetgame = true;\n\tnetdemo = true;\n    }\n\n    // don't spend a lot of time in loadlevel \n    precache = false;\n    G_InitNew (skill, episode, map); \n    precache = true; \n    starttime = I_GetTime (); \n\n    usergame = false; \n    demoplayback = true; \n} \n\n//\n// G_TimeDemo \n//\nvoid G_TimeDemo (char* name) \n{\n    //!\n    // @vanilla \n    //\n    // Disable rendering the screen entirely.\n    //\n\n    nodrawers = M_CheckParm (\"-nodraw\"); \n\n    timingdemo = true; \n    singletics = true; \n\n    defdemoname = name; \n    gameaction = ga_playdemo; \n} \n \n \n/* \n=================== \n= \n= G_CheckDemoStatus \n= \n= Called after a death or level completion to allow demos to be cleaned up \n= Returns true if a new demo loop action will take place \n=================== \n*/ \n \nboolean G_CheckDemoStatus (void) \n{ \n    int             endtime; \n\t \n    if (timingdemo) \n    { \n        float fps;\n        int realtics;\n\n\tendtime = I_GetTime (); \n        realtics = endtime - starttime;\n        fps = ((float) gametic * TICRATE) / realtics;\n\n        // Prevent recursive calls\n        timingdemo = false;\n        demoplayback = false;\n\n\tI_Error (\"timed %i gametics in %i realtics (%f fps)\",\n                 gametic, realtics, fps);\n    } \n\t \n    if (demoplayback) \n    { \n        W_ReleaseLumpName(defdemoname);\n\tdemoplayback = false; \n\tnetdemo = false;\n\tnetgame = false;\n\tdeathmatch = false;\n\tplayeringame[1] = playeringame[2] = playeringame[3] = 0;\n\trespawnparm = false;\n\tfastparm = false;\n\tnomonsters = false;\n\tconsoleplayer = 0;\n        \n        if (singledemo) \n            I_Quit (); \n        else \n            D_AdvanceDemo (); \n\n\treturn true; \n    } \n \n    if (demorecording) \n    { \n\t*demo_p++ = DEMOMARKER; \n\tM_WriteFile (demoname, demobuffer, demo_p - demobuffer); \n\tZ_Free (demobuffer); \n\tdemorecording = false; \n\tI_Error (\"Demo %s recorded\",demoname); \n    } \n\t \n    return false; \n} \n \n \n \n"
  },
  {
    "path": "fbdoom/g_game.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//   Duh.\n// \n\n\n#ifndef __G_GAME__\n#define __G_GAME__\n\n#include \"doomdef.h\"\n#include \"d_event.h\"\n#include \"d_ticcmd.h\"\n\n\n//\n// GAME\n//\nvoid G_DeathMatchSpawnPlayer (int playernum);\n\nvoid G_InitNew (skill_t skill, int episode, int map);\n\n// Can be called by the startup code or M_Responder.\n// A normal game starts at map 1,\n// but a warp test can start elsewhere\nvoid G_DeferedInitNew (skill_t skill, int episode, int map);\n\nvoid G_DeferedPlayDemo (char* demo);\n\n// Can be called by the startup code or M_Responder,\n// calls P_SetupLevel or W_EnterWorld.\nvoid G_LoadGame (char* name);\n\nvoid G_DoLoadGame (void);\n\n// Called by M_Responder.\nvoid G_SaveGame (int slot, char* description);\n\n// Only called by startup code.\nvoid G_RecordDemo (char* name);\n\nvoid G_BeginRecording (void);\n\nvoid G_PlayDemo (char* name);\nvoid G_TimeDemo (char* name);\nboolean G_CheckDemoStatus (void);\n\nvoid G_ExitLevel (void);\nvoid G_SecretExitLevel (void);\n\nvoid G_WorldDone (void);\n\n// Read current data from inputs and build a player movement command.\n\nvoid G_BuildTiccmd (ticcmd_t *cmd, int maketic); \n\nvoid G_Ticker (void);\nboolean G_Responder (event_t*\tev);\n\nvoid G_ScreenShot (void);\n\nvoid G_DrawMouseSpeedBox(void);\nint G_VanillaVersionCode(void);\n\nextern int vanilla_savegame_limit;\nextern int vanilla_demo_limit;\n#endif\n\n"
  },
  {
    "path": "fbdoom/gusconf.c",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     GUS emulation code.\n//\n//     Actually emulating a GUS is far too much work; fortunately\n//     GUS \"emulation\" already exists in the form of Timidity, which\n//     supports GUS patch files. This code therefore converts Doom's\n//     DMXGUS lump into an equivalent Timidity configuration file.\n//\n\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\n#define MAX_INSTRUMENTS 256\n\ntypedef struct\n{\n    char *patch_names[MAX_INSTRUMENTS];\n    int mapping[MAX_INSTRUMENTS];\n} gus_config_t;\n\nchar *gus_patch_path = \"\";\nunsigned int gus_ram_kb = 1024;\n\nstatic unsigned int MappingIndex(void)\n{\n    unsigned int result = gus_ram_kb / 256;\n\n    if (result < 1)\n    {\n        return 1;\n    }\n    else if (result > 4)\n    {\n        return 4;\n    }\n    else\n    {\n        return result;\n    }\n}\n\nstatic int SplitLine(char *line, char **fields, unsigned int max_fields)\n{\n    unsigned int num_fields;\n    char *p;\n\n    fields[0] = line;\n    num_fields = 1;\n\n    for (p = line; *p != '\\0'; ++p)\n    {\n        if (*p == ',')\n        {\n            *p = '\\0';\n\n            // Skip spaces following the comma.\n            do\n            {\n                ++p;\n            } while (*p != '\\0' && isspace(*p));\n\n            fields[num_fields] = p;\n            ++num_fields;\n            --p;\n\n            if (num_fields >= max_fields)\n            {\n                break;\n            }\n        }\n        else if (*p == '#')\n        {\n            *p = '\\0';\n            break;\n        }\n    }\n\n    // Strip off trailing whitespace from the end of the line.\n    p = fields[num_fields - 1] + strlen(fields[num_fields - 1]);\n    while (p > fields[num_fields - 1] && isspace(*(p - 1)))\n    {\n        --p;\n        *p = '\\0';\n    }\n\n    return num_fields;\n}\n\nstatic void ParseLine(gus_config_t *config, char *line)\n{\n    char *fields[6];\n    unsigned int num_fields;\n    unsigned int instr_id, mapped_id;\n\n    num_fields = SplitLine(line, fields, 6);\n\n    if (num_fields < 6)\n    {\n        return;\n    }\n\n    instr_id = atoi(fields[0]);\n    mapped_id = atoi(fields[MappingIndex()]);\n\n    free(config->patch_names[instr_id]);\n    config->patch_names[instr_id] = strdup(fields[5]);\n    config->mapping[instr_id] = mapped_id;\n}\n\nstatic void ParseDMXConfig(char *dmxconf, gus_config_t *config)\n{\n    char *p, *newline;\n    unsigned int i;\n\n    memset(config, 0, sizeof(gus_config_t));\n\n    for (i = 0; i < MAX_INSTRUMENTS; ++i)\n    {\n        config->mapping[i] = -1;\n    }\n\n    p = dmxconf;\n\n    for (;;)\n    {\n        newline = strchr(p, '\\n');\n\n        if (newline != NULL)\n        {\n            *newline = '\\0';\n        }\n\n        ParseLine(config, p);\n\n        if (newline == NULL)\n        {\n            break;\n        }\n        else\n        {\n            p = newline + 1;\n        }\n    }\n}\n\nstatic void FreeDMXConfig(gus_config_t *config)\n{\n    unsigned int i;\n\n    for (i = 0; i < MAX_INSTRUMENTS; ++i)\n    {\n        free(config->patch_names[i]);\n    }\n}\n\nstatic char *ReadDMXConfig(void)\n{\n    int lumpnum;\n    unsigned int len;\n    char *data;\n\n    // TODO: This should be chosen based on gamemode == commercial:\n\n    lumpnum = W_CheckNumForName(\"DMXGUS\");\n\n    if (lumpnum < 0)\n    {\n        lumpnum = W_GetNumForName(\"DMXGUSC\");\n    }\n\n    len = W_LumpLength(lumpnum);\n    data = Z_Malloc(len + 1, PU_STATIC, NULL);\n    W_ReadLump(lumpnum, data);\n\n    return data;\n}\n\nstatic boolean WriteTimidityConfig(char *path, gus_config_t *config)\n{\n    FILE *fstream;\n    unsigned int i;\n\n    fstream = fopen(path, \"w\");\n\n    if (fstream == NULL)\n    {\n        return false;\n    }\n\n    fprintf(fstream, \"# Autogenerated Timidity config.\\n\\n\");\n\n    fprintf(fstream, \"dir %s\\n\", gus_patch_path);\n\n    fprintf(fstream, \"\\nbank 0\\n\\n\");\n\n    for (i = 0; i < 128; ++i)\n    {\n        if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS\n         && config->patch_names[config->mapping[i]] != NULL)\n        {\n            fprintf(fstream, \"%i %s\\n\",\n                    i, config->patch_names[config->mapping[i]]);\n        }\n    }\n\n    fprintf(fstream, \"\\ndrumset 0\\n\\n\");\n\n    for (i = 128 + 25; i < MAX_INSTRUMENTS; ++i)\n    {\n        if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS\n         && config->patch_names[config->mapping[i]] != NULL)\n        {\n            fprintf(fstream, \"%i %s\\n\",\n                    i - 128, config->patch_names[config->mapping[i]]);\n        }\n    }\n\n    fprintf(fstream, \"\\n\");\n\n    fclose(fstream);\n\n    return true;\n}\n\nboolean GUS_WriteConfig(char *path)\n{\n    boolean result;\n    char *dmxconf;\n    gus_config_t config;\n\n    if (!strcmp(gus_patch_path, \"\"))\n    {\n        printf(\"You haven't configured gus_patch_path.\\n\");\n        printf(\"gus_patch_path needs to point to the location of \"\n               \"your GUS patch set.\\n\"\n               \"To get a copy of the \\\"standard\\\" GUS patches, \"\n               \"download a copy of dgguspat.zip.\\n\");\n\n        return false;\n    }\n\n    dmxconf = ReadDMXConfig();\n    ParseDMXConfig(dmxconf, &config);\n\n    result = WriteTimidityConfig(path, &config);\n\n    FreeDMXConfig(&config);\n    Z_Free(dmxconf);\n\n    return result;\n}\n\n"
  },
  {
    "path": "fbdoom/gusconf.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     GUS emulation code.\n//\n\n#ifndef __GUSCONF_H__\n#define __GUSCONF_H__\n\n#include \"doomtype.h\"\n\nextern char *gus_patch_path;\nextern unsigned int gus_ram_kb;\n\nboolean GUS_WriteConfig(char *path);\n\n#endif /* #ifndef __GUSCONF_H__ */\n\n"
  },
  {
    "path": "fbdoom/hu_lib.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:  heads-up text and input code\n//\n\n\n#include <ctype.h>\n\n#include \"doomdef.h\"\n#include \"doomkeys.h\"\n\n#include \"v_video.h\"\n#include \"i_swap.h\"\n\n#include \"hu_lib.h\"\n#include \"r_local.h\"\n#include \"r_draw.h\"\n\n// boolean : whether the screen is always erased\n#define noterased viewwindowx\n\nextern boolean\tautomapactive;\t// in AM_map.c\n\nvoid HUlib_init(void)\n{\n}\n\nvoid HUlib_clearTextLine(hu_textline_t* t)\n{\n    t->len = 0;\n    t->l[0] = 0;\n    t->needsupdate = true;\n}\n\nvoid\nHUlib_initTextLine\n( hu_textline_t*\tt,\n  int\t\t\tx,\n  int\t\t\ty,\n  patch_t**\t\tf,\n  int\t\t\tsc )\n{\n    t->x = x;\n    t->y = y;\n    t->f = f;\n    t->sc = sc;\n    HUlib_clearTextLine(t);\n}\n\nboolean\nHUlib_addCharToTextLine\n( hu_textline_t*\tt,\n  char\t\t\tch )\n{\n\n    if (t->len == HU_MAXLINELENGTH)\n\treturn false;\n    else\n    {\n\tt->l[t->len++] = ch;\n\tt->l[t->len] = 0;\n\tt->needsupdate = 4;\n\treturn true;\n    }\n\n}\n\nboolean HUlib_delCharFromTextLine(hu_textline_t* t)\n{\n\n    if (!t->len) return false;\n    else\n    {\n\tt->l[--t->len] = 0;\n\tt->needsupdate = 4;\n\treturn true;\n    }\n\n}\n\nvoid\nHUlib_drawTextLine\n( hu_textline_t*\tl,\n  boolean\t\tdrawcursor )\n{\n\n    int\t\t\ti;\n    int\t\t\tw;\n    int\t\t\tx;\n    unsigned char\tc;\n\n    // draw the new stuff\n    x = l->x;\n    for (i=0;i<l->len;i++)\n    {\n\tc = toupper((int)l->l[i]);\n\tif (c != ' '\n\t    && c >= l->sc\n\t    && c <= '_')\n\t{\n\t    w = SHORT(l->f[c - l->sc]->width);\n\t    if (x+w > SCREENWIDTH)\n\t\tbreak;\n\t    V_DrawPatchDirect(x, l->y, l->f[c - l->sc]);\n\t    x += w;\n\t}\n\telse\n\t{\n\t    x += 4;\n\t    if (x >= SCREENWIDTH)\n\t\tbreak;\n\t}\n    }\n\n    // draw the cursor if requested\n    if (drawcursor\n\t&& x + SHORT(l->f['_' - l->sc]->width) <= SCREENWIDTH)\n    {\n\tV_DrawPatchDirect(x, l->y, l->f['_' - l->sc]);\n    }\n}\n\n\n// sorta called by HU_Erase and just better darn get things straight\nvoid HUlib_eraseTextLine(hu_textline_t* l)\n{\n    int\t\t\tlh;\n    int\t\t\ty;\n    int\t\t\tyoffset;\n\n    // Only erases when NOT in automap and the screen is reduced,\n    // and the text must either need updating or refreshing\n    // (because of a recent change back from the automap)\n\n    if (!automapactive &&\n\tviewwindowx && l->needsupdate)\n    {\n\tlh = SHORT(l->f[0]->height) + 1;\n\tfor (y=l->y,yoffset=y*SCREENWIDTH ; y<l->y+lh ; y++,yoffset+=SCREENWIDTH)\n\t{\n\t    if (y < viewwindowy || y >= viewwindowy + viewheight)\n\t\tR_VideoErase(yoffset, SCREENWIDTH); // erase entire line\n\t    else\n\t    {\n\t\tR_VideoErase(yoffset, viewwindowx); // erase left border\n\t\tR_VideoErase(yoffset + viewwindowx + viewwidth, viewwindowx);\n\t\t// erase right border\n\t    }\n\t}\n    }\n\n    if (l->needsupdate) l->needsupdate--;\n\n}\n\nvoid\nHUlib_initSText\n( hu_stext_t*\ts,\n  int\t\tx,\n  int\t\ty,\n  int\t\th,\n  patch_t**\tfont,\n  int\t\tstartchar,\n  boolean*\ton )\n{\n\n    int i;\n\n    s->h = h;\n    s->on = on;\n    s->laston = true;\n    s->cl = 0;\n    for (i=0;i<h;i++)\n\tHUlib_initTextLine(&s->l[i],\n\t\t\t   x, y - i*(SHORT(font[0]->height)+1),\n\t\t\t   font, startchar);\n\n}\n\nvoid HUlib_addLineToSText(hu_stext_t* s)\n{\n\n    int i;\n\n    // add a clear line\n    if (++s->cl == s->h)\n\ts->cl = 0;\n    HUlib_clearTextLine(&s->l[s->cl]);\n\n    // everything needs updating\n    for (i=0 ; i<s->h ; i++)\n\ts->l[i].needsupdate = 4;\n\n}\n\nvoid\nHUlib_addMessageToSText\n( hu_stext_t*\ts,\n  char*\t\tprefix,\n  char*\t\tmsg )\n{\n    HUlib_addLineToSText(s);\n    if (prefix)\n\twhile (*prefix)\n\t    HUlib_addCharToTextLine(&s->l[s->cl], *(prefix++));\n\n    while (*msg)\n\tHUlib_addCharToTextLine(&s->l[s->cl], *(msg++));\n}\n\nvoid HUlib_drawSText(hu_stext_t* s)\n{\n    int i, idx;\n    hu_textline_t *l;\n\n    if (!*s->on)\n\treturn; // if not on, don't draw\n\n    // draw everything\n    for (i=0 ; i<s->h ; i++)\n    {\n\tidx = s->cl - i;\n\tif (idx < 0)\n\t    idx += s->h; // handle queue of lines\n\t\n\tl = &s->l[idx];\n\n\t// need a decision made here on whether to skip the draw\n\tHUlib_drawTextLine(l, false); // no cursor, please\n    }\n\n}\n\nvoid HUlib_eraseSText(hu_stext_t* s)\n{\n\n    int i;\n\n    for (i=0 ; i<s->h ; i++)\n    {\n\tif (s->laston && !*s->on)\n\t    s->l[i].needsupdate = 4;\n\tHUlib_eraseTextLine(&s->l[i]);\n    }\n    s->laston = *s->on;\n\n}\n\nvoid\nHUlib_initIText\n( hu_itext_t*\tit,\n  int\t\tx,\n  int\t\ty,\n  patch_t**\tfont,\n  int\t\tstartchar,\n  boolean*\ton )\n{\n    it->lm = 0; // default left margin is start of text\n    it->on = on;\n    it->laston = true;\n    HUlib_initTextLine(&it->l, x, y, font, startchar);\n}\n\n\n// The following deletion routines adhere to the left margin restriction\nvoid HUlib_delCharFromIText(hu_itext_t* it)\n{\n    if (it->l.len != it->lm)\n\tHUlib_delCharFromTextLine(&it->l);\n}\n\nvoid HUlib_eraseLineFromIText(hu_itext_t* it)\n{\n    while (it->lm != it->l.len)\n\tHUlib_delCharFromTextLine(&it->l);\n}\n\n// Resets left margin as well\nvoid HUlib_resetIText(hu_itext_t* it)\n{\n    it->lm = 0;\n    HUlib_clearTextLine(&it->l);\n}\n\nvoid\nHUlib_addPrefixToIText\n( hu_itext_t*\tit,\n  char*\t\tstr )\n{\n    while (*str)\n\tHUlib_addCharToTextLine(&it->l, *(str++));\n    it->lm = it->l.len;\n}\n\n// wrapper function for handling general keyed input.\n// returns true if it ate the key\nboolean\nHUlib_keyInIText\n( hu_itext_t*\tit,\n  unsigned char ch )\n{\n    ch = toupper(ch);\n\n    if (ch >= ' ' && ch <= '_') \n  \tHUlib_addCharToTextLine(&it->l, (char) ch);\n    else \n\tif (ch == KEY_BACKSPACE) \n\t    HUlib_delCharFromIText(it);\n\telse \n\t    if (ch != KEY_ENTER) \n\t\treturn false; // did not eat key\n\n    return true; // ate the key\n\n}\n\nvoid HUlib_drawIText(hu_itext_t* it)\n{\n\n    hu_textline_t *l = &it->l;\n\n    if (!*it->on)\n\treturn;\n    HUlib_drawTextLine(l, true); // draw the line w/ cursor\n\n}\n\nvoid HUlib_eraseIText(hu_itext_t* it)\n{\n    if (it->laston && !*it->on)\n\tit->l.needsupdate = 4;\n    HUlib_eraseTextLine(&it->l);\n    it->laston = *it->on;\n}\n\n"
  },
  {
    "path": "fbdoom/hu_lib.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:  none\n//\n\n#ifndef __HULIB__\n#define __HULIB__\n\n// We are referring to patches.\n#include \"r_defs.h\"\n\n// font stuff\n#define HU_CHARERASE\tKEY_BACKSPACE\n\n#define HU_MAXLINES\t\t4\n#define HU_MAXLINELENGTH\t80\n\n//\n// Typedefs of widgets\n//\n\n// Text Line widget\n//  (parent of Scrolling Text and Input Text widgets)\ntypedef struct\n{\n    // left-justified position of scrolling text window\n    int\t\tx;\n    int\t\ty;\n    \n    patch_t**\tf;\t\t\t// font\n    int\t\tsc;\t\t\t// start character\n    char\tl[HU_MAXLINELENGTH+1];\t// line of text\n    int\t\tlen;\t\t      \t// current line length\n\n    // whether this line needs to be udpated\n    int\t\tneedsupdate;\t      \n\n} hu_textline_t;\n\n\n\n// Scrolling Text window widget\n//  (child of Text Line widget)\ntypedef struct\n{\n    hu_textline_t\tl[HU_MAXLINES];\t// text lines to draw\n    int\t\t\th;\t\t// height in lines\n    int\t\t\tcl;\t\t// current line number\n\n    // pointer to boolean stating whether to update window\n    boolean*\t\ton;\n    boolean\t\tlaston;\t\t// last value of *->on.\n\n} hu_stext_t;\n\n\n\n// Input Text Line widget\n//  (child of Text Line widget)\ntypedef struct\n{\n    hu_textline_t\tl;\t\t// text line to input on\n\n     // left margin past which I am not to delete characters\n    int\t\t\tlm;\n\n    // pointer to boolean stating whether to update window\n    boolean*\t\ton; \n    boolean\t\tlaston; // last value of *->on;\n\n} hu_itext_t;\n\n\n//\n// Widget creation, access, and update routines\n//\n\n// initializes heads-up widget library\nvoid HUlib_init(void);\n\n//\n// textline code\n//\n\n// clear a line of text\nvoid\tHUlib_clearTextLine(hu_textline_t *t);\n\nvoid\tHUlib_initTextLine(hu_textline_t *t, int x, int y, patch_t **f, int sc);\n\n// returns success\nboolean HUlib_addCharToTextLine(hu_textline_t *t, char ch);\n\n// returns success\nboolean HUlib_delCharFromTextLine(hu_textline_t *t);\n\n// draws tline\nvoid\tHUlib_drawTextLine(hu_textline_t *l, boolean drawcursor);\n\n// erases text line\nvoid\tHUlib_eraseTextLine(hu_textline_t *l); \n\n\n//\n// Scrolling Text window widget routines\n//\n\n// ?\nvoid\nHUlib_initSText\n( hu_stext_t*\ts,\n  int\t\tx,\n  int\t\ty,\n  int\t\th,\n  patch_t**\tfont,\n  int\t\tstartchar,\n  boolean*\ton );\n\n// add a new line\nvoid HUlib_addLineToSText(hu_stext_t* s);  \n\n// ?\nvoid\nHUlib_addMessageToSText\n( hu_stext_t*\ts,\n  char*\t\tprefix,\n  char*\t\tmsg );\n\n// draws stext\nvoid HUlib_drawSText(hu_stext_t* s);\n\n// erases all stext lines\nvoid HUlib_eraseSText(hu_stext_t* s); \n\n// Input Text Line widget routines\nvoid\nHUlib_initIText\n( hu_itext_t*\tit,\n  int\t\tx,\n  int\t\ty,\n  patch_t**\tfont,\n  int\t\tstartchar,\n  boolean*\ton );\n\n// enforces left margin\nvoid HUlib_delCharFromIText(hu_itext_t* it);\n\n// enforces left margin\nvoid HUlib_eraseLineFromIText(hu_itext_t* it);\n\n// resets line and left margin\nvoid HUlib_resetIText(hu_itext_t* it);\n\n// left of left-margin\nvoid\nHUlib_addPrefixToIText\n( hu_itext_t*\tit,\n  char*\t\tstr );\n\n// whether eaten\nboolean\nHUlib_keyInIText\n( hu_itext_t*\tit,\n  unsigned char ch );\n\nvoid HUlib_drawIText(hu_itext_t* it);\n\n// erases all itext lines\nvoid HUlib_eraseIText(hu_itext_t* it); \n\n#endif\n"
  },
  {
    "path": "fbdoom/hu_stuff.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:  Heads-up displays\n//\n\n\n#include <ctype.h>\n\n#include \"doomdef.h\"\n#include \"doomkeys.h\"\n\n#include \"z_zone.h\"\n\n#include \"deh_main.h\"\n#include \"i_swap.h\"\n#include \"i_video.h\"\n\n#include \"hu_stuff.h\"\n#include \"hu_lib.h\"\n#include \"m_controls.h\"\n#include \"m_misc.h\"\n#include \"w_wad.h\"\n\n#include \"s_sound.h\"\n\n#include \"doomstat.h\"\n\n// Data.\n#include \"dstrings.h\"\n#include \"sounds.h\"\n\n//\n// Locally used constants, shortcuts.\n//\n#define HU_TITLE\t(mapnames[(gameepisode-1)*9+gamemap-1])\n#define HU_TITLE2\t(mapnames_commercial[gamemap-1])\n#define HU_TITLEP\t(mapnames_commercial[gamemap-1 + 32])\n#define HU_TITLET\t(mapnames_commercial[gamemap-1 + 64])\n#define HU_TITLE_CHEX   (mapnames[gamemap - 1])\n#define HU_TITLEHEIGHT\t1\n#define HU_TITLEX\t0\n#define HU_TITLEY\t(167 - SHORT(hu_font[0]->height))\n\n#define HU_INPUTTOGGLE\t't'\n#define HU_INPUTX\tHU_MSGX\n#define HU_INPUTY\t(HU_MSGY + HU_MSGHEIGHT*(SHORT(hu_font[0]->height) +1))\n#define HU_INPUTWIDTH\t64\n#define HU_INPUTHEIGHT\t1\n\n\n\nchar *chat_macros[10] =\n{\n    HUSTR_CHATMACRO0,\n    HUSTR_CHATMACRO1,\n    HUSTR_CHATMACRO2,\n    HUSTR_CHATMACRO3,\n    HUSTR_CHATMACRO4,\n    HUSTR_CHATMACRO5,\n    HUSTR_CHATMACRO6,\n    HUSTR_CHATMACRO7,\n    HUSTR_CHATMACRO8,\n    HUSTR_CHATMACRO9\n};\n\nchar*\tplayer_names[] =\n{\n    HUSTR_PLRGREEN,\n    HUSTR_PLRINDIGO,\n    HUSTR_PLRBROWN,\n    HUSTR_PLRRED\n};\n\nchar\t\t\tchat_char; // remove later.\nstatic player_t*\tplr;\npatch_t*\t\thu_font[HU_FONTSIZE];\nstatic hu_textline_t\tw_title;\nboolean\t\t\tchat_on;\nstatic hu_itext_t\tw_chat;\nstatic boolean\t\talways_off = false;\nstatic char\t\tchat_dest[MAXPLAYERS];\nstatic hu_itext_t w_inputbuffer[MAXPLAYERS];\n\nstatic boolean\t\tmessage_on;\nboolean\t\t\tmessage_dontfuckwithme;\nstatic boolean\t\tmessage_nottobefuckedwith;\n\nstatic hu_stext_t\tw_message;\nstatic int\t\tmessage_counter;\n\nextern int\t\tshowMessages;\n\nstatic boolean\t\theadsupactive = false;\n\n//\n// Builtin map names.\n// The actual names can be found in DStrings.h.\n//\n\nchar*\tmapnames[] =\t// DOOM shareware/registered/retail (Ultimate) names.\n{\n\n    HUSTR_E1M1,\n    HUSTR_E1M2,\n    HUSTR_E1M3,\n    HUSTR_E1M4,\n    HUSTR_E1M5,\n    HUSTR_E1M6,\n    HUSTR_E1M7,\n    HUSTR_E1M8,\n    HUSTR_E1M9,\n\n    HUSTR_E2M1,\n    HUSTR_E2M2,\n    HUSTR_E2M3,\n    HUSTR_E2M4,\n    HUSTR_E2M5,\n    HUSTR_E2M6,\n    HUSTR_E2M7,\n    HUSTR_E2M8,\n    HUSTR_E2M9,\n\n    HUSTR_E3M1,\n    HUSTR_E3M2,\n    HUSTR_E3M3,\n    HUSTR_E3M4,\n    HUSTR_E3M5,\n    HUSTR_E3M6,\n    HUSTR_E3M7,\n    HUSTR_E3M8,\n    HUSTR_E3M9,\n\n    HUSTR_E4M1,\n    HUSTR_E4M2,\n    HUSTR_E4M3,\n    HUSTR_E4M4,\n    HUSTR_E4M5,\n    HUSTR_E4M6,\n    HUSTR_E4M7,\n    HUSTR_E4M8,\n    HUSTR_E4M9,\n\n    \"NEWLEVEL\",\n    \"NEWLEVEL\",\n    \"NEWLEVEL\",\n    \"NEWLEVEL\",\n    \"NEWLEVEL\",\n    \"NEWLEVEL\",\n    \"NEWLEVEL\",\n    \"NEWLEVEL\",\n    \"NEWLEVEL\"\n};\n\n// List of names for levels in commercial IWADs\n// (doom2.wad, plutonia.wad, tnt.wad).  These are stored in a\n// single large array; WADs like pl2.wad have a MAP33, and rely on\n// the layout in the Vanilla executable, where it is possible to\n// overflow the end of one array into the next.\n\nchar *mapnames_commercial[] =\n{\n    // DOOM 2 map names.\n\n    HUSTR_1,\n    HUSTR_2,\n    HUSTR_3,\n    HUSTR_4,\n    HUSTR_5,\n    HUSTR_6,\n    HUSTR_7,\n    HUSTR_8,\n    HUSTR_9,\n    HUSTR_10,\n    HUSTR_11,\n\t\n    HUSTR_12,\n    HUSTR_13,\n    HUSTR_14,\n    HUSTR_15,\n    HUSTR_16,\n    HUSTR_17,\n    HUSTR_18,\n    HUSTR_19,\n    HUSTR_20,\n\t\n    HUSTR_21,\n    HUSTR_22,\n    HUSTR_23,\n    HUSTR_24,\n    HUSTR_25,\n    HUSTR_26,\n    HUSTR_27,\n    HUSTR_28,\n    HUSTR_29,\n    HUSTR_30,\n    HUSTR_31,\n    HUSTR_32,\n\n    // Plutonia WAD map names.\n\n    PHUSTR_1,\n    PHUSTR_2,\n    PHUSTR_3,\n    PHUSTR_4,\n    PHUSTR_5,\n    PHUSTR_6,\n    PHUSTR_7,\n    PHUSTR_8,\n    PHUSTR_9,\n    PHUSTR_10,\n    PHUSTR_11,\n\t\n    PHUSTR_12,\n    PHUSTR_13,\n    PHUSTR_14,\n    PHUSTR_15,\n    PHUSTR_16,\n    PHUSTR_17,\n    PHUSTR_18,\n    PHUSTR_19,\n    PHUSTR_20,\n\t\n    PHUSTR_21,\n    PHUSTR_22,\n    PHUSTR_23,\n    PHUSTR_24,\n    PHUSTR_25,\n    PHUSTR_26,\n    PHUSTR_27,\n    PHUSTR_28,\n    PHUSTR_29,\n    PHUSTR_30,\n    PHUSTR_31,\n    PHUSTR_32,\n    \n    // TNT WAD map names.\n\n    THUSTR_1,\n    THUSTR_2,\n    THUSTR_3,\n    THUSTR_4,\n    THUSTR_5,\n    THUSTR_6,\n    THUSTR_7,\n    THUSTR_8,\n    THUSTR_9,\n    THUSTR_10,\n    THUSTR_11,\n\t\n    THUSTR_12,\n    THUSTR_13,\n    THUSTR_14,\n    THUSTR_15,\n    THUSTR_16,\n    THUSTR_17,\n    THUSTR_18,\n    THUSTR_19,\n    THUSTR_20,\n\t\n    THUSTR_21,\n    THUSTR_22,\n    THUSTR_23,\n    THUSTR_24,\n    THUSTR_25,\n    THUSTR_26,\n    THUSTR_27,\n    THUSTR_28,\n    THUSTR_29,\n    THUSTR_30,\n    THUSTR_31,\n    THUSTR_32\n};\n\nvoid HU_Init(void)\n{\n\n    int\t\ti;\n    int\t\tj;\n    char\tbuffer[9];\n\n    // load the heads-up font\n    j = HU_FONTSTART;\n    for (i=0;i<HU_FONTSIZE;i++)\n    {\n\tDEH_snprintf(buffer, 9, \"STCFN%.3d\", j++);\n\thu_font[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);\n    }\n\n}\n\nvoid HU_Stop(void)\n{\n    headsupactive = false;\n}\n\nvoid HU_Start(void)\n{\n\n    int\t\ti;\n    char*\ts;\n\n    if (headsupactive)\n\tHU_Stop();\n\n    plr = &players[consoleplayer];\n    message_on = false;\n    message_dontfuckwithme = false;\n    message_nottobefuckedwith = false;\n    chat_on = false;\n\n    // create the message widget\n    HUlib_initSText(&w_message,\n\t\t    HU_MSGX, HU_MSGY, HU_MSGHEIGHT,\n\t\t    hu_font,\n\t\t    HU_FONTSTART, &message_on);\n\n    // create the map title widget\n    HUlib_initTextLine(&w_title,\n\t\t       HU_TITLEX, HU_TITLEY,\n\t\t       hu_font,\n\t\t       HU_FONTSTART);\n    \n    switch ( logical_gamemission )\n    {\n      case doom:\n\ts = HU_TITLE;\n\tbreak;\n      case doom2:\n\t s = HU_TITLE2;\n\t break;\n      case pack_plut:\n\ts = HU_TITLEP;\n\tbreak;\n      case pack_tnt:\n\ts = HU_TITLET;\n\tbreak;\n      default:\n         s = \"Unknown level\";\n         break;\n    }\n\n    // Chex.exe always uses the episode 1 level title\n    // eg. E2M1 gives the title for E1M1\n\n    if (gameversion == exe_chex)\n    {\n        s = HU_TITLE_CHEX;\n    }\n\n    // dehacked substitution to get modified level name\n\n    s = DEH_String(s);\n    \n    while (*s)\n\tHUlib_addCharToTextLine(&w_title, *(s++));\n\n    // create the chat widget\n    HUlib_initIText(&w_chat,\n\t\t    HU_INPUTX, HU_INPUTY,\n\t\t    hu_font,\n\t\t    HU_FONTSTART, &chat_on);\n\n    // create the inputbuffer widgets\n    for (i=0 ; i<MAXPLAYERS ; i++)\n\tHUlib_initIText(&w_inputbuffer[i], 0, 0, 0, 0, &always_off);\n\n    headsupactive = true;\n\n}\n\nvoid HU_Drawer(void)\n{\n\n    HUlib_drawSText(&w_message);\n    HUlib_drawIText(&w_chat);\n    if (automapactive)\n\tHUlib_drawTextLine(&w_title, false);\n\n}\n\nvoid HU_Erase(void)\n{\n\n    HUlib_eraseSText(&w_message);\n    HUlib_eraseIText(&w_chat);\n    HUlib_eraseTextLine(&w_title);\n\n}\n\nvoid HU_Ticker(void)\n{\n\n    int i, rc;\n    char c;\n\n    // tick down message counter if message is up\n    if (message_counter && !--message_counter)\n    {\n\tmessage_on = false;\n\tmessage_nottobefuckedwith = false;\n    }\n\n    if (showMessages || message_dontfuckwithme)\n    {\n\n\t// display message if necessary\n\tif ((plr->message && !message_nottobefuckedwith)\n\t    || (plr->message && message_dontfuckwithme))\n\t{\n\t    HUlib_addMessageToSText(&w_message, 0, plr->message);\n\t    plr->message = 0;\n\t    message_on = true;\n\t    message_counter = HU_MSGTIMEOUT;\n\t    message_nottobefuckedwith = message_dontfuckwithme;\n\t    message_dontfuckwithme = 0;\n\t}\n\n    } // else message_on = false;\n\n    // check for incoming chat characters\n    if (netgame)\n    {\n\tfor (i=0 ; i<MAXPLAYERS; i++)\n\t{\n\t    if (!playeringame[i])\n\t\tcontinue;\n\t    if (i != consoleplayer\n\t\t&& (c = players[i].cmd.chatchar))\n\t    {\n\t\tif (c <= HU_BROADCAST)\n\t\t    chat_dest[i] = c;\n\t\telse\n\t\t{\n\t\t    rc = HUlib_keyInIText(&w_inputbuffer[i], c);\n\t\t    if (rc && c == KEY_ENTER)\n\t\t    {\n\t\t\tif (w_inputbuffer[i].l.len\n\t\t\t    && (chat_dest[i] == consoleplayer+1\n\t\t\t\t|| chat_dest[i] == HU_BROADCAST))\n\t\t\t{\n\t\t\t    HUlib_addMessageToSText(&w_message,\n\t\t\t\t\t\t    DEH_String(player_names[i]),\n\t\t\t\t\t\t    w_inputbuffer[i].l.l);\n\t\t\t    \n\t\t\t    message_nottobefuckedwith = true;\n\t\t\t    message_on = true;\n\t\t\t    message_counter = HU_MSGTIMEOUT;\n\t\t\t    if ( gamemode == commercial )\n\t\t\t      S_StartSound(0, sfx_radio);\n\t\t\t    else\n\t\t\t      S_StartSound(0, sfx_tink);\n\t\t\t}\n\t\t\tHUlib_resetIText(&w_inputbuffer[i]);\n\t\t    }\n\t\t}\n\t\tplayers[i].cmd.chatchar = 0;\n\t    }\n\t}\n    }\n\n}\n\n#define QUEUESIZE\t\t128\n\nstatic char\tchatchars[QUEUESIZE];\nstatic int\thead = 0;\nstatic int\ttail = 0;\n\n\nvoid HU_queueChatChar(char c)\n{\n    if (((head + 1) & (QUEUESIZE-1)) == tail)\n    {\n\tplr->message = DEH_String(HUSTR_MSGU);\n    }\n    else\n    {\n\tchatchars[head] = c;\n\thead = (head + 1) & (QUEUESIZE-1);\n    }\n}\n\nchar HU_dequeueChatChar(void)\n{\n    char c;\n\n    if (head != tail)\n    {\n\tc = chatchars[tail];\n\ttail = (tail + 1) & (QUEUESIZE-1);\n    }\n    else\n    {\n\tc = 0;\n    }\n\n    return c;\n}\n\nboolean HU_Responder(event_t *ev)\n{\n\n    static char\t\tlastmessage[HU_MAXLINELENGTH+1];\n    char*\t\tmacromessage;\n    boolean\t\teatkey = false;\n    static boolean\taltdown = false;\n    unsigned char \tc;\n    int\t\t\ti;\n    int\t\t\tnumplayers;\n    \n    static int\t\tnum_nobrainers = 0;\n\n    numplayers = 0;\n    for (i=0 ; i<MAXPLAYERS ; i++)\n\tnumplayers += playeringame[i];\n\n    if (ev->data1 == KEY_RSHIFT)\n    {\n\treturn false;\n    }\n    else if (ev->data1 == KEY_RALT || ev->data1 == KEY_LALT)\n    {\n\taltdown = ev->type == ev_keydown;\n\treturn false;\n    }\n\n    if (ev->type != ev_keydown)\n\treturn false;\n\n    if (!chat_on)\n    {\n\tif (ev->data1 == key_message_refresh)\n\t{\n\t    message_on = true;\n\t    message_counter = HU_MSGTIMEOUT;\n\t    eatkey = true;\n\t}\n\telse if (netgame && ev->data2 == key_multi_msg)\n\t{\n\t    eatkey = chat_on = true;\n\t    HUlib_resetIText(&w_chat);\n\t    HU_queueChatChar(HU_BROADCAST);\n\t}\n\telse if (netgame && numplayers > 2)\n\t{\n\t    for (i=0; i<MAXPLAYERS ; i++)\n\t    {\n\t\tif (ev->data2 == key_multi_msgplayer[i])\n\t\t{\n\t\t    if (playeringame[i] && i!=consoleplayer)\n\t\t    {\n\t\t\teatkey = chat_on = true;\n\t\t\tHUlib_resetIText(&w_chat);\n\t\t\tHU_queueChatChar(i+1);\n\t\t\tbreak;\n\t\t    }\n\t\t    else if (i == consoleplayer)\n\t\t    {\n\t\t\tnum_nobrainers++;\n\t\t\tif (num_nobrainers < 3)\n\t\t\t    plr->message = DEH_String(HUSTR_TALKTOSELF1);\n\t\t\telse if (num_nobrainers < 6)\n\t\t\t    plr->message = DEH_String(HUSTR_TALKTOSELF2);\n\t\t\telse if (num_nobrainers < 9)\n\t\t\t    plr->message = DEH_String(HUSTR_TALKTOSELF3);\n\t\t\telse if (num_nobrainers < 32)\n\t\t\t    plr->message = DEH_String(HUSTR_TALKTOSELF4);\n\t\t\telse\n\t\t\t    plr->message = DEH_String(HUSTR_TALKTOSELF5);\n\t\t    }\n\t\t}\n\t    }\n\t}\n    }\n    else\n    {\n\t// send a macro\n\tif (altdown)\n\t{\n\t    c = ev->data1 - '0';\n\t    if (c > 9)\n\t\treturn false;\n\t    // fprintf(stderr, \"got here\\n\");\n\t    macromessage = chat_macros[c];\n\t    \n\t    // kill last message with a '\\n'\n\t    HU_queueChatChar(KEY_ENTER); // DEBUG!!!\n\t    \n\t    // send the macro message\n\t    while (*macromessage)\n\t\tHU_queueChatChar(*macromessage++);\n\t    HU_queueChatChar(KEY_ENTER);\n\t    \n            // leave chat mode and notify that it was sent\n            chat_on = false;\n            M_StringCopy(lastmessage, chat_macros[c], sizeof(lastmessage));\n            plr->message = lastmessage;\n            eatkey = true;\n\t}\n\telse\n\t{\n            c = ev->data2;\n\n\t    eatkey = HUlib_keyInIText(&w_chat, c);\n\t    if (eatkey)\n\t    {\n\t\t// static unsigned char buf[20]; // DEBUG\n\t\tHU_queueChatChar(c);\n\t\t\n\t\t// M_snprintf(buf, sizeof(buf), \"KEY: %d => %d\", ev->data1, c);\n\t\t//        plr->message = buf;\n\t    }\n\t    if (c == KEY_ENTER)\n\t    {\n\t\tchat_on = false;\n                if (w_chat.l.len)\n                {\n                    M_StringCopy(lastmessage, w_chat.l.l, sizeof(lastmessage));\n                    plr->message = lastmessage;\n                }\n\t    }\n\t    else if (c == KEY_ESCAPE)\n\t\tchat_on = false;\n\t}\n    }\n\n    return eatkey;\n\n}\n"
  },
  {
    "path": "fbdoom/hu_stuff.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:  Head up display\n//\n\n#ifndef __HU_STUFF_H__\n#define __HU_STUFF_H__\n\n#include \"d_event.h\"\n\n\n//\n// Globally visible constants.\n//\n#define HU_FONTSTART\t'!'\t// the first font characters\n#define HU_FONTEND\t'_'\t// the last font characters\n\n// Calculate # of glyphs in font.\n#define HU_FONTSIZE\t(HU_FONTEND - HU_FONTSTART + 1)\t\n\n#define HU_BROADCAST\t5\n\n#define HU_MSGX\t\t0\n#define HU_MSGY\t\t0\n#define HU_MSGWIDTH\t64\t// in characters\n#define HU_MSGHEIGHT\t1\t// in lines\n\n#define HU_MSGTIMEOUT\t(4*TICRATE)\n\n//\n// HEADS UP TEXT\n//\n\nvoid HU_Init(void);\nvoid HU_Start(void);\n\nboolean HU_Responder(event_t* ev);\n\nvoid HU_Ticker(void);\nvoid HU_Drawer(void);\nchar HU_dequeueChatChar(void);\nvoid HU_Erase(void);\n\nextern char *chat_macros[10];\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/i_cdmus.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 1993-2008 Raven Software\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n//\n// SDL implementation of the Hexen CD interface.\n//\n\n#include <stdio.h>\n\n#ifdef ORIGCODE\n#include \"SDL.h\"\n#include \"SDL_cdrom.h\"\n#endif\n\n#include \"doomtype.h\"\n\n#include \"i_cdmus.h\"\n\n#ifdef ORIGCODE\nstatic SDL_CD *cd_handle = NULL;\nstatic char *startup_error = NULL;\nstatic const char *cd_name = NULL;\n#endif\n\nint cd_Error;\n\nint I_CDMusInit(void)\n{\n#ifdef ORIGCODE\n    int drive_num = 0;\n\n    // The initialize function is re-invoked when the CD track play cheat\n    // is used, so use the opportunity to call SDL_CDStatus() to update\n    // the status of the drive.\n\n    if (cd_handle == NULL)\n    {\n        if (SDL_Init(SDL_INIT_CDROM) < 0)\n        {\n            startup_error = \"Failed to init CD subsystem.\";\n            cd_Error = 1;\n            return -1;\n        }\n\n        // TODO: config variable to control CDROM to use.\n\n        cd_handle = SDL_CDOpen(drive_num);\n\n        if (cd_handle == NULL)\n        {\n            startup_error = \"Failed to open CD-ROM drive.\";\n            cd_Error = 1;\n            return -1;\n        }\n\n        cd_name = SDL_CDName(drive_num);\n    }\n\n    if (SDL_CDStatus(cd_handle) == CD_ERROR)\n    {\n        startup_error = \"Failed to read CD status.\";\n        cd_Error = 1;\n        return -1;\n    }\n\n    if (!CD_INDRIVE(cd_handle->status))\n    {\n        startup_error = \"No CD in drive.\";\n        cd_Error = 1;\n        return -1;\n    }\n\n    cd_Error = 0;\n#endif\n    return 0;\n}\n\n// We cannot print status messages inline during startup, they must\n// be deferred until after I_CDMusInit has returned.\n\nvoid I_CDMusPrintStartup(void)\n{\n#ifdef ORIGCODE\n    if (cd_name != NULL)\n    {\n        printf(\"I_CDMusInit: Using CD-ROM drive: %s\\n\", cd_name);\n    }\n\n    if (startup_error != NULL)\n    {\n        fprintf(stderr, \"I_CDMusInit: %s\\n\", startup_error);\n    }\n#endif\n}\n\nint I_CDMusPlay(int track)\n{\n#ifdef ORIGCODE\n    int result;\n\n    if (cd_handle == NULL)\n    {\n        cd_Error = 1;\n        return -1;\n    }\n\n    // Play one track\n    // Track is indexed from 1.\n\n    result = SDL_CDPlayTracks(cd_handle, track - 1, 0, 1, 0);\n\n    cd_Error = 0;\n    return result;\n#else\n\treturn 0;\n#endif\n}\n\nint I_CDMusStop(void)\n{\n#ifdef ORIGCODE\n    int result;\n\n    result = SDL_CDStop(cd_handle);\n\n    cd_Error = 0;\n\n    return result;\n#else\n\treturn 0;\n#endif\n}\n\nint I_CDMusResume(void)\n{\n#ifdef ORIGCODE\n    int result;\n\n    result = SDL_CDResume(cd_handle);\n\n    cd_Error = 0;\n\n    return result;\n#else\n\treturn 0;\n#endif\n}\n\nint I_CDMusSetVolume(int volume)\n{\n    /* Not supported yet */\n\n    cd_Error = 0;\n\n    return 0;\n}\n\nint I_CDMusFirstTrack(void)\n{\n#ifdef ORIGCODE\n    int i;\n\n    if (cd_handle == NULL)\n    {\n        cd_Error = 1;\n        return -1;\n    }\n\n    // Find the first audio track.\n\n    for (i=0; i<cd_handle->numtracks; ++i) \n    {\n        if (cd_handle->track[i].type == SDL_AUDIO_TRACK)\n        {\n            cd_Error = 0;\n\n            // Tracks are indexed from 1.\n            return i + 1;\n        }\n    }\n\n    // Don't know?\n    cd_Error = 1;\n\n    return -1;\n#else\n\treturn 0;\n#endif\n}\n\nint I_CDMusLastTrack(void)\n{\n#ifdef ORIGCODE\n    if (cd_handle == NULL)\n    {\n        cd_Error = 1;\n        return -1;\n    }\n\n    cd_Error = 0;\n\n    return cd_handle->numtracks;\n#else\n\treturn 0;\n#endif\n}\n\nint I_CDMusTrackLength(int track_num)\n{\n#ifdef ORIGCODE\n    SDL_CDtrack *track;\n\n    if (cd_handle == NULL || track_num < 1 || track_num > cd_handle->numtracks)\n    {\n        cd_Error = 1;\n        return -1;\n    }\n\n    // Track number is indexed from 1.\n\n    track = &cd_handle->track[track_num - 1];\n\n    // Round up to the next second\n\n    cd_Error = 0;\n\n    return (track->length + CD_FPS - 1) / CD_FPS;\n#else\n\treturn 0;\n#endif\n}\n\n"
  },
  {
    "path": "fbdoom/i_cdmus.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 1993-2008 Raven Software\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n\n// i_cdmus.h\n\n#ifndef __ICDMUS__\n#define __ICDMUS__\n\n#define CDERR_NOTINSTALLED   10 // MSCDEX not installed\n#define CDERR_NOAUDIOSUPPORT 11 // CD-ROM Doesn't support audio\n#define CDERR_NOAUDIOTRACKS  12 // Current CD has no audio tracks\n#define CDERR_BADDRIVE       20 // Bad drive number\n#define CDERR_BADTRACK       21 // Bad track number\n#define CDERR_IOCTLBUFFMEM   22 // Not enough low memory for IOCTL\n#define CDERR_DEVREQBASE     100        // DevReq errors\n\nextern int cd_Error;\n\nint I_CDMusInit(void);\nvoid I_CDMusPrintStartup(void);\nint I_CDMusPlay(int track);\nint I_CDMusStop(void);\nint I_CDMusResume(void);\nint I_CDMusSetVolume(int volume);\nint I_CDMusFirstTrack(void);\nint I_CDMusLastTrack(void);\nint I_CDMusTrackLength(int track);\n\n#endif\n"
  },
  {
    "path": "fbdoom/i_endoom.c",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//    Exit text-mode ENDOOM screen.\n//\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"config.h\"\n#include \"doomtype.h\"\n#include \"i_video.h\"\n\n#ifdef ORIGCODE\n#include \"txt_main.h\"\n#endif\n\n#define ENDOOM_W 80\n#define ENDOOM_H 25\n\n// \n// Displays the text mode ending screen after the game quits\n//\n\nvoid I_Endoom(byte *endoom_data)\n{\n#ifdef ORIGCODE\n    unsigned char *screendata;\n    int y;\n    int indent;\n\n    // Set up text mode screen\n\n    TXT_Init();\n    I_InitWindowTitle();\n    I_InitWindowIcon();\n\n    // Write the data to the screen memory\n\n    screendata = TXT_GetScreenData();\n\n    indent = (ENDOOM_W - TXT_SCREEN_W) / 2;\n\n    for (y=0; y<TXT_SCREEN_H; ++y)\n    {\n        memcpy(screendata + (y * TXT_SCREEN_W * 2),\n               endoom_data + (y * ENDOOM_W + indent) * 2,\n               TXT_SCREEN_W * 2);\n    }\n\n    // Wait for a keypress\n\n    while (true)\n    {\n        TXT_UpdateScreen();\n\n        if (TXT_GetChar() > 0)\n        {\n            break;\n        }\n\n        TXT_Sleep(0);\n    }\n\n    // Shut down text mode screen\n\n    TXT_Shutdown();\n#endif\n}\n\n"
  },
  {
    "path": "fbdoom/i_endoom.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//    Exit text-mode ENDOOM screen.\n//\n\n\n#ifndef __I_ENDOOM__\n#define __I_ENDOOM__\n\n// Display the Endoom screen on shutdown.  Pass a pointer to the\n// ENDOOM lump.\n\nvoid I_Endoom(byte *data);\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/i_input_sdl.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tDOOM graphics stuff for SDL.\n//\n\n\n#include \"SDL/SDL.h\"\n#include <stdlib.h>\n#include <ctype.h>\n#include <math.h>\n#include <string.h>\n\n#include \"config.h\"\n#include \"deh_str.h\"\n#include \"doomtype.h\"\n#include \"doomkeys.h\"\n#include \"i_joystick.h\"\n#include \"i_system.h\"\n#include \"i_swap.h\"\n#include \"i_timer.h\"\n#include \"i_video.h\"\n#include \"i_scale.h\"\n#include \"m_argv.h\"\n#include \"m_config.h\"\n#include \"m_misc.h\"\n#include \"tables.h\"\n#include \"v_video.h\"\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\n// SDL surface for the screen.\n\nstatic SDL_Surface *screen;\n\n// If true, keyboard mapping is ignored, like in Vanilla Doom.\n// The sensible thing to do is to disable this if you have a non-US\n// keyboard.\n\nint vanilla_keyboard_mapping = true;\n\n// Is the shift key currently down?\n\nstatic int shiftdown = 0;\n// Lookup table for mapping ASCII characters to their equivalent when\n// shift is pressed on an American layout keyboard:\n\nstatic const char shiftxform[] =\n{\n    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,\n    11, 12, 13, 14, 15, 16, 17, 18, 19, 20,\n    21, 22, 23, 24, 25, 26, 27, 28, 29, 30,\n    31, ' ', '!', '\"', '#', '$', '%', '&',\n    '\"', // shift-'\n    '(', ')', '*', '+',\n    '<', // shift-,\n    '_', // shift--\n    '>', // shift-.\n    '?', // shift-/\n    ')', // shift-0\n    '!', // shift-1\n    '@', // shift-2\n    '#', // shift-3\n    '$', // shift-4\n    '%', // shift-5\n    '^', // shift-6\n    '&', // shift-7\n    '*', // shift-8\n    '(', // shift-9\n    ':',\n    ':', // shift-;\n    '<',\n    '+', // shift-=\n    '>', '?', '@',\n    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',\n    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',\n    '[', // shift-[\n    '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK\n    ']', // shift-]\n    '\"', '_',\n    '\\'', // shift-`\n    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',\n    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',\n    '{', '|', '}', '~', 127\n};\n\n\n//\n// Translates the SDL key\n//\n\nstatic int TranslateKey(SDL_keysym *sym)\n{\n    switch(sym->sym)\n    {\n      case SDLK_LEFT:\treturn KEY_LEFTARROW;\n      case SDLK_RIGHT:\treturn KEY_RIGHTARROW;\n      case SDLK_DOWN:\treturn KEY_DOWNARROW;\n      case SDLK_UP:\treturn KEY_UPARROW;\n      case SDLK_ESCAPE:\treturn KEY_ESCAPE;\n      case SDLK_RETURN:\treturn KEY_ENTER;\n      case SDLK_TAB:\treturn KEY_TAB;\n      case SDLK_F1:\treturn KEY_F1;\n      case SDLK_F2:\treturn KEY_F2;\n      case SDLK_F3:\treturn KEY_F3;\n      case SDLK_F4:\treturn KEY_F4;\n      case SDLK_F5:\treturn KEY_F5;\n      case SDLK_F6:\treturn KEY_F6;\n      case SDLK_F7:\treturn KEY_F7;\n      case SDLK_F8:\treturn KEY_F8;\n      case SDLK_F9:\treturn KEY_F9;\n      case SDLK_F10:\treturn KEY_F10;\n      case SDLK_F11:\treturn KEY_F11;\n      case SDLK_F12:\treturn KEY_F12;\n      case SDLK_PRINT:  return KEY_PRTSCR;\n\n      case SDLK_BACKSPACE: return KEY_BACKSPACE;\n      case SDLK_DELETE:\treturn KEY_DEL;\n\n      case SDLK_PAUSE:\treturn KEY_PAUSE;\n\n#if !SDL_VERSION_ATLEAST(1, 3, 0)\n      case SDLK_EQUALS: return KEY_EQUALS;\n#endif\n\n      case SDLK_MINUS:          return KEY_MINUS;\n\n      case SDLK_LSHIFT:\n      case SDLK_RSHIFT:\n\treturn KEY_RSHIFT;\n\t\n      case SDLK_LCTRL:\n      case SDLK_RCTRL:\n\treturn KEY_RCTRL;\n\t\n      case SDLK_LALT:\n      case SDLK_RALT:\n#if !SDL_VERSION_ATLEAST(1, 3, 0)\n      case SDLK_LMETA:\n      case SDLK_RMETA:\n#endif\n        return KEY_RALT;\n\n      case SDLK_CAPSLOCK: return KEY_CAPSLOCK;\n      case SDLK_SCROLLOCK: return KEY_SCRLCK;\n      case SDLK_NUMLOCK: return KEY_NUMLOCK;\n\n      case SDLK_KP0: return KEYP_0;\n      case SDLK_KP1: return KEYP_1;\n      case SDLK_KP2: return KEYP_2;\n      case SDLK_KP3: return KEYP_3;\n      case SDLK_KP4: return KEYP_4;\n      case SDLK_KP5: return KEYP_5;\n      case SDLK_KP6: return KEYP_6;\n      case SDLK_KP7: return KEYP_7;\n      case SDLK_KP8: return KEYP_8;\n      case SDLK_KP9: return KEYP_9;\n\n      case SDLK_KP_PERIOD:   return KEYP_PERIOD;\n      case SDLK_KP_MULTIPLY: return KEYP_MULTIPLY;\n      case SDLK_KP_PLUS:     return KEYP_PLUS;\n      case SDLK_KP_MINUS:    return KEYP_MINUS;\n      case SDLK_KP_DIVIDE:   return KEYP_DIVIDE;\n      case SDLK_KP_EQUALS:   return KEYP_EQUALS;\n      case SDLK_KP_ENTER:    return KEYP_ENTER;\n\n      case SDLK_HOME: return KEY_HOME;\n      case SDLK_INSERT: return KEY_INS;\n      case SDLK_END: return KEY_END;\n      case SDLK_PAGEUP: return KEY_PGUP;\n      case SDLK_PAGEDOWN: return KEY_PGDN;\n\n#ifdef SDL_HAVE_APP_KEYS\n        case SDLK_APP1:        return KEY_F1;\n        case SDLK_APP2:        return KEY_F2;\n        case SDLK_APP3:        return KEY_F3;\n        case SDLK_APP4:        return KEY_F4;\n        case SDLK_APP5:        return KEY_F5;\n        case SDLK_APP6:        return KEY_F6;\n#endif\n\n      default:\n        return tolower(sym->sym);\n    }\n}\n\n// Get the equivalent ASCII (Unicode?) character for a keypress.\n\nstatic int GetTypedChar(SDL_Event *event)\n{\n    int key;\n\n    // If Vanilla keyboard mapping enabled, the keyboard\n    // scan code is used to give the character typed.\n    // This does not change depending on keyboard layout.\n    // If you have a German keyboard, pressing 'z' will\n    // give 'y', for example.  It is desirable to be able\n    // to fix this so that people with non-standard \n    // keyboard mappings can type properly.  If vanilla\n    // mode is disabled, use the properly translated \n    // version.\n\n    if (vanilla_keyboard_mapping)\n    {\n        key = TranslateKey(&event->key.keysym);\n\n        // Is shift held down?  If so, perform a translation.\n\n        if (shiftdown > 0)\n        {\n            if (key >= 0 && key < arrlen(shiftxform))\n            {\n                key = shiftxform[key];\n            }\n            else\n            {\n                key = 0;\n            }\n        }\n\n        return key;\n    }\n    else\n    {\n        // Unicode value, from key layout.\n\n        return tolower(event->key.keysym.unicode);\n    }\n}\n\nstatic void UpdateShiftStatus(SDL_Event *event)\n{\n    int change;\n\n    if (event->type == SDL_KEYDOWN)\n    {\n        change = 1;\n    }\n    else if (event->type == SDL_KEYUP)\n    {\n        change = -1;\n    }\n    else\n    {\n        return;\n    }\n\n    if (event->key.keysym.sym == SDLK_LSHIFT \n     || event->key.keysym.sym == SDLK_RSHIFT)\n    {\n        shiftdown += change;\n    }\n}\n\n\nvoid I_GetEvent(void)\n{\n    SDL_Event sdlevent;\n    event_t event;\n\n    // possibly not needed\n    \n    SDL_PumpEvents();\n\n    // put event-grabbing stuff in here\n    \n    while (SDL_PollEvent(&sdlevent))\n    {\n        if (sdlevent.type == SDL_QUIT)\n        {\n            I_Quit();\n        }\n\n        UpdateShiftStatus(&sdlevent);\n\n        // process event\n        \n        switch (sdlevent.type)\n        {\n            case SDL_KEYDOWN:\n                // data1 has the key pressed, data2 has the character\n                // (shift-translated, etc)\n                event.type = ev_keydown;\n                event.data1 = TranslateKey(&sdlevent.key.keysym);\n                event.data2 = GetTypedChar(&sdlevent);\n\n                if (event.data1 != 0)\n                {\n                    D_PostEvent(&event);\n                }\n                break;\n\n            case SDL_KEYUP:\n                event.type = ev_keyup;\n                event.data1 = TranslateKey(&sdlevent.key.keysym);\n\n                // data2 is just initialized to zero for ev_keyup.\n                // For ev_keydown it's the shifted Unicode character\n                // that was typed, but if something wants to detect\n                // key releases it should do so based on data1\n                // (key ID), not the printable char.\n\n                event.data2 = 0;\n\n                if (event.data1 != 0)\n                {\n                    D_PostEvent(&event);\n                }\n                break;\n\n                /*\n            case SDL_MOUSEMOTION:\n                event.type = ev_mouse;\n                event.data1 = mouse_button_state;\n                event.data2 = AccelerateMouse(sdlevent.motion.xrel);\n                event.data3 = -AccelerateMouse(sdlevent.motion.yrel);\n                D_PostEvent(&event);\n                break;\n                */\n\n            case SDL_QUIT:\n                event.type = ev_quit;\n                D_PostEvent(&event);\n                break;\n\n            case SDL_ACTIVEEVENT:\n                // need to update our focus state\n                //UpdateFocus();\n                break;\n\n            default:\n                break;\n        }\n    }\n}\n\n\n//void I_ShutdownGraphics(void)\n//{\n//    //SDL_QuitSubSystem(SDL_INIT_VIDEO);\n//}\n\n//\n// I_StartTic\n//\nvoid I_StartTic (void)\n{\n    I_GetEvent();\n}\n\nvoid I_InitInput(void)\n{\n    SDL_Event dummy;\n    byte *doompal;\n    char *env;\n\n    // Pass through the XSCREENSAVER_WINDOW environment variable to \n    // SDL_WINDOWID, to embed the SDL window into the Xscreensaver\n    // window.\n\n    env = getenv(\"XSCREENSAVER_WINDOW\");\n\n    if (env != NULL)\n    {\n        char winenv[30];\n        int winid;\n\n        sscanf(env, \"0x%x\", &winid);\n        M_snprintf(winenv, sizeof(winenv), \"SDL_WINDOWID=%i\", winid);\n\n        putenv(winenv);\n    }\n\n    if (SDL_Init(SDL_INIT_VIDEO) < 0) \n    {\n        I_Error(\"Failed to initialize video: %s\", SDL_GetError());\n    }\n\n    //UpdateFocus();\n\n    // We need SDL to give us translated versions of keys as well\n\n    SDL_EnableUNICODE(1);\n\n    // Repeat key presses - this is what Vanilla Doom does\n    // Not sure about repeat rate - probably dependent on which DOS\n    // driver is used.  This is good enough though.\n\n    SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);\n\n    // clear out any events waiting at the start and center the mouse\n  \n    while (SDL_PollEvent(&dummy));\n}\n\n"
  },
  {
    "path": "fbdoom/i_input_tty.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tDOOM keyboard input via linux tty\n//\n\n\n#include <stdlib.h>\n#include <ctype.h>\n#include <math.h>\n#include <string.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <termios.h>\n#include <sys/ioctl.h>\n#include <linux/keyboard.h>\n#include <linux/kd.h>\n\n#include \"config.h\"\n#include \"deh_str.h\"\n#include \"doomtype.h\"\n#include \"doomkeys.h\"\n#include \"i_joystick.h\"\n#include \"i_system.h\"\n#include \"i_swap.h\"\n#include \"i_timer.h\"\n#include \"i_video.h\"\n#include \"i_scale.h\"\n#include \"m_argv.h\"\n#include \"m_config.h\"\n#include \"m_misc.h\"\n#include \"tables.h\"\n#include \"v_video.h\"\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\nint vanilla_keyboard_mapping = 1;\n\n// Is the shift key currently down?\n\nstatic int shiftdown = 0;\n\n// Lookup table for mapping AT keycodes to their doom keycode\nstatic const char at_to_doom[] =\n{\n    /* 0x00 */ 0x00,\n    /* 0x01 */ KEY_ESCAPE,\n    /* 0x02 */ '1',\n    /* 0x03 */ '2',\n    /* 0x04 */ '3',\n    /* 0x05 */ '4',\n    /* 0x06 */ '5',\n    /* 0x07 */ '6',\n    /* 0x08 */ '7',\n    /* 0x09 */ '8',\n    /* 0x0a */ '9',\n    /* 0x0b */ '0',\n    /* 0x0c */ '-',\n    /* 0x0d */ '=',\n    /* 0x0e */ KEY_BACKSPACE,\n    /* 0x0f */ KEY_TAB,\n    /* 0x10 */ 'q',\n    /* 0x11 */ 'w',\n    /* 0x12 */ 'e',\n    /* 0x13 */ 'r',\n    /* 0x14 */ 't',\n    /* 0x15 */ 'y',\n    /* 0x16 */ 'u',\n    /* 0x17 */ 'i',\n    /* 0x18 */ 'o',\n    /* 0x19 */ 'p',\n    /* 0x1a */ '[',\n    /* 0x1b */ ']',\n    /* 0x1c */ KEY_ENTER,\n    /* 0x1d */ KEY_FIRE, /* KEY_RCTRL, */\n    /* 0x1e */ 'a',\n    /* 0x1f */ 's',\n    /* 0x20 */ 'd',\n    /* 0x21 */ 'f',\n    /* 0x22 */ 'g',\n    /* 0x23 */ 'h',\n    /* 0x24 */ 'j',\n    /* 0x25 */ 'k',\n    /* 0x26 */ 'l',\n    /* 0x27 */ ';',\n    /* 0x28 */ '\\'',\n    /* 0x29 */ '`',\n    /* 0x2a */ KEY_RSHIFT,\n    /* 0x2b */ '\\\\',\n    /* 0x2c */ 'z',\n    /* 0x2d */ 'x',\n    /* 0x2e */ 'c',\n    /* 0x2f */ 'v',\n    /* 0x30 */ 'b',\n    /* 0x31 */ 'n',\n    /* 0x32 */ 'm',\n    /* 0x33 */ ',',\n    /* 0x34 */ '.',\n    /* 0x35 */ '/',\n    /* 0x36 */ KEY_RSHIFT,\n    /* 0x37 */ KEYP_MULTIPLY,\n    /* 0x38 */ KEY_LALT,\n    /* 0x39 */ KEY_USE,\n    /* 0x3a */ KEY_CAPSLOCK,\n    /* 0x3b */ KEY_F1,\n    /* 0x3c */ KEY_F2,\n    /* 0x3d */ KEY_F3,\n    /* 0x3e */ KEY_F4,\n    /* 0x3f */ KEY_F5,\n    /* 0x40 */ KEY_F6,\n    /* 0x41 */ KEY_F7,\n    /* 0x42 */ KEY_F8,\n    /* 0x43 */ KEY_F9,\n    /* 0x44 */ KEY_F10,\n    /* 0x45 */ KEY_NUMLOCK,\n    /* 0x46 */ 0x0,\n    /* 0x47 */ 0x0, /* 47 (Keypad-7/Home) */\n    /* 0x48 */ 0x0, /* 48 (Keypad-8/Up) */\n    /* 0x49 */ 0x0, /* 49 (Keypad-9/PgUp) */\n    /* 0x4a */ 0x0, /* 4a (Keypad--) */\n    /* 0x4b */ 0x0, /* 4b (Keypad-4/Left) */\n    /* 0x4c */ 0x0, /* 4c (Keypad-5) */\n    /* 0x4d */ 0x0, /* 4d (Keypad-6/Right) */\n    /* 0x4e */ 0x0, /* 4e (Keypad-+) */\n    /* 0x4f */ 0x0, /* 4f (Keypad-1/End) */\n    /* 0x50 */ 0x0, /* 50 (Keypad-2/Down) */\n    /* 0x51 */ 0x0, /* 51 (Keypad-3/PgDn) */\n    /* 0x52 */ 0x0, /* 52 (Keypad-0/Ins) */\n    /* 0x53 */ 0x0, /* 53 (Keypad-./Del) */\n    /* 0x54 */ 0x0, /* 54 (Alt-SysRq) on a 84+ key keyboard */\n    /* 0x55 */ 0x0,\n    /* 0x56 */ 0x0,\n    /* 0x57 */ 0x0,\n    /* 0x58 */ 0x0,\n    /* 0x59 */ 0x0,\n    /* 0x5a */ 0x0,\n    /* 0x5b */ 0x0,\n    /* 0x5c */ 0x0,\n    /* 0x5d */ 0x0,\n    /* 0x5e */ 0x0,\n    /* 0x5f */ 0x0,\n    /* 0x60 */ 0x0,\n    /* 0x61 */ 0x0,\n    /* 0x62 */ 0x0,\n    /* 0x63 */ 0x0,\n    /* 0x64 */ 0x0,\n    /* 0x65 */ 0x0,\n    /* 0x66 */ 0x0,\n    /* 0x67 */ KEY_UPARROW,\n    /* 0x68 */ 0x0,\n    /* 0x69 */ KEY_LEFTARROW,\n    /* 0x6a */ KEY_RIGHTARROW,\n    /* 0x6b */ 0x0,\n    /* 0x6c */ KEY_DOWNARROW,\n    /* 0x6d */ 0x0,\n    /* 0x6e */ 0x0,\n    /* 0x6f */ 0x0,\n    /* 0x70 */ 0x0,\n    /* 0x71 */ 0x0,\n    /* 0x72 */ 0x0,\n    /* 0x73 */ 0x0,\n    /* 0x74 */ 0x0,\n    /* 0x75 */ 0x0,\n    /* 0x76 */ 0x0,\n    /* 0x77 */ 0x0,\n    /* 0x78 */ 0x0,\n    /* 0x79 */ 0x0,\n    /* 0x7a */ 0x0,\n    /* 0x7b */ 0x0,\n    /* 0x7c */ 0x0,\n    /* 0x7d */ 0x0,\n    /* 0x7e */ 0x0,\n    /* 0x7f */ KEY_FIRE, //KEY_RCTRL,\n};\n\n// Lookup table for mapping ASCII characters to their equivalent when\n// shift is pressed on an American layout keyboard:\nstatic const char shiftxform[] =\n{\n    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,\n    11, 12, 13, 14, 15, 16, 17, 18, 19, 20,\n    21, 22, 23, 24, 25, 26, 27, 28, 29, 30,\n    31, ' ', '!', '\"', '#', '$', '%', '&',\n    '\"', // shift-'\n    '(', ')', '*', '+',\n    '<', // shift-,\n    '_', // shift--\n    '>', // shift-.\n    '?', // shift-/\n    ')', // shift-0\n    '!', // shift-1\n    '@', // shift-2\n    '#', // shift-3\n    '$', // shift-4\n    '%', // shift-5\n    '^', // shift-6\n    '&', // shift-7\n    '*', // shift-8\n    '(', // shift-9\n    ':',\n    ':', // shift-;\n    '<',\n    '+', // shift-=\n    '>', '?', '@',\n    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',\n    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',\n    '[', // shift-[\n    '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK\n    ']', // shift-]\n    '\"', '_',\n    '\\'', // shift-`\n    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',\n    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',\n    '{', '|', '}', '~', 127\n};\n\n/* Checks whether or not the given file descriptor is associated\n   with a local keyboard.\n   Returns 1 if it is, 0 if not (or if something prevented us from\n   checking). */\n\nint tty_is_kbd(int fd)\n{\n    int data = 0;\n\n    if (ioctl(fd, KDGKBTYPE, &data) != 0)\n        return 0;\n\n    if (data == KB_84) {\n        printf(\"84-key keyboard found.\\n\");\n        return 1;\n    } else if (data == KB_101) {\n        printf(\"101-key keyboard found.\\n\");\n        return 1;\n    } else {\n        printf(\"KDGKBTYPE = 0x%x.\\n\", data);\n        return 0;\n    }\n}\n\nstatic int old_mode = -1;\nstatic struct termios old_term;\nstatic int kb = -1; /* keyboard file descriptor */\n\nvoid kbd_shutdown(void)\n{\n    /* Shut down nicely. */\n\n    printf(\"Cleaning up.\\n\");\n    fflush(stdout);\n\n    printf(\"Exiting normally.\\n\");\n    if (old_mode != -1) {\n        ioctl(kb, KDSKBMODE, old_mode);\n        tcsetattr(kb, 0, &old_term);\n    }\n\n    if (kb > 3)\n        close(kb);\n\n    exit(0);\n}\n\nstatic int kbd_init(void)\n{\n    struct termios new_term;\n    char *files_to_try[] = {\"/dev/tty\", \"/dev/tty0\", \"/dev/console\", NULL};\n    int i;\n    int flags;\n    int found = 0;\n\n    /* First we need to find a file descriptor that represents the\n       system's keyboard. This should be /dev/tty, /dev/console,\n       stdin, stdout, or stderr. We'll try them in that order.\n       If none are acceptable, we're probably not being run\n       from a VT. */\n    for (i = 0; files_to_try[i] != NULL; i++) {\n        /* Try to open the file. */\n        kb = open(files_to_try[i], O_RDONLY);\n        if (kb < 0) continue;\n        /* See if this is valid for our purposes. */\n        if (tty_is_kbd(kb)) {\n            printf(\"Using keyboard on %s.\\n\", files_to_try[i]);\n            found = 1;\n            break;\n        }\n        close(kb);\n    }\n\n    /* If those didn't work, not all is lost. We can try the\n       3 standard file descriptors, in hopes that one of them\n       might point to a console. This is not especially likely. */\n    if (files_to_try[i] == NULL) {\n        for (kb = 0; kb < 3; kb++) {\n            if (tty_is_kbd(i)) {\n                found = 1;\n                break;\n            }\n        }\n    }\n\n    if (!found) {\n        printf(\"Unable to find a file descriptor associated with \"\\\n                \"the keyboard.\\n\" \\\n                \"Perhaps you're not using a virtual terminal?\\n\");\n        return 1;\n    }\n\n    /* Find the keyboard's mode so we can restore it later. */\n    if (ioctl(kb, KDGKBMODE, &old_mode) != 0) {\n        printf(\"Unable to query keyboard mode.\\n\");\n        kbd_shutdown();\n    }\n\n    /* Adjust the terminal's settings. In particular, disable\n       echoing, signal generation, and line buffering. Any of\n       these could cause trouble. Save the old settings first. */\n    if (tcgetattr(kb, &old_term) != 0) {\n        printf(\"Unable to query terminal settings.\\n\");\n        kbd_shutdown();\n    }\n\n    new_term = old_term;\n    new_term.c_iflag = 0;\n    new_term.c_lflag &= ~(ECHO | ICANON | ISIG);\n\n    /* TCSAFLUSH discards unread input before making the change.\n       A good idea. */\n    if (tcsetattr(kb, TCSAFLUSH, &new_term) != 0) {\n        printf(\"Unable to change terminal settings.\\n\");\n    }\n    \n    /* Put the keyboard in mediumraw mode. */\n    if (ioctl(kb, KDSKBMODE, K_MEDIUMRAW) != 0) {\n        printf(\"Unable to set mediumraw mode.\\n\");\n        kbd_shutdown();\n    }\n\n    /* Put in non-blocking mode */\n    flags = fcntl(kb, F_GETFL, 0);\n    fcntl(kb, F_SETFL, flags | O_NONBLOCK);\n\n    printf(\"Ready to read keycodes. Press Backspace to exit.\\n\");\n\n    return 0;\n}\n\nint kbd_read(int *pressed, unsigned char *key)\n{\n    unsigned char data;\n\n    if (read(kb, &data, 1) < 1) {\n        return 0;\n    }\n\n    *pressed = (data & 0x80) == 0x80;\n    *key = data & 0x7F;\n\n    /* Print the keycode. The top bit is the pressed/released\n       flag, and the lower seven are the keycode. */\n    //printf(\"%s: 0x%2X (%i)\\n\", *pressed ? \"Released\" : \" Pressed\", (unsigned int)*key, (unsigned int)*key);\n\n    return 1;\n}\n\nstatic unsigned char TranslateKey(unsigned char key)\n{\n    if (key < sizeof(at_to_doom))\n        return at_to_doom[key];\n    else\n        return 0x0;\n\n    //default:\n    //  return tolower(key);\n}\n\n// Get the equivalent ASCII (Unicode?) character for a keypress.\n\nstatic unsigned char GetTypedChar(unsigned char key)\n{\n    key = TranslateKey(key);\n\n    // Is shift held down?  If so, perform a translation.\n\n    if (shiftdown > 0)\n    {\n        if (key >= 0 && key < arrlen(shiftxform))\n        {\n            key = shiftxform[key];\n        }\n        else\n        {\n            key = 0;\n        }\n    }\n\n    return key;\n}\n\nstatic void UpdateShiftStatus(int pressed, unsigned char key)\n{\n    int change;\n\n    if (pressed) {\n        change = 1;\n    } else {\n        change = -1;\n    }\n\n    if (key == 0x2a || key == 0x36) {\n        shiftdown += change;\n    }\n}\n\n\nvoid I_GetEvent(void)\n{\n    event_t event;\n    int pressed;\n    unsigned char key;\n\n    // put event-grabbing stuff in here\n    \n    while (kbd_read(&pressed, &key))\n    {\n        if (key == 0x0E) {\n            kbd_shutdown();\n            I_Quit();\n        }\n\n        UpdateShiftStatus(pressed, key);\n\n        // process event\n        \n        if (!pressed)\n        {\n            // data1 has the key pressed, data2 has the character\n            // (shift-translated, etc)\n            event.type = ev_keydown;\n            event.data1 = TranslateKey(key);\n            event.data2 = GetTypedChar(key);\n\n            if (event.data1 != 0)\n            {\n                D_PostEvent(&event);\n            }\n        }\n        else\n        {\n            event.type = ev_keyup;\n            event.data1 = TranslateKey(key);\n\n            // data2 is just initialized to zero for ev_keyup.\n            // For ev_keydown it's the shifted Unicode character\n            // that was typed, but if something wants to detect\n            // key releases it should do so based on data1\n            // (key ID), not the printable char.\n\n            event.data2 = 0;\n\n            if (event.data1 != 0)\n            {\n                D_PostEvent(&event);\n            }\n            break;\n        }\n    }\n\n\n                /*\n            case SDL_MOUSEMOTION:\n                event.type = ev_mouse;\n                event.data1 = mouse_button_state;\n                event.data2 = AccelerateMouse(sdlevent.motion.xrel);\n                event.data3 = -AccelerateMouse(sdlevent.motion.yrel);\n                D_PostEvent(&event);\n                break;\n                */\n}\n\nvoid I_InitInput(void)\n{\n    kbd_init();\n\n    //UpdateFocus();\n}\n\n"
  },
  {
    "path": "fbdoom/i_input_tty.h",
    "content": "#ifndef __I_INPUT_TTY__\n#define __I_INPUT_TTY__\nvoid kbd_shutdown(void);\n#endif /* #ifndef __I_INPUT_TTY__ */"
  },
  {
    "path": "fbdoom/i_joystick.c",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//       SDL Joystick code.\n//\n\n#ifdef ORIGCODE\n#include \"SDL.h\"\n#include \"SDL_joystick.h\"\n#endif\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"doomtype.h\"\n#include \"d_event.h\"\n#include \"i_joystick.h\"\n#include \"i_system.h\"\n\n#include \"m_config.h\"\n#include \"m_misc.h\"\n\n// When an axis is within the dead zone, it is set to zero.\n// This is 5% of the full range:\n\n#define DEAD_ZONE (32768 / 3)\n\n#ifdef ORIGCODE\nstatic SDL_Joystick *joystick = NULL;\n#endif\n\n// Configuration variables:\n\n// Standard default.cfg Joystick enable/disable\n\nstatic int usejoystick = 0;\n\n// Joystick to use, as an SDL joystick index:\n\nstatic int joystick_index = -1;\n\n// Which joystick axis to use for horizontal movement, and whether to\n// invert the direction:\n\nstatic int joystick_x_axis = 0;\nstatic int joystick_x_invert = 0;\n\n// Which joystick axis to use for vertical movement, and whether to\n// invert the direction:\n\nstatic int joystick_y_axis = 1;\nstatic int joystick_y_invert = 0;\n\n// Which joystick axis to use for strafing?\n\nstatic int joystick_strafe_axis = -1;\nstatic int joystick_strafe_invert = 0;\n\n// Virtual to physical button joystick button mapping. By default this\n// is a straight mapping.\nstatic int joystick_physical_buttons[NUM_VIRTUAL_BUTTONS] = {\n    0, 1, 2, 3, 4, 5, 6, 7, 8, 9\n};\n\nvoid I_ShutdownJoystick(void)\n{\n#ifdef ORIGCODE\n    if (joystick != NULL)\n    {\n        SDL_JoystickClose(joystick);\n        joystick = NULL;\n        SDL_QuitSubSystem(SDL_INIT_JOYSTICK);\n    }\n#endif\n}\n\n#ifdef ORIGCODE\nstatic boolean IsValidAxis(int axis)\n{\n    int num_axes;\n\n    if (axis < 0)\n    {\n        return true;\n    }\n\n    if (IS_BUTTON_AXIS(axis))\n    {\n        return true;\n    }\n\n    if (IS_HAT_AXIS(axis))\n    {\n        return HAT_AXIS_HAT(axis) < SDL_JoystickNumHats(joystick);\n    }\n\n    num_axes = SDL_JoystickNumAxes(joystick);\n\n    return axis < num_axes;\n}\n#endif\n\nvoid I_InitJoystick(void)\n{\n#ifdef ORIGCODE\n    if (!usejoystick)\n    {\n        return;\n    }\n\n    if (SDL_Init(SDL_INIT_JOYSTICK) < 0)\n    {\n        return;\n    }\n\n    if (joystick_index < 0 || joystick_index >= SDL_NumJoysticks())\n    {\n        printf(\"I_InitJoystick: Invalid joystick ID: %i\\n\", joystick_index);\n        SDL_QuitSubSystem(SDL_INIT_JOYSTICK);\n        return;\n    }\n\n    // Open the joystick\n\n    joystick = SDL_JoystickOpen(joystick_index);\n\n    if (joystick == NULL)\n    {\n        printf(\"I_InitJoystick: Failed to open joystick #%i\\n\",\n               joystick_index);\n        SDL_QuitSubSystem(SDL_INIT_JOYSTICK);\n        return;\n    }\n\n    if (!IsValidAxis(joystick_x_axis)\n     || !IsValidAxis(joystick_y_axis)\n     || !IsValidAxis(joystick_strafe_axis))\n    {\n        printf(\"I_InitJoystick: Invalid joystick axis for joystick #%i \"\n               \"(run joystick setup again)\\n\",\n               joystick_index);\n\n        SDL_JoystickClose(joystick);\n        joystick = NULL;\n        SDL_QuitSubSystem(SDL_INIT_JOYSTICK);\n    }\n\n    SDL_JoystickEventState(SDL_ENABLE);\n\n    // Initialized okay!\n\n    printf(\"I_InitJoystick: %s\\n\", SDL_JoystickName(joystick_index));\n\n    I_AtExit(I_ShutdownJoystick, true);\n#endif\n}\n\n#ifdef ORIGCODE\nstatic boolean IsAxisButton(int physbutton)\n{\n    if (IS_BUTTON_AXIS(joystick_x_axis))\n    {\n        if (physbutton == BUTTON_AXIS_NEG(joystick_x_axis)\n         || physbutton == BUTTON_AXIS_POS(joystick_x_axis))\n        {\n            return true;\n        }\n    }\n    if (IS_BUTTON_AXIS(joystick_y_axis))\n    {\n        if (physbutton == BUTTON_AXIS_NEG(joystick_y_axis)\n         || physbutton == BUTTON_AXIS_POS(joystick_y_axis))\n        {\n            return true;\n        }\n    }\n    if (IS_BUTTON_AXIS(joystick_strafe_axis))\n    {\n        if (physbutton == BUTTON_AXIS_NEG(joystick_strafe_axis)\n         || physbutton == BUTTON_AXIS_POS(joystick_strafe_axis))\n        {\n            return true;\n        }\n    }\n\n    return false;\n}\n\n// Get the state of the given virtual button.\n\nstatic int ReadButtonState(int vbutton)\n{\n    int physbutton;\n\n    // Map from virtual button to physical (SDL) button.\n    if (vbutton < NUM_VIRTUAL_BUTTONS)\n    {\n        physbutton = joystick_physical_buttons[vbutton];\n    }\n    else\n    {\n        physbutton = vbutton;\n    }\n\n    // Never read axis buttons as buttons.\n    if (IsAxisButton(physbutton))\n    {\n        return 0;\n    }\n\n    return SDL_JoystickGetButton(joystick, physbutton);\n}\n\n// Get a bitmask of all currently-pressed buttons\n\nstatic int GetButtonsState(void)\n{\n    int i;\n    int result;\n\n    result = 0;\n\n    for (i = 0; i < 20; ++i)\n    {\n        if (ReadButtonState(i))\n        {\n            result |= 1 << i;\n        }\n    }\n\n    return result;\n}\n\n// Read the state of an axis, inverting if necessary.\n\nstatic int GetAxisState(int axis, int invert)\n{\n    int result;\n\n    // Axis -1 means disabled.\n\n    if (axis < 0)\n    {\n        return 0;\n    }\n\n    // Is this a button axis, or a hat axis?\n    // If so, we need to handle it specially.\n\n    result = 0;\n\n    if (IS_BUTTON_AXIS(axis))\n    {\n        if (SDL_JoystickGetButton(joystick, BUTTON_AXIS_NEG(axis)))\n        {\n            result -= 32767;\n        }\n        if (SDL_JoystickGetButton(joystick, BUTTON_AXIS_POS(axis)))\n        {\n            result += 32767;\n        }\n    }\n    else if (IS_HAT_AXIS(axis))\n    {\n        int direction = HAT_AXIS_DIRECTION(axis);\n        int hatval = SDL_JoystickGetHat(joystick, HAT_AXIS_HAT(axis));\n\n        if (direction == HAT_AXIS_HORIZONTAL)\n        {\n            if ((hatval & SDL_HAT_LEFT) != 0)\n            {\n                result -= 32767;\n            }\n            else if ((hatval & SDL_HAT_RIGHT) != 0)\n            {\n                result += 32767;\n            }\n        }\n        else if (direction == HAT_AXIS_VERTICAL)\n        {\n            if ((hatval & SDL_HAT_UP) != 0)\n            {\n                result -= 32767;\n            }\n            else if ((hatval & SDL_HAT_DOWN) != 0)\n            {\n                result += 32767;\n            }\n        }\n    }\n    else\n    {\n        result = SDL_JoystickGetAxis(joystick, axis);\n\n        if (result < DEAD_ZONE && result > -DEAD_ZONE)\n        {\n            result = 0;\n        }\n    }\n\n    if (invert)\n    {\n        result = -result;\n    }\n\n    return result;\n}\n#endif\nvoid I_UpdateJoystick(void)\n{\n#ifdef ORIGCODE\n    if (joystick != NULL)\n    {\n        event_t ev;\n\n        ev.type = ev_joystick;\n        ev.data1 = GetButtonsState();\n        ev.data2 = GetAxisState(joystick_x_axis, joystick_x_invert);\n        ev.data3 = GetAxisState(joystick_y_axis, joystick_y_invert);\n        ev.data4 = GetAxisState(joystick_strafe_axis, joystick_strafe_invert);\n\n        D_PostEvent(&ev);\n    }\n#endif\n}\n\nvoid I_BindJoystickVariables(void)\n{\n    int i;\n\n    M_BindVariable(\"use_joystick\",          &usejoystick);\n    M_BindVariable(\"joystick_index\",        &joystick_index);\n    M_BindVariable(\"joystick_x_axis\",       &joystick_x_axis);\n    M_BindVariable(\"joystick_y_axis\",       &joystick_y_axis);\n    M_BindVariable(\"joystick_strafe_axis\",  &joystick_strafe_axis);\n    M_BindVariable(\"joystick_x_invert\",     &joystick_x_invert);\n    M_BindVariable(\"joystick_y_invert\",     &joystick_y_invert);\n    M_BindVariable(\"joystick_strafe_invert\",&joystick_strafe_invert);\n\n    for (i = 0; i < NUM_VIRTUAL_BUTTONS; ++i)\n    {\n        char name[32];\n        M_snprintf(name, sizeof(name), \"joystick_physical_button%i\", i);\n        M_BindVariable(name, &joystick_physical_buttons[i]);\n    }\n}\n\n"
  },
  {
    "path": "fbdoom/i_joystick.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//      System-specific joystick interface.\n//\n\n\n#ifndef __I_JOYSTICK__\n#define __I_JOYSTICK__\n\n// Number of \"virtual\" joystick buttons defined in configuration files.\n// This needs to be at least as large as the number of different key\n// bindings supported by the higher-level game code (joyb* variables).\n#define NUM_VIRTUAL_BUTTONS 10\n\n// If this bit is set in a configuration file axis value, the axis is\n// not actually a joystick axis, but instead is a \"button axis\". This\n// means that instead of reading an SDL joystick axis, we read the\n// state of two buttons to get the axis value. This is needed for eg.\n// the PS3 SIXAXIS controller, where the D-pad buttons register as\n// buttons, not as two axes.\n#define BUTTON_AXIS 0x10000\n\n// Query whether a given axis value describes a button axis.\n#define IS_BUTTON_AXIS(axis) ((axis) >= 0 && ((axis) & BUTTON_AXIS) != 0)\n\n// Get the individual buttons from a button axis value.\n#define BUTTON_AXIS_NEG(axis)  ((axis) & 0xff)\n#define BUTTON_AXIS_POS(axis)  (((axis) >> 8) & 0xff)\n\n// Create a button axis value from two button values.\n#define CREATE_BUTTON_AXIS(neg, pos) (BUTTON_AXIS | (neg) | ((pos) << 8))\n\n// If this bit is set in an axis value, the axis is not actually a\n// joystick axis, but is a \"hat\" axis. This means that we read (one of)\n// the hats on the joystick.\n#define HAT_AXIS    0x20000\n\n#define IS_HAT_AXIS(axis) ((axis) >= 0 && ((axis) & HAT_AXIS) != 0)\n\n// Get the hat number from a hat axis value.\n#define HAT_AXIS_HAT(axis)         ((axis) & 0xff)\n// Which axis of the hat? (horizonal or vertical)\n#define HAT_AXIS_DIRECTION(axis)   (((axis) >> 8) & 0xff)\n\n#define CREATE_HAT_AXIS(hat, direction) \\\n    (HAT_AXIS | (hat) | ((direction) << 8))\n\n#define HAT_AXIS_HORIZONTAL 1\n#define HAT_AXIS_VERTICAL   2\n\nvoid I_InitJoystick(void);\nvoid I_ShutdownJoystick(void);\nvoid I_UpdateJoystick(void);\n\nvoid I_BindJoystickVariables(void);\n\n#endif /* #ifndef __I_JOYSTICK__ */\n\n"
  },
  {
    "path": "fbdoom/i_main.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tMain program, simply calls D_DoomMain high level loop.\n//\n\n#include \"config.h\"\n\n#include <stdio.h>\n\n#include \"doomtype.h\"\n#include \"i_system.h\"\n#include \"m_argv.h\"\n\n//\n// D_DoomMain()\n// Not a globally visible function, just included for source reference,\n// calls all startup code, parses command line options.\n//\n\nvoid D_DoomMain (void);\n\nint main(int argc, char **argv)\n{\n    // save arguments\n\n    myargc = argc;\n    myargv = argv;\n\n    M_FindResponseFile();\n\n    // start doom\n    printf(\"Starting D_DoomMain\\r\\n\");\n    D_DoomMain ();\n\n    return 0;\n}\n\n"
  },
  {
    "path": "fbdoom/i_oplmusic.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tSystem interface for music.\n//\n\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"memio.h\"\n#include \"mus2mid.h\"\n\n#include \"deh_main.h\"\n#include \"i_sound.h\"\n#include \"i_swap.h\"\n#include \"m_misc.h\"\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\n#include \"opl.h\"\n#include \"midifile.h\"\n\n// #define OPL_MIDI_DEBUG\n\n#define MAXMIDLENGTH (96 * 1024)\n#define GENMIDI_NUM_INSTRS  128\n#define GENMIDI_NUM_PERCUSSION 47\n\n#define GENMIDI_HEADER          \"#OPL_II#\"\n#define GENMIDI_FLAG_FIXED      0x0001         /* fixed pitch */\n#define GENMIDI_FLAG_2VOICE     0x0004         /* double voice (OPL3) */\n\n#define PERCUSSION_LOG_LEN 16\n\ntypedef struct\n{\n    byte tremolo;\n    byte attack;\n    byte sustain;\n    byte waveform;\n    byte scale;\n    byte level;\n} PACKEDATTR genmidi_op_t;\n\ntypedef struct\n{\n    genmidi_op_t modulator;\n    byte feedback;\n    genmidi_op_t carrier;\n    byte unused;\n    short base_note_offset;\n} PACKEDATTR genmidi_voice_t;\n\ntypedef struct\n{\n    unsigned short flags;\n    byte fine_tuning;\n    byte fixed_note;\n\n    genmidi_voice_t voices[2];\n} PACKEDATTR genmidi_instr_t;\n\n// Data associated with a channel of a track that is currently playing.\n\ntypedef struct\n{\n    // The instrument currently used for this track.\n\n    genmidi_instr_t *instrument;\n\n    // Volume level\n\n    int volume;\n\n    // Pitch bend value:\n\n    int bend;\n\n} opl_channel_data_t;\n\n// Data associated with a track that is currently playing.\n\ntypedef struct\n{\n    // Data for each channel.\n\n    opl_channel_data_t channels[MIDI_CHANNELS_PER_TRACK];\n\n    // Track iterator used to read new events.\n\n    midi_track_iter_t *iter;\n} opl_track_data_t;\n\ntypedef struct opl_voice_s opl_voice_t;\n\nstruct opl_voice_s\n{\n    // Index of this voice:\n    int index;\n\n    // The operators used by this voice:\n    int op1, op2;\n\n    // Currently-loaded instrument data\n    genmidi_instr_t *current_instr;\n\n    // The voice number in the instrument to use.\n    // This is normally set to zero; if this is a double voice\n    // instrument, it may be one.\n    unsigned int current_instr_voice;\n\n    // The channel currently using this voice.\n    opl_channel_data_t *channel;\n\n    // The midi key that this voice is playing.\n    unsigned int key;\n\n    // The note being played.  This is normally the same as\n    // the key, but if the instrument is a fixed pitch\n    // instrument, it is different.\n    unsigned int note;\n\n    // The frequency value being used.\n    unsigned int freq;\n\n    // The volume of the note being played on this channel.\n    unsigned int note_volume;\n\n    // The current volume (register value) that has been set for this channel.\n    unsigned int reg_volume;\n\n    // Next in linked list; a voice is always either in the\n    // free list or the allocated list.\n    opl_voice_t *next;\n};\n\n// Operators used by the different voices.\n\nstatic const int voice_operators[2][OPL_NUM_VOICES] = {\n    { 0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12 },\n    { 0x03, 0x04, 0x05, 0x0b, 0x0c, 0x0d, 0x13, 0x14, 0x15 }\n};\n\n// Frequency values to use for each note.\n\nstatic const unsigned short frequency_curve[] = {\n\n    0x133, 0x133, 0x134, 0x134, 0x135, 0x136, 0x136, 0x137,   // -1\n    0x137, 0x138, 0x138, 0x139, 0x139, 0x13a, 0x13b, 0x13b,\n    0x13c, 0x13c, 0x13d, 0x13d, 0x13e, 0x13f, 0x13f, 0x140,\n    0x140, 0x141, 0x142, 0x142, 0x143, 0x143, 0x144, 0x144,\n\n    0x145, 0x146, 0x146, 0x147, 0x147, 0x148, 0x149, 0x149,   // -2\n    0x14a, 0x14a, 0x14b, 0x14c, 0x14c, 0x14d, 0x14d, 0x14e,\n    0x14f, 0x14f, 0x150, 0x150, 0x151, 0x152, 0x152, 0x153,\n    0x153, 0x154, 0x155, 0x155, 0x156, 0x157, 0x157, 0x158,\n\n    // These are used for the first seven MIDI note values:\n\n    0x158, 0x159, 0x15a, 0x15a, 0x15b, 0x15b, 0x15c, 0x15d,   // 0\n    0x15d, 0x15e, 0x15f, 0x15f, 0x160, 0x161, 0x161, 0x162,\n    0x162, 0x163, 0x164, 0x164, 0x165, 0x166, 0x166, 0x167,\n    0x168, 0x168, 0x169, 0x16a, 0x16a, 0x16b, 0x16c, 0x16c,\n\n    0x16d, 0x16e, 0x16e, 0x16f, 0x170, 0x170, 0x171, 0x172,   // 1\n    0x172, 0x173, 0x174, 0x174, 0x175, 0x176, 0x176, 0x177,\n    0x178, 0x178, 0x179, 0x17a, 0x17a, 0x17b, 0x17c, 0x17c,\n    0x17d, 0x17e, 0x17e, 0x17f, 0x180, 0x181, 0x181, 0x182,\n\n    0x183, 0x183, 0x184, 0x185, 0x185, 0x186, 0x187, 0x188,   // 2\n    0x188, 0x189, 0x18a, 0x18a, 0x18b, 0x18c, 0x18d, 0x18d,\n    0x18e, 0x18f, 0x18f, 0x190, 0x191, 0x192, 0x192, 0x193,\n    0x194, 0x194, 0x195, 0x196, 0x197, 0x197, 0x198, 0x199,\n\n    0x19a, 0x19a, 0x19b, 0x19c, 0x19d, 0x19d, 0x19e, 0x19f,   // 3\n    0x1a0, 0x1a0, 0x1a1, 0x1a2, 0x1a3, 0x1a3, 0x1a4, 0x1a5,\n    0x1a6, 0x1a6, 0x1a7, 0x1a8, 0x1a9, 0x1a9, 0x1aa, 0x1ab,\n    0x1ac, 0x1ad, 0x1ad, 0x1ae, 0x1af, 0x1b0, 0x1b0, 0x1b1,\n\n    0x1b2, 0x1b3, 0x1b4, 0x1b4, 0x1b5, 0x1b6, 0x1b7, 0x1b8,   // 4\n    0x1b8, 0x1b9, 0x1ba, 0x1bb, 0x1bc, 0x1bc, 0x1bd, 0x1be,\n    0x1bf, 0x1c0, 0x1c0, 0x1c1, 0x1c2, 0x1c3, 0x1c4, 0x1c4,\n    0x1c5, 0x1c6, 0x1c7, 0x1c8, 0x1c9, 0x1c9, 0x1ca, 0x1cb,\n\n    0x1cc, 0x1cd, 0x1ce, 0x1ce, 0x1cf, 0x1d0, 0x1d1, 0x1d2,   // 5\n    0x1d3, 0x1d3, 0x1d4, 0x1d5, 0x1d6, 0x1d7, 0x1d8, 0x1d8,\n    0x1d9, 0x1da, 0x1db, 0x1dc, 0x1dd, 0x1de, 0x1de, 0x1df,\n    0x1e0, 0x1e1, 0x1e2, 0x1e3, 0x1e4, 0x1e5, 0x1e5, 0x1e6,\n\n    0x1e7, 0x1e8, 0x1e9, 0x1ea, 0x1eb, 0x1ec, 0x1ed, 0x1ed,   // 6\n    0x1ee, 0x1ef, 0x1f0, 0x1f1, 0x1f2, 0x1f3, 0x1f4, 0x1f5,\n    0x1f6, 0x1f6, 0x1f7, 0x1f8, 0x1f9, 0x1fa, 0x1fb, 0x1fc,\n    0x1fd, 0x1fe, 0x1ff, 0x200, 0x201, 0x201, 0x202, 0x203,\n\n    // First note of looped range used for all octaves:\n\n    0x204, 0x205, 0x206, 0x207, 0x208, 0x209, 0x20a, 0x20b,   // 7\n    0x20c, 0x20d, 0x20e, 0x20f, 0x210, 0x210, 0x211, 0x212,\n    0x213, 0x214, 0x215, 0x216, 0x217, 0x218, 0x219, 0x21a,\n    0x21b, 0x21c, 0x21d, 0x21e, 0x21f, 0x220, 0x221, 0x222,\n\n    0x223, 0x224, 0x225, 0x226, 0x227, 0x228, 0x229, 0x22a,   // 8\n    0x22b, 0x22c, 0x22d, 0x22e, 0x22f, 0x230, 0x231, 0x232,\n    0x233, 0x234, 0x235, 0x236, 0x237, 0x238, 0x239, 0x23a,\n    0x23b, 0x23c, 0x23d, 0x23e, 0x23f, 0x240, 0x241, 0x242,\n\n    0x244, 0x245, 0x246, 0x247, 0x248, 0x249, 0x24a, 0x24b,   // 9\n    0x24c, 0x24d, 0x24e, 0x24f, 0x250, 0x251, 0x252, 0x253,\n    0x254, 0x256, 0x257, 0x258, 0x259, 0x25a, 0x25b, 0x25c,\n    0x25d, 0x25e, 0x25f, 0x260, 0x262, 0x263, 0x264, 0x265,\n\n    0x266, 0x267, 0x268, 0x269, 0x26a, 0x26c, 0x26d, 0x26e,   // 10\n    0x26f, 0x270, 0x271, 0x272, 0x273, 0x275, 0x276, 0x277,\n    0x278, 0x279, 0x27a, 0x27b, 0x27d, 0x27e, 0x27f, 0x280,\n    0x281, 0x282, 0x284, 0x285, 0x286, 0x287, 0x288, 0x289,\n\n    0x28b, 0x28c, 0x28d, 0x28e, 0x28f, 0x290, 0x292, 0x293,   // 11\n    0x294, 0x295, 0x296, 0x298, 0x299, 0x29a, 0x29b, 0x29c,\n    0x29e, 0x29f, 0x2a0, 0x2a1, 0x2a2, 0x2a4, 0x2a5, 0x2a6,\n    0x2a7, 0x2a9, 0x2aa, 0x2ab, 0x2ac, 0x2ae, 0x2af, 0x2b0,\n\n    0x2b1, 0x2b2, 0x2b4, 0x2b5, 0x2b6, 0x2b7, 0x2b9, 0x2ba,   // 12\n    0x2bb, 0x2bd, 0x2be, 0x2bf, 0x2c0, 0x2c2, 0x2c3, 0x2c4,\n    0x2c5, 0x2c7, 0x2c8, 0x2c9, 0x2cb, 0x2cc, 0x2cd, 0x2ce,\n    0x2d0, 0x2d1, 0x2d2, 0x2d4, 0x2d5, 0x2d6, 0x2d8, 0x2d9,\n\n    0x2da, 0x2dc, 0x2dd, 0x2de, 0x2e0, 0x2e1, 0x2e2, 0x2e4,   // 13\n    0x2e5, 0x2e6, 0x2e8, 0x2e9, 0x2ea, 0x2ec, 0x2ed, 0x2ee,\n    0x2f0, 0x2f1, 0x2f2, 0x2f4, 0x2f5, 0x2f6, 0x2f8, 0x2f9,\n    0x2fb, 0x2fc, 0x2fd, 0x2ff, 0x300, 0x302, 0x303, 0x304,\n\n    0x306, 0x307, 0x309, 0x30a, 0x30b, 0x30d, 0x30e, 0x310,   // 14\n    0x311, 0x312, 0x314, 0x315, 0x317, 0x318, 0x31a, 0x31b,\n    0x31c, 0x31e, 0x31f, 0x321, 0x322, 0x324, 0x325, 0x327,\n    0x328, 0x329, 0x32b, 0x32c, 0x32e, 0x32f, 0x331, 0x332,\n\n    0x334, 0x335, 0x337, 0x338, 0x33a, 0x33b, 0x33d, 0x33e,   // 15\n    0x340, 0x341, 0x343, 0x344, 0x346, 0x347, 0x349, 0x34a,\n    0x34c, 0x34d, 0x34f, 0x350, 0x352, 0x353, 0x355, 0x357,\n    0x358, 0x35a, 0x35b, 0x35d, 0x35e, 0x360, 0x361, 0x363,\n\n    0x365, 0x366, 0x368, 0x369, 0x36b, 0x36c, 0x36e, 0x370,   // 16\n    0x371, 0x373, 0x374, 0x376, 0x378, 0x379, 0x37b, 0x37c,\n    0x37e, 0x380, 0x381, 0x383, 0x384, 0x386, 0x388, 0x389,\n    0x38b, 0x38d, 0x38e, 0x390, 0x392, 0x393, 0x395, 0x397,\n\n    0x398, 0x39a, 0x39c, 0x39d, 0x39f, 0x3a1, 0x3a2, 0x3a4,   // 17\n    0x3a6, 0x3a7, 0x3a9, 0x3ab, 0x3ac, 0x3ae, 0x3b0, 0x3b1,\n    0x3b3, 0x3b5, 0x3b7, 0x3b8, 0x3ba, 0x3bc, 0x3bd, 0x3bf,\n    0x3c1, 0x3c3, 0x3c4, 0x3c6, 0x3c8, 0x3ca, 0x3cb, 0x3cd,\n\n    // The last note has an incomplete range, and loops round back to\n    // the start.  Note that the last value is actually a buffer overrun\n    // and does not fit with the other values.\n\n    0x3cf, 0x3d1, 0x3d2, 0x3d4, 0x3d6, 0x3d8, 0x3da, 0x3db,   // 18\n    0x3dd, 0x3df, 0x3e1, 0x3e3, 0x3e4, 0x3e6, 0x3e8, 0x3ea,\n    0x3ec, 0x3ed, 0x3ef, 0x3f1, 0x3f3, 0x3f5, 0x3f6, 0x3f8,\n    0x3fa, 0x3fc, 0x3fe, 0x36c,\n};\n\n// Mapping from MIDI volume level to OPL level value.\n\nstatic const unsigned int volume_mapping_table[] = {\n    0, 1, 3, 5, 6, 8, 10, 11,\n    13, 14, 16, 17, 19, 20, 22, 23,\n    25, 26, 27, 29, 30, 32, 33, 34,\n    36, 37, 39, 41, 43, 45, 47, 49,\n    50, 52, 54, 55, 57, 59, 60, 61,\n    63, 64, 66, 67, 68, 69, 71, 72,\n    73, 74, 75, 76, 77, 79, 80, 81,\n    82, 83, 84, 84, 85, 86, 87, 88,\n    89, 90, 91, 92, 92, 93, 94, 95,\n    96, 96, 97, 98, 99, 99, 100, 101,\n    101, 102, 103, 103, 104, 105, 105, 106,\n    107, 107, 108, 109, 109, 110, 110, 111,\n    112, 112, 113, 113, 114, 114, 115, 115,\n    116, 117, 117, 118, 118, 119, 119, 120,\n    120, 121, 121, 122, 122, 123, 123, 123,\n    124, 124, 125, 125, 126, 126, 127, 127\n};\n\nstatic boolean music_initialized = false;\n\n//static boolean musicpaused = false;\nstatic int current_music_volume;\n\n// GENMIDI lump instrument data:\n\nstatic genmidi_instr_t *main_instrs;\nstatic genmidi_instr_t *percussion_instrs;\nstatic char (*main_instr_names)[32];\nstatic char (*percussion_names)[32];\n\n// Voices:\n\nstatic opl_voice_t voices[OPL_NUM_VOICES];\nstatic opl_voice_t *voice_free_list;\nstatic opl_voice_t *voice_alloced_list;\n\n// Track data for playing tracks:\n\nstatic opl_track_data_t *tracks;\nstatic unsigned int num_tracks = 0;\nstatic unsigned int running_tracks = 0;\nstatic boolean song_looping;\n\n// Tempo control variables\n\nstatic unsigned int ticks_per_beat;\nstatic unsigned int us_per_beat;\n\n// Mini-log of recently played percussion instruments:\n\nstatic uint8_t last_perc[PERCUSSION_LOG_LEN];\nstatic unsigned int last_perc_count;\n\n// Configuration file variable, containing the port number for the\n// adlib chip.\n\nint opl_io_port = 0x388;\n\n// Load instrument table from GENMIDI lump:\n\nstatic boolean LoadInstrumentTable(void)\n{\n    byte *lump;\n\n    lump = W_CacheLumpName(\"GENMIDI\", PU_STATIC);\n\n    // Check header\n\n    if (strncmp((char *) lump, GENMIDI_HEADER, strlen(GENMIDI_HEADER)) != 0)\n    {\n        W_ReleaseLumpName(\"GENMIDI\");\n\n        return false;\n    }\n\n    main_instrs = (genmidi_instr_t *) (lump + strlen(GENMIDI_HEADER));\n    percussion_instrs = main_instrs + GENMIDI_NUM_INSTRS;\n    main_instr_names = (char (*)[32]) (percussion_instrs + GENMIDI_NUM_PERCUSSION);\n    percussion_names = main_instr_names + GENMIDI_NUM_INSTRS;\n\n    return true;\n}\n\n// Get the next available voice from the freelist.\n\nstatic opl_voice_t *GetFreeVoice(void)\n{\n    opl_voice_t *result;\n\n    // None available?\n\n    if (voice_free_list == NULL)\n    {\n        return NULL;\n    }\n\n    // Remove from free list\n\n    result = voice_free_list;\n    voice_free_list = voice_free_list->next;\n\n    // Add to allocated list\n\n    result->next = voice_alloced_list;\n    voice_alloced_list = result;\n\n    return result;\n}\n\n// Remove a voice from the allocated voices list.\n\nstatic void RemoveVoiceFromAllocedList(opl_voice_t *voice)\n{\n    opl_voice_t **rover;\n\n    rover = &voice_alloced_list;\n\n    // Search the list until we find the voice, then remove it.\n\n    while (*rover != NULL)\n    {\n        if (*rover == voice)\n        {\n            *rover = voice->next;\n            voice->next = NULL;\n            break;\n        }\n\n        rover = &(*rover)->next;\n    }\n}\n\n// Release a voice back to the freelist.\n\nstatic void ReleaseVoice(opl_voice_t *voice)\n{\n    opl_voice_t **rover;\n\n    voice->channel = NULL;\n    voice->note = 0;\n\n    // Remove from alloced list.\n\n    RemoveVoiceFromAllocedList(voice);\n\n    // Search to the end of the freelist (This is how Doom behaves!)\n\n    rover = &voice_free_list;\n\n    while (*rover != NULL)\n    {\n        rover = &(*rover)->next;\n    }\n\n    *rover = voice;\n    voice->next = NULL;\n}\n\n// Load data to the specified operator\n\nstatic void LoadOperatorData(int operator, genmidi_op_t *data,\n                             boolean max_level)\n{\n    int level;\n\n    // The scale and level fields must be combined for the level register.\n    // For the carrier wave we always set the maximum level.\n\n    level = (data->scale & 0xc0) | (data->level & 0x3f);\n\n    if (max_level)\n    {\n        level |= 0x3f;\n    }\n\n    OPL_WriteRegister(OPL_REGS_LEVEL + operator, level);\n    OPL_WriteRegister(OPL_REGS_TREMOLO + operator, data->tremolo);\n    OPL_WriteRegister(OPL_REGS_ATTACK + operator, data->attack);\n    OPL_WriteRegister(OPL_REGS_SUSTAIN + operator, data->sustain);\n    OPL_WriteRegister(OPL_REGS_WAVEFORM + operator, data->waveform);\n}\n\n// Set the instrument for a particular voice.\n\nstatic void SetVoiceInstrument(opl_voice_t *voice,\n                               genmidi_instr_t *instr,\n                               unsigned int instr_voice)\n{\n    genmidi_voice_t *data;\n    unsigned int modulating;\n\n    // Instrument already set for this channel?\n\n    if (voice->current_instr == instr\n     && voice->current_instr_voice == instr_voice)\n    {\n        return;\n    }\n\n    voice->current_instr = instr;\n    voice->current_instr_voice = instr_voice;\n\n    data = &instr->voices[instr_voice];\n\n    // Are we usind modulated feedback mode?\n\n    modulating = (data->feedback & 0x01) == 0;\n\n    // Doom loads the second operator first, then the first.\n    // The carrier is set to minimum volume until the voice volume\n    // is set in SetVoiceVolume (below).  If we are not using\n    // modulating mode, we must set both to minimum volume.\n\n    LoadOperatorData(voice->op2, &data->carrier, true);\n    LoadOperatorData(voice->op1, &data->modulator, !modulating);\n\n    // Set feedback register that control the connection between the\n    // two operators.  Turn on bits in the upper nybble; I think this\n    // is for OPL3, where it turns on channel A/B.\n\n    OPL_WriteRegister(OPL_REGS_FEEDBACK + voice->index,\n                      data->feedback | 0x30);\n\n    // Hack to force a volume update.\n\n    voice->reg_volume = 999;\n}\n\nstatic void SetVoiceVolume(opl_voice_t *voice, unsigned int volume)\n{\n    genmidi_voice_t *opl_voice;\n    unsigned int full_volume;\n    unsigned int op_volume;\n    unsigned int reg_volume;\n\n    voice->note_volume = volume;\n\n    opl_voice = &voice->current_instr->voices[voice->current_instr_voice];\n\n    // Multiply note volume and channel volume to get the actual volume.\n\n    full_volume = (volume_mapping_table[voice->note_volume]\n                   * volume_mapping_table[voice->channel->volume]\n                   * volume_mapping_table[current_music_volume]) / (127 * 127);\n\n    // The volume of each instrument can be controlled via GENMIDI:\n\n    op_volume = 0x3f - opl_voice->carrier.level;\n\n    // The volume value to use in the register:\n\n    reg_volume = (op_volume * full_volume) / 128;\n    reg_volume = (0x3f - reg_volume) | opl_voice->carrier.scale;\n\n    // Update the volume register(s) if necessary.\n\n    if (reg_volume != voice->reg_volume)\n    {\n        voice->reg_volume = reg_volume;\n\n        OPL_WriteRegister(OPL_REGS_LEVEL + voice->op2, reg_volume);\n\n        // If we are using non-modulated feedback mode, we must set the\n        // volume for both voices.\n        // Note that the same register volume value is written for\n        // both voices, always calculated from the carrier's level\n        // value.\n\n        if ((opl_voice->feedback & 0x01) != 0)\n        {\n            OPL_WriteRegister(OPL_REGS_LEVEL + voice->op1, reg_volume);\n        }\n    }\n}\n\n// Initialize the voice table and freelist\n\nstatic void InitVoices(void)\n{\n    int i;\n\n    // Start with an empty free list.\n\n    voice_free_list = NULL;\n\n    // Initialize each voice.\n\n    for (i=0; i<OPL_NUM_VOICES; ++i)\n    {\n        voices[i].index = i;\n        voices[i].op1 = voice_operators[0][i];\n        voices[i].op2 = voice_operators[1][i];\n        voices[i].current_instr = NULL;\n\n        // Add this voice to the freelist.\n\n        ReleaseVoice(&voices[i]);\n    }\n}\n\n// Set music volume (0 - 127)\n\nstatic void I_OPL_SetMusicVolume(int volume)\n{\n    unsigned int i;\n\n    // Internal state variable.\n\n    current_music_volume = volume;\n\n    // Update the volume of all voices.\n\n    for (i=0; i<OPL_NUM_VOICES; ++i)\n    {\n        if (voices[i].channel != NULL)\n        {\n            SetVoiceVolume(&voices[i], voices[i].note_volume);\n        }\n    }\n}\n\nstatic void VoiceKeyOff(opl_voice_t *voice)\n{\n    OPL_WriteRegister(OPL_REGS_FREQ_2 + voice->index, voice->freq >> 8);\n}\n\n// Get the frequency that we should be using for a voice.\n\nstatic void KeyOffEvent(opl_track_data_t *track, midi_event_t *event)\n{\n    opl_channel_data_t *channel;\n    unsigned int key;\n    unsigned int i;\n\n/*\n    printf(\"note off: channel %i, %i, %i\\n\",\n           event->data.channel.channel,\n           event->data.channel.param1,\n           event->data.channel.param2);\n*/\n\n    channel = &track->channels[event->data.channel.channel];\n    key = event->data.channel.param1;\n\n    // Turn off voices being used to play this key.\n    // If it is a double voice instrument there will be two.\n\n    for (i=0; i<OPL_NUM_VOICES; ++i)\n    {\n        if (voices[i].channel == channel && voices[i].key == key)\n        {\n            VoiceKeyOff(&voices[i]);\n\n            // Finished with this voice now.\n\n            ReleaseVoice(&voices[i]);\n        }\n    }\n}\n\n// Compare the priorities of channels, returning either -1, 0 or 1.\n\nstatic int CompareChannelPriorities(opl_channel_data_t *chan1,\n                                    opl_channel_data_t *chan2)\n{\n    // TODO ...\n\n    return 1;\n}\n\n// When all voices are in use, we must discard an existing voice to\n// play a new note.  Find and free an existing voice.  The channel\n// passed to the function is the channel for the new note to be\n// played.\n\nstatic opl_voice_t *ReplaceExistingVoice(opl_channel_data_t *channel)\n{\n    opl_voice_t *rover;\n    opl_voice_t *result;\n\n    // Check the allocated voices, if we find an instrument that is\n    // of a lower priority to the new instrument, discard it.\n    // If a voice is being used to play the second voice of an instrument,\n    // use that, as second voices are non-essential.\n    // Lower numbered MIDI channels implicitly have a higher priority\n    // than higher-numbered channels, eg. MIDI channel 1 is never\n    // discarded for MIDI channel 2.\n\n    result = NULL;\n\n    for (rover = voice_alloced_list; rover != NULL; rover = rover->next)\n    {\n        if (rover->current_instr_voice != 0\n         || (rover->channel > channel\n             && CompareChannelPriorities(channel, rover->channel) > 0))\n        {\n            result = rover;\n            break;\n        }\n    }\n\n    // If we didn't find a voice, find an existing voice being used to\n    // play a note on the same channel, and use that.\n\n    if (result == NULL)\n    {\n        for (rover = voice_alloced_list; rover != NULL; rover = rover->next)\n        {\n            if (rover->channel == channel)\n            {\n                result = rover;\n                break;\n            }\n        }\n    }\n\n    // Still nothing found?  Give up and just use the first voice in\n    // the list.\n\n    if (result == NULL)\n    {\n        result = voice_alloced_list;\n    }\n\n    // Stop playing this voice playing and release it back to the free\n    // list.\n\n    VoiceKeyOff(result);\n    ReleaseVoice(result);\n\n    // Re-allocate the voice again and return it.\n\n    return GetFreeVoice();\n}\n\n\nstatic unsigned int FrequencyForVoice(opl_voice_t *voice)\n{\n    genmidi_voice_t *gm_voice;\n    unsigned int freq_index;\n    unsigned int octave;\n    unsigned int sub_index;\n    unsigned int note;\n\n    note = voice->note;\n\n    // Apply note offset.\n    // Don't apply offset if the instrument is a fixed note instrument.\n\n    gm_voice = &voice->current_instr->voices[voice->current_instr_voice];\n\n    if ((SHORT(voice->current_instr->flags) & GENMIDI_FLAG_FIXED) == 0)\n    {\n        note += (signed short) SHORT(gm_voice->base_note_offset);\n    }\n\n    // Avoid possible overflow due to base note offset:\n\n    if (note > 0x7f)\n    {\n        note = voice->note;\n    }\n\n    freq_index = 64 + 32 * note + voice->channel->bend;\n\n    // If this is the second voice of a double voice instrument, the\n    // frequency index can be adjusted by the fine tuning field.\n\n    if (voice->current_instr_voice != 0)\n    {\n        freq_index += (voice->current_instr->fine_tuning / 2) - 64;\n    }\n\n    // The first 7 notes use the start of the table, while\n    // consecutive notes loop around the latter part.\n\n    if (freq_index < 284)\n    {\n        return frequency_curve[freq_index];\n    }\n\n    sub_index = (freq_index - 284) % (12 * 32);\n    octave = (freq_index - 284) / (12 * 32);\n\n    // Once the seventh octave is reached, things break down.\n    // We can only go up to octave 7 as a maximum anyway (the OPL\n    // register only has three bits for octave number), but for the\n    // notes in octave 7, the first five bits have octave=7, the\n    // following notes have octave=6.  This 7/6 pattern repeats in\n    // following octaves (which are technically impossible to\n    // represent anyway).\n\n    if (octave >= 7)\n    {\n        if (sub_index < 5)\n        {\n            octave = 7;\n        }\n        else\n        {\n            octave = 6;\n        }\n    }\n\n    // Calculate the resulting register value to use for the frequency.\n\n    return frequency_curve[sub_index + 284] | (octave << 10);\n}\n\n// Update the frequency that a voice is programmed to use.\n\nstatic void UpdateVoiceFrequency(opl_voice_t *voice)\n{\n    unsigned int freq;\n\n    // Calculate the frequency to use for this voice and update it\n    // if neccessary.\n\n    freq = FrequencyForVoice(voice);\n\n    if (voice->freq != freq)\n    {\n        OPL_WriteRegister(OPL_REGS_FREQ_1 + voice->index, freq & 0xff);\n        OPL_WriteRegister(OPL_REGS_FREQ_2 + voice->index, (freq >> 8) | 0x20);\n\n        voice->freq = freq;\n    }\n}\n\n// Program a single voice for an instrument.  For a double voice \n// instrument (GENMIDI_FLAG_2VOICE), this is called twice for each\n// key on event.\n\nstatic void VoiceKeyOn(opl_channel_data_t *channel,\n                       genmidi_instr_t *instrument,\n                       unsigned int instrument_voice,\n                       unsigned int key,\n                       unsigned int volume)\n{\n    opl_voice_t *voice;\n\n    // Find a voice to use for this new note.\n\n    voice = GetFreeVoice();\n\n    // If there are no more voices left, we must decide what to do.\n    // If this is the first voice of the instrument, free an existing\n    // voice and use that.  Otherwise, if this is the second voice,\n    // it isn't as important; just discard it.\n\n    if (voice == NULL)\n    {\n        if (instrument_voice == 0)\n        {\n            voice = ReplaceExistingVoice(channel);\n        }\n        else\n        {\n            return;\n        }\n    }\n\n    voice->channel = channel;\n    voice->key = key;\n\n    // Work out the note to use.  This is normally the same as\n    // the key, unless it is a fixed pitch instrument.\n\n    if ((SHORT(instrument->flags) & GENMIDI_FLAG_FIXED) != 0)\n    {\n        voice->note = instrument->fixed_note;\n    }\n    else\n    {\n        voice->note = key;\n    }\n\n    // Program the voice with the instrument data:\n\n    SetVoiceInstrument(voice, instrument, instrument_voice);\n\n    // Set the volume level.\n\n    SetVoiceVolume(voice, volume);\n\n    // Write the frequency value to turn the note on.\n\n    voice->freq = 0;\n    UpdateVoiceFrequency(voice);\n}\n\nstatic void KeyOnEvent(opl_track_data_t *track, midi_event_t *event)\n{\n    genmidi_instr_t *instrument;\n    opl_channel_data_t *channel;\n    unsigned int key;\n    unsigned int volume;\n\n/*\n    printf(\"note on: channel %i, %i, %i\\n\",\n           event->data.channel.channel,\n           event->data.channel.param1,\n           event->data.channel.param2);\n*/\n\n    key = event->data.channel.param1;\n    volume = event->data.channel.param2;\n\n    // A volume of zero means key off. Some MIDI tracks, eg. the ones\n    // in AV.wad, use a second key on with a volume of zero to mean\n    // key off.\n    if (volume <= 0)\n    {\n        KeyOffEvent(track, event);\n        return;\n    }\n\n    // The channel.\n    channel = &track->channels[event->data.channel.channel];\n\n    // Percussion channel (10) is treated differently.\n\n    if (event->data.channel.channel == 9)\n    {\n        if (key < 35 || key > 81)\n        {\n            return;\n        }\n\n        instrument = &percussion_instrs[key - 35];\n\n        last_perc[last_perc_count] = key;\n        last_perc_count = (last_perc_count + 1) % PERCUSSION_LOG_LEN;\n    }\n    else\n    {\n        instrument = channel->instrument;\n    }\n\n    // Find and program a voice for this instrument.  If this\n    // is a double voice instrument, we must do this twice.\n\n    VoiceKeyOn(channel, instrument, 0, key, volume);\n\n    if ((SHORT(instrument->flags) & GENMIDI_FLAG_2VOICE) != 0)\n    {\n        VoiceKeyOn(channel, instrument, 1, key, volume);\n    }\n}\n\nstatic void ProgramChangeEvent(opl_track_data_t *track, midi_event_t *event)\n{\n    int channel;\n    int instrument;\n\n    // Set the instrument used on this channel.\n\n    channel = event->data.channel.channel;\n    instrument = event->data.channel.param1;\n    track->channels[channel].instrument = &main_instrs[instrument];\n\n    // TODO: Look through existing voices that are turned on on this\n    // channel, and change the instrument.\n}\n\nstatic void SetChannelVolume(opl_channel_data_t *channel, unsigned int volume)\n{\n    unsigned int i;\n\n    channel->volume = volume;\n\n    // Update all voices that this channel is using.\n\n    for (i=0; i<OPL_NUM_VOICES; ++i)\n    {\n        if (voices[i].channel == channel)\n        {\n            SetVoiceVolume(&voices[i], voices[i].note_volume);\n        }\n    }\n}\n\n// Handler for the MIDI_CONTROLLER_ALL_NOTES_OFF channel event.\nstatic void AllNotesOff(opl_channel_data_t *channel, unsigned int param)\n{\n    unsigned int i;\n\n    for (i = 0; i < OPL_NUM_VOICES; ++i)\n    {\n        if (voices[i].channel == channel)\n        {\n            VoiceKeyOff(&voices[i]);\n            ReleaseVoice(&voices[i]);\n        }\n    }\n}\n\nstatic void ControllerEvent(opl_track_data_t *track, midi_event_t *event)\n{\n    unsigned int controller;\n    unsigned int param;\n    opl_channel_data_t *channel;\n\n/*\n    printf(\"change controller: channel %i, %i, %i\\n\",\n           event->data.channel.channel,\n           event->data.channel.param1,\n           event->data.channel.param2);\n*/\n\n    channel = &track->channels[event->data.channel.channel];\n    controller = event->data.channel.param1;\n    param = event->data.channel.param2;\n\n    switch (controller)\n    {\n        case MIDI_CONTROLLER_MAIN_VOLUME:\n            SetChannelVolume(channel, param);\n            break;\n\n        case MIDI_CONTROLLER_ALL_NOTES_OFF:\n            AllNotesOff(channel, param);\n            break;\n\n        default:\n#ifdef OPL_MIDI_DEBUG\n            fprintf(stderr, \"Unknown MIDI controller type: %i\\n\", controller);\n#endif\n            break;\n    }\n}\n\n// Process a pitch bend event.\n\nstatic void PitchBendEvent(opl_track_data_t *track, midi_event_t *event)\n{\n    opl_channel_data_t *channel;\n    unsigned int i;\n\n    // Update the channel bend value.  Only the MSB of the pitch bend\n    // value is considered: this is what Doom does.\n\n    channel = &track->channels[event->data.channel.channel];\n    channel->bend = event->data.channel.param2 - 64;\n\n    // Update all voices for this channel.\n\n    for (i=0; i<OPL_NUM_VOICES; ++i)\n    {\n        if (voices[i].channel == channel)\n        {\n            UpdateVoiceFrequency(&voices[i]);\n        }\n    }\n}\n\nstatic void MetaSetTempo(unsigned int tempo)\n{\n    OPL_AdjustCallbacks((float) us_per_beat / tempo);\n    us_per_beat = tempo;\n}\n\n// Process a meta event.\n\nstatic void MetaEvent(opl_track_data_t *track, midi_event_t *event)\n{\n    byte *data = event->data.meta.data;\n    unsigned int data_len = event->data.meta.length;\n\n    switch (event->data.meta.type)\n    {\n        // Things we can just ignore.\n\n        case MIDI_META_SEQUENCE_NUMBER:\n        case MIDI_META_TEXT:\n        case MIDI_META_COPYRIGHT:\n        case MIDI_META_TRACK_NAME:\n        case MIDI_META_INSTR_NAME:\n        case MIDI_META_LYRICS:\n        case MIDI_META_MARKER:\n        case MIDI_META_CUE_POINT:\n        case MIDI_META_SEQUENCER_SPECIFIC:\n            break;\n\n        case MIDI_META_SET_TEMPO:\n            if (data_len == 3)\n            {\n                MetaSetTempo((data[0] << 16) | (data[1] << 8) | data[2]);\n            }\n            break;\n\n        // End of track - actually handled when we run out of events\n        // in the track, see below.\n\n        case MIDI_META_END_OF_TRACK:\n            break;\n\n        default:\n#ifdef OPL_MIDI_DEBUG\n            fprintf(stderr, \"Unknown MIDI meta event type: %i\\n\",\n                            event->data.meta.type);\n#endif\n            break;\n    }\n}\n\n// Process a MIDI event from a track.\n\nstatic void ProcessEvent(opl_track_data_t *track, midi_event_t *event)\n{\n    switch (event->event_type)\n    {\n        case MIDI_EVENT_NOTE_OFF:\n            KeyOffEvent(track, event);\n            break;\n\n        case MIDI_EVENT_NOTE_ON:\n            KeyOnEvent(track, event);\n            break;\n\n        case MIDI_EVENT_CONTROLLER:\n            ControllerEvent(track, event);\n            break;\n\n        case MIDI_EVENT_PROGRAM_CHANGE:\n            ProgramChangeEvent(track, event);\n            break;\n\n        case MIDI_EVENT_PITCH_BEND:\n            PitchBendEvent(track, event);\n            break;\n\n        case MIDI_EVENT_META:\n            MetaEvent(track, event);\n            break;\n\n        // SysEx events can be ignored.\n\n        case MIDI_EVENT_SYSEX:\n        case MIDI_EVENT_SYSEX_SPLIT:\n            break;\n\n        default:\n#ifdef OPL_MIDI_DEBUG\n            fprintf(stderr, \"Unknown MIDI event type %i\\n\", event->event_type);\n#endif\n            break;\n    }\n}\n\nstatic void ScheduleTrack(opl_track_data_t *track);\n\n// Restart a song from the beginning.\n\nstatic void RestartSong(void *unused)\n{\n    unsigned int i;\n\n    running_tracks = num_tracks;\n\n    for (i=0; i<num_tracks; ++i)\n    {\n        MIDI_RestartIterator(tracks[i].iter);\n        ScheduleTrack(&tracks[i]);\n    }\n}\n\n// Callback function invoked when another event needs to be read from\n// a track.\n\nstatic void TrackTimerCallback(void *arg)\n{\n    opl_track_data_t *track = arg;\n    midi_event_t *event;\n\n    // Get the next event and process it.\n\n    if (!MIDI_GetNextEvent(track->iter, &event))\n    {\n        return;\n    }\n\n    ProcessEvent(track, event);\n\n    // End of track?\n\n    if (event->event_type == MIDI_EVENT_META\n     && event->data.meta.type == MIDI_META_END_OF_TRACK)\n    {\n        --running_tracks;\n\n        // When all tracks have finished, restart the song.\n        // Don't restart the song immediately, but wait for 5ms\n        // before triggering a restart.  Otherwise it is possible\n        // to construct an empty MIDI file that causes the game\n        // to lock up in an infinite loop. (5ms should be short\n        // enough not to be noticeable by the listener).\n\n        if (running_tracks <= 0 && song_looping)\n        {\n            OPL_SetCallback(5000, RestartSong, NULL);\n        }\n\n        return;\n    }\n\n    // Reschedule the callback for the next event in the track.\n\n    ScheduleTrack(track);\n}\n\nstatic void ScheduleTrack(opl_track_data_t *track)\n{\n    unsigned int nticks;\n    uint64_t us;\n\n    // Get the number of microseconds until the next event.\n\n    nticks = MIDI_GetDeltaTime(track->iter);\n    us = ((uint64_t) nticks * us_per_beat) / ticks_per_beat;\n\n    // Set a timer to be invoked when the next event is\n    // ready to play.\n\n    OPL_SetCallback(us, TrackTimerCallback, track);\n}\n\n// Initialize a channel.\n\nstatic void InitChannel(opl_track_data_t *track, opl_channel_data_t *channel)\n{\n    // TODO: Work out sensible defaults?\n\n    channel->instrument = &main_instrs[0];\n    channel->volume = 127;\n    channel->bend = 0;\n}\n\n// Start a MIDI track playing:\n\nstatic void StartTrack(midi_file_t *file, unsigned int track_num)\n{\n    opl_track_data_t *track;\n    unsigned int i;\n\n    track = &tracks[track_num];\n    track->iter = MIDI_IterateTrack(file, track_num);\n\n    for (i=0; i<MIDI_CHANNELS_PER_TRACK; ++i)\n    {\n        InitChannel(track, &track->channels[i]);\n    }\n\n    // Schedule the first event.\n\n    ScheduleTrack(track);\n}\n\n// Start playing a mid\n\nstatic void I_OPL_PlaySong(void *handle, boolean looping)\n{\n    midi_file_t *file;\n    unsigned int i;\n\n    if (!music_initialized || handle == NULL)\n    {\n        return;\n    }\n\n    file = handle;\n\n    // Allocate track data.\n\n    tracks = malloc(MIDI_NumTracks(file) * sizeof(opl_track_data_t));\n\n    num_tracks = MIDI_NumTracks(file);\n    running_tracks = num_tracks;\n    song_looping = looping;\n\n    ticks_per_beat = MIDI_GetFileTimeDivision(file);\n\n    // Default is 120 bpm.\n    // TODO: this is wrong\n\n    us_per_beat = 500 * 1000;\n\n    for (i=0; i<num_tracks; ++i)\n    {\n        StartTrack(file, i);\n    }\n}\n\nstatic void I_OPL_PauseSong(void)\n{\n    unsigned int i;\n\n    if (!music_initialized)\n    {\n        return;\n    }\n\n    // Pause OPL callbacks.\n\n    OPL_SetPaused(1);\n\n    // Turn off all main instrument voices (not percussion).\n    // This is what Vanilla does.\n\n    for (i=0; i<OPL_NUM_VOICES; ++i)\n    {\n        if (voices[i].channel != NULL\n         && voices[i].current_instr < percussion_instrs)\n        {\n            VoiceKeyOff(&voices[i]);\n        }\n    }\n}\n\nstatic void I_OPL_ResumeSong(void)\n{\n    if (!music_initialized)\n    {\n        return;\n    }\n\n    OPL_SetPaused(0);\n}\n\nstatic void I_OPL_StopSong(void)\n{\n    unsigned int i;\n\n    if (!music_initialized)\n    {\n        return;\n    }\n\n    OPL_Lock();\n\n    // Stop all playback.\n\n    OPL_ClearCallbacks();\n\n    // Free all voices.\n\n    for (i=0; i<OPL_NUM_VOICES; ++i)\n    {\n        if (voices[i].channel != NULL)\n        {\n            VoiceKeyOff(&voices[i]);\n            ReleaseVoice(&voices[i]);\n        }\n    }\n\n    // Free all track data.\n\n    for (i=0; i<num_tracks; ++i)\n    {\n        MIDI_FreeIterator(tracks[i].iter);\n    }\n\n    free(tracks);\n\n    tracks = NULL;\n    num_tracks = 0;\n\n    OPL_Unlock();\n}\n\nstatic void I_OPL_UnRegisterSong(void *handle)\n{\n    if (!music_initialized)\n    {\n        return;\n    }\n\n    if (handle != NULL)\n    {\n        MIDI_FreeFile(handle);\n    }\n}\n\n// Determine whether memory block is a .mid file \n\nstatic boolean IsMid(byte *mem, int len)\n{\n    return len > 4 && !memcmp(mem, \"MThd\", 4);\n}\n\nstatic boolean ConvertMus(byte *musdata, int len, char *filename)\n{\n    MEMFILE *instream;\n    MEMFILE *outstream;\n    void *outbuf;\n    size_t outbuf_len;\n    int result;\n\n    instream = mem_fopen_read(musdata, len);\n    outstream = mem_fopen_write();\n\n    result = mus2mid(instream, outstream);\n\n    if (result == 0)\n    {\n        mem_get_buf(outstream, &outbuf, &outbuf_len);\n\n        M_WriteFile(filename, outbuf, outbuf_len);\n    }\n\n    mem_fclose(instream);\n    mem_fclose(outstream);\n\n    return result;\n}\n\nstatic void *I_OPL_RegisterSong(void *data, int len)\n{\n    midi_file_t *result;\n    char *filename;\n\n    if (!music_initialized)\n    {\n        return NULL;\n    }\n\n    // MUS files begin with \"MUS\"\n    // Reject anything which doesnt have this signature\n\n    filename = M_TempFile(\"doom.mid\");\n\n    if (IsMid(data, len) && len < MAXMIDLENGTH)\n    {\n        M_WriteFile(filename, data, len);\n    }\n    else\n    {\n\t// Assume a MUS file and try to convert\n\n        ConvertMus(data, len, filename);\n    }\n\n    result = MIDI_LoadFile(filename);\n\n    if (result == NULL)\n    {\n        fprintf(stderr, \"I_OPL_RegisterSong: Failed to load MID.\\n\");\n    }\n\n    // remove file now\n\n    remove(filename);\n    free(filename);\n\n    return result;\n}\n\n// Is the song playing?\n\nstatic boolean I_OPL_MusicIsPlaying(void)\n{\n    if (!music_initialized)\n    {\n        return false;\n    }\n\n    return num_tracks > 0;\n}\n\n// Shutdown music\n\nstatic void I_OPL_ShutdownMusic(void)\n{\n    if (music_initialized)\n    {\n        // Stop currently-playing track, if there is one:\n\n        I_OPL_StopSong();\n\n        OPL_Shutdown();\n\n        // Release GENMIDI lump\n\n        W_ReleaseLumpName(\"GENMIDI\");\n\n        music_initialized = false;\n    }\n}\n\n// Initialize music subsystem\n\nstatic boolean I_OPL_InitMusic(void)\n{\n    OPL_SetSampleRate(snd_samplerate);\n\n    if (!OPL_Init(opl_io_port))\n    {\n        printf(\"Dude.  The Adlib isn't responding.\\n\");\n        return false;\n    }\n\n    // Load instruments from GENMIDI lump:\n\n    if (!LoadInstrumentTable())\n    {\n        OPL_Shutdown();\n        return false;\n    }\n\n    InitVoices();\n\n    tracks = NULL;\n    num_tracks = 0;\n    music_initialized = true;\n\n    return true;\n}\n\nstatic snddevice_t music_opl_devices[] =\n{\n    SNDDEVICE_ADLIB,\n    SNDDEVICE_SB,\n};\n\nmusic_module_t music_opl_module =\n{\n    music_opl_devices,\n    arrlen(music_opl_devices),\n    I_OPL_InitMusic,\n    I_OPL_ShutdownMusic,\n    I_OPL_SetMusicVolume,\n    I_OPL_PauseSong,\n    I_OPL_ResumeSong,\n    I_OPL_RegisterSong,\n    I_OPL_UnRegisterSong,\n    I_OPL_PlaySong,\n    I_OPL_StopSong,\n    I_OPL_MusicIsPlaying,\n    NULL,  // Poll\n};\n\n//----------------------------------------------------------------------\n//\n// Development / debug message generation, to help developing GENMIDI\n// lumps.\n//\n//----------------------------------------------------------------------\n\nstatic int NumActiveChannels(void)\n{\n    int i;\n\n    for (i = MIDI_CHANNELS_PER_TRACK - 1; i >= 0; --i)\n    {\n        if (tracks[0].channels[i].instrument != &main_instrs[0])\n        {\n            return i + 1;\n        }\n    }\n\n    return 0;\n}\n\nstatic int ChannelInUse(opl_channel_data_t *channel)\n{\n    opl_voice_t *voice;\n\n    for (voice = voice_alloced_list; voice != NULL; voice = voice->next)\n    {\n        if (voice->channel == channel)\n        {\n            return 1;\n        }\n    }\n\n    return 0;\n}\n\nvoid I_OPL_DevMessages(char *result, size_t result_len)\n{\n    char tmp[80];\n    int instr_num;\n    int lines;\n    int i;\n\n    if (num_tracks == 0)\n    {\n        M_snprintf(result, result_len, \"No OPL track!\");\n        return;\n    }\n\n    M_snprintf(result, result_len, \"Tracks:\\n\");\n    lines = 1;\n\n    for (i = 0; i < NumActiveChannels(); ++i)\n    {\n        if (tracks[0].channels[i].instrument == NULL)\n        {\n            continue;\n        }\n\n        instr_num = tracks[0].channels[i].instrument - main_instrs;\n\n        M_snprintf(tmp, sizeof(tmp),\n                   \"chan %i: %c i#%i (%s)\\n\",\n                   i,\n                   ChannelInUse(&tracks[0].channels[i]) ? '\\'' : ' ',\n                   instr_num + 1,\n                   main_instr_names[instr_num]);\n        M_StringConcat(result, tmp, result_len);\n\n        ++lines;\n    }\n\n    M_snprintf(tmp, sizeof(tmp), \"\\nLast percussion:\\n\");\n    M_StringConcat(result, tmp, result_len);\n    lines += 2;\n\n    i = (last_perc_count + PERCUSSION_LOG_LEN - 1) % PERCUSSION_LOG_LEN;\n\n    do {\n        if (last_perc[i] == 0)\n        {\n            break;\n        }\n\n        M_snprintf(tmp, sizeof(tmp),\n                   \"%cp#%i (%s)\\n\",\n                   i == 0 ? '\\'' : ' ',\n                   last_perc[i],\n                   percussion_names[last_perc[i] - 35]);\n        M_StringConcat(result, tmp, result_len);\n        ++lines;\n\n        i = (i + PERCUSSION_LOG_LEN - 1) % PERCUSSION_LOG_LEN;\n    } while (lines < 25 && i != last_perc_count);\n}\n\n"
  },
  {
    "path": "fbdoom/i_pcsound.c",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tSystem interface for PC speaker sound.\n//\n\n#include \"SDL.h\"\n#include <string.h>\n\n#include \"doomtype.h\"\n\n#include \"deh_str.h\"\n#include \"i_sound.h\"\n#include \"m_misc.h\"\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\n#include \"pcsound.h\"\n\n#define TIMER_FREQ 1193181 /* hz */\n\nstatic boolean pcs_initialized = false;\n\nstatic SDL_mutex *sound_lock;\nstatic boolean use_sfx_prefix;\n\nstatic uint8_t *current_sound_lump = NULL;\nstatic uint8_t *current_sound_pos = NULL;\nstatic unsigned int current_sound_remaining = 0;\nstatic int current_sound_handle = 0;\nstatic int current_sound_lump_num = -1;\n\nstatic const uint16_t divisors[] = {\n    0,\n    6818, 6628, 6449, 6279, 6087, 5906, 5736, 5575,\n    5423, 5279, 5120, 4971, 4830, 4697, 4554, 4435,\n    4307, 4186, 4058, 3950, 3836, 3728, 3615, 3519,\n    3418, 3323, 3224, 3131, 3043, 2960, 2875, 2794,\n    2711, 2633, 2560, 2485, 2415, 2348, 2281, 2213,\n    2153, 2089, 2032, 1975, 1918, 1864, 1810, 1757,\n    1709, 1659, 1612, 1565, 1521, 1478, 1435, 1395,\n    1355, 1316, 1280, 1242, 1207, 1173, 1140, 1107,\n    1075, 1045, 1015,  986,  959,  931,  905,  879,\n     854,  829,  806,  783,  760,  739,  718,  697,\n     677,  658,  640,  621,  604,  586,  570,  553,\n     538,  522,  507,  493,  479,  465,  452,  439,\n     427,  415,  403,  391,  380,  369,  359,  348,\n     339,  329,  319,  310,  302,  293,  285,  276,\n     269,  261,  253,  246,  239,  232,  226,  219,\n     213,  207,  201,  195,  190,  184,  179,\n};\n\nstatic void PCSCallbackFunc(int *duration, int *freq)\n{\n    unsigned int tone;\n\n    *duration = 1000 / 140;\n\n    if (SDL_LockMutex(sound_lock) < 0)\n    {\n        *freq = 0;\n        return;\n    }\n\n    if (current_sound_lump != NULL && current_sound_remaining > 0)\n    {\n        // Read the next tone\n\n        tone = *current_sound_pos;\n\n        // Use the tone -> frequency lookup table.  See pcspkr10.zip\n        // for a full discussion of this.\n        // Check we don't overflow the frequency table.\n\n        if (tone < arrlen(divisors) && divisors[tone] != 0)\n        {\n            *freq = (int) (TIMER_FREQ / divisors[tone]);\n        }\n        else\n        {\n            *freq = 0;\n        }\n\n        ++current_sound_pos;\n        --current_sound_remaining;\n    }\n    else\n    {\n        *freq = 0;\n    }\n\n    SDL_UnlockMutex(sound_lock);\n}\n\nstatic boolean CachePCSLump(sfxinfo_t *sfxinfo)\n{\n    int lumplen;\n    int headerlen;\n\n    // Free the current sound lump back to the cache\n \n    if (current_sound_lump != NULL)\n    {\n        W_ReleaseLumpNum(current_sound_lump_num);\n        current_sound_lump = NULL;\n    }\n\n    // Load from WAD\n\n    current_sound_lump = W_CacheLumpNum(sfxinfo->lumpnum, PU_STATIC);\n    lumplen = W_LumpLength(sfxinfo->lumpnum);\n\n    // Read header\n  \n    if (current_sound_lump[0] != 0x00 || current_sound_lump[1] != 0x00)\n    {\n        return false;\n    }\n\n    headerlen = (current_sound_lump[3] << 8) | current_sound_lump[2];\n\n    if (headerlen > lumplen - 4)\n    {\n        return false;\n    }\n\n    // Header checks out ok\n\n    current_sound_remaining = headerlen;\n    current_sound_pos = current_sound_lump + 4;\n    current_sound_lump_num = sfxinfo->lumpnum;\n\n    return true;\n}\n\n// These Doom PC speaker sounds are not played - this can be seen in the \n// Heretic source code, where there are remnants of this left over\n// from Doom.\n\nstatic boolean IsDisabledSound(sfxinfo_t *sfxinfo)\n{\n    int i;\n    const char *disabled_sounds[] = {\n        \"posact\",\n        \"bgact\",\n        \"dmact\",\n        \"dmpain\",\n        \"popain\",\n        \"sawidl\",\n    };\n\n    for (i=0; i<arrlen(disabled_sounds); ++i)\n    {\n        if (!strcmp(sfxinfo->name, disabled_sounds[i]))\n        {\n            return true;\n        }\n    }\n\n    return false;\n}\n\nstatic int I_PCS_StartSound(sfxinfo_t *sfxinfo,\n                            int channel,\n                            int vol,\n                            int sep)\n{\n    int result;\n\n    if (!pcs_initialized)\n    {\n        return -1;\n    }\n\n    if (IsDisabledSound(sfxinfo))\n    {\n        return -1;\n    }\n\n    if (SDL_LockMutex(sound_lock) < 0)\n    {\n        return -1;\n    }\n\n    result = CachePCSLump(sfxinfo);\n\n    if (result)\n    {\n        current_sound_handle = channel;\n    }\n\n    SDL_UnlockMutex(sound_lock);\n\n    if (result)\n    {\n        return channel;\n    }\n    else\n    {\n        return -1;\n    }\n}\n\nstatic void I_PCS_StopSound(int handle)\n{\n    if (!pcs_initialized)\n    {\n        return;\n    }\n\n    if (SDL_LockMutex(sound_lock) < 0)\n    {\n        return;\n    }\n\n    // If this is the channel currently playing, immediately end it.\n\n    if (current_sound_handle == handle)\n    {\n        current_sound_remaining = 0;\n    }\n    \n    SDL_UnlockMutex(sound_lock);\n}\n\n//\n// Retrieve the raw data lump index\n//  for a given SFX name.\n//\n\nstatic int I_PCS_GetSfxLumpNum(sfxinfo_t* sfx)\n{\n    char namebuf[9];\n\n    if (use_sfx_prefix)\n    {\n        M_snprintf(namebuf, sizeof(namebuf), \"dp%s\", DEH_String(sfx->name));\n    }\n    else\n    {\n        M_StringCopy(namebuf, DEH_String(sfx->name), sizeof(namebuf));\n    }\n\n    return W_GetNumForName(namebuf);\n}\n\n\nstatic boolean I_PCS_SoundIsPlaying(int handle)\n{\n    if (!pcs_initialized)\n    {\n        return false;\n    }\n\n    if (handle != current_sound_handle)\n    {\n        return false;\n    }\n\n    return current_sound_lump != NULL && current_sound_remaining > 0;\n}\n\nstatic boolean I_PCS_InitSound(boolean _use_sfx_prefix)\n{\n    use_sfx_prefix = _use_sfx_prefix;\n\n    // Use the sample rate from the configuration file\n\n    PCSound_SetSampleRate(snd_samplerate);\n\n    // Initialize the PC speaker subsystem.\n\n    pcs_initialized = PCSound_Init(PCSCallbackFunc);\n\n    if (pcs_initialized)\n    {\n        sound_lock = SDL_CreateMutex();\n    }\n\n    return pcs_initialized;\n}\n\nstatic void I_PCS_ShutdownSound(void)\n{\n    if (pcs_initialized)\n    {\n        PCSound_Shutdown();\n    }\n}\n\nstatic void I_PCS_UpdateSound(void)\n{\n    // no-op.\n}\n\nvoid I_PCS_UpdateSoundParams(int channel, int vol, int sep)\n{\n    // no-op.\n}\n\nstatic snddevice_t sound_pcsound_devices[] = \n{\n    SNDDEVICE_PCSPEAKER,\n};\n\nsound_module_t sound_pcsound_module = \n{\n    sound_pcsound_devices,\n    arrlen(sound_pcsound_devices),\n    I_PCS_InitSound,\n    I_PCS_ShutdownSound,\n    I_PCS_GetSfxLumpNum,\n    I_PCS_UpdateSound,\n    I_PCS_UpdateSoundParams,\n    I_PCS_StartSound,\n    I_PCS_StopSound,\n    I_PCS_SoundIsPlaying,\n};\n\n"
  },
  {
    "path": "fbdoom/i_scale.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     Screen scale-up code: \n//         1x,2x,3x,4x pixel doubling\n//         Aspect ratio-correcting stretch functions\n//\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"doomtype.h\"\n\n#include \"i_video.h\"\n#include \"m_argv.h\"\n#include \"z_zone.h\"\n\n#if defined(_MSC_VER) && !defined(__cplusplus)\n#define inline __inline\n#endif\n\n// Should be I_VideoBuffer\n\nstatic byte *src_buffer;\n\n// Destination buffer, ie. screen->pixels.\n\nstatic byte *dest_buffer;\n\n// Pitch of destination buffer, ie. screen->pitch.\n\nstatic int dest_pitch;\n\n// Lookup tables used for aspect ratio correction stretching code.\n// stretch_tables[0] : 20% / 80%\n// stretch_tables[1] : 40% / 60%\n// All other combinations can be reached from these two tables.\n\nstatic byte *stretch_tables[2] = { NULL, NULL };\n\n// 50%/50% stretch table, for 800x600 squash mode\n\nstatic byte *half_stretch_table = NULL;\n\n// Called to set the source and destination buffers before doing the\n// scale.\n\nvoid I_InitScale(byte *_src_buffer, byte *_dest_buffer, int _dest_pitch)\n{\n    src_buffer = _src_buffer;\n    dest_buffer = _dest_buffer;\n    dest_pitch = _dest_pitch;\n}\n\n//\n// Pixel doubling scale-up functions.\n//\n\n// 1x scale doesn't really do any scaling: it just copies the buffer\n// a line at a time for when pitch != SCREENWIDTH (!native_surface)\n\nstatic boolean I_Scale1x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp;\n    int y;\n    int w = x2 - x1;\n    \n    // Need to byte-copy from buffer into the screen buffer\n\n    bufp = src_buffer + y1 * SCREENWIDTH + x1;\n    screenp = (byte *) dest_buffer + y1 * dest_pitch + x1;\n\n    for (y=y1; y<y2; ++y)\n    {\n        memcpy(screenp, bufp, w);\n        screenp += dest_pitch;\n        bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_scale_1x = {\n    SCREENWIDTH, SCREENHEIGHT,\n    NULL,\n    I_Scale1x,\n    false,\n};\n\n// 2x scale (640x400)\n\nstatic boolean I_Scale2x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp, *screenp2;\n    int x, y;\n    int multi_pitch;\n\n    multi_pitch = dest_pitch * 2;\n    bufp = src_buffer + y1 * SCREENWIDTH + x1;\n    screenp = (byte *) dest_buffer + (y1 * dest_pitch + x1) * 2;\n    screenp2 = screenp + dest_pitch;\n\n    for (y=y1; y<y2; ++y)\n    {\n        byte *sp, *sp2, *bp;\n        sp = screenp;\n        sp2 = screenp2;\n        bp = bufp;\n\n        for (x=x1; x<x2; ++x)\n        {\n            *sp++ = *bp;  *sp++ = *bp;\n            *sp2++ = *bp; *sp2++ = *bp;\n            ++bp;\n        }\n        screenp += multi_pitch;\n        screenp2 += multi_pitch;\n        bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_scale_2x = {\n    SCREENWIDTH * 2, SCREENHEIGHT * 2,\n    NULL,\n    I_Scale2x,\n    false,\n};\n\n// 3x scale (960x600)\n\nstatic boolean I_Scale3x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp, *screenp2, *screenp3;\n    int x, y;\n    int multi_pitch;\n\n    multi_pitch = dest_pitch * 3;\n    bufp = src_buffer + y1 * SCREENWIDTH + x1;\n    screenp = (byte *) dest_buffer + (y1 * dest_pitch + x1) * 3;\n    screenp2 = screenp + dest_pitch;\n    screenp3 = screenp + dest_pitch * 2;\n\n    for (y=y1; y<y2; ++y)\n    {\n        byte *sp, *sp2, *sp3, *bp;\n        sp = screenp;\n        sp2 = screenp2;\n        sp3 = screenp3;\n        bp = bufp;\n\n        for (x=x1; x<x2; ++x)\n        {\n            *sp++ = *bp;  *sp++ = *bp;  *sp++ = *bp;\n            *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp;\n            *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp;\n            ++bp;\n        }\n        screenp += multi_pitch;\n        screenp2 += multi_pitch;\n        screenp3 += multi_pitch;\n        bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_scale_3x = {\n    SCREENWIDTH * 3, SCREENHEIGHT * 3,\n    NULL,\n    I_Scale3x,\n    false,\n};\n\n// 4x scale (1280x800)\n\nstatic boolean I_Scale4x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp, *screenp2, *screenp3, *screenp4;\n    int x, y;\n    int multi_pitch;\n\n    multi_pitch = dest_pitch * 4;\n    bufp = src_buffer + y1 * SCREENWIDTH + x1;\n    screenp = (byte *) dest_buffer + (y1 * dest_pitch + x1) * 4;\n    screenp2 = screenp + dest_pitch;\n    screenp3 = screenp + dest_pitch * 2;\n    screenp4 = screenp + dest_pitch * 3;\n\n    for (y=y1; y<y2; ++y)\n    {\n        byte *sp, *sp2, *sp3, *sp4, *bp;\n        sp = screenp;\n        sp2 = screenp2;\n        sp3 = screenp3;\n        sp4 = screenp4;\n        bp = bufp;\n\n        for (x=x1; x<x2; ++x)\n        {\n            *sp++ = *bp;  *sp++ = *bp;  *sp++ = *bp;  *sp++ = *bp;\n            *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp;\n            *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp;\n            *sp4++ = *bp; *sp4++ = *bp; *sp4++ = *bp; *sp4++ = *bp;\n            ++bp;\n        }\n        screenp += multi_pitch;\n        screenp2 += multi_pitch;\n        screenp3 += multi_pitch;\n        screenp4 += multi_pitch;\n        bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_scale_4x = {\n    SCREENWIDTH * 4, SCREENHEIGHT * 4,\n    NULL,\n    I_Scale4x,\n    false,\n};\n\n// 5x scale (1600x1000)\n\nstatic boolean I_Scale5x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp, *screenp2, *screenp3, *screenp4, *screenp5;\n    int x, y;\n    int multi_pitch;\n\n    multi_pitch = dest_pitch * 5;\n    bufp = src_buffer + y1 * SCREENWIDTH + x1;\n    screenp = (byte *) dest_buffer + (y1 * dest_pitch + x1) * 5;\n    screenp2 = screenp + dest_pitch;\n    screenp3 = screenp + dest_pitch * 2;\n    screenp4 = screenp + dest_pitch * 3;\n    screenp5 = screenp + dest_pitch * 4;\n\n    for (y=y1; y<y2; ++y)\n    {\n        byte *sp, *sp2, *sp3, *sp4, *sp5, *bp;\n        sp = screenp;\n        sp2 = screenp2;\n        sp3 = screenp3;\n        sp4 = screenp4;\n        sp5 = screenp5;\n        bp = bufp;\n\n        for (x=x1; x<x2; ++x)\n        {\n            *sp++ = *bp;  *sp++ = *bp;  *sp++ = *bp;  *sp++ = *bp;  *sp++ = *bp;\n            *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp;\n            *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp;\n            *sp4++ = *bp; *sp4++ = *bp; *sp4++ = *bp; *sp4++ = *bp; *sp4++ = *bp;\n            *sp5++ = *bp; *sp5++ = *bp; *sp5++ = *bp; *sp5++ = *bp; *sp5++ = *bp;\n            ++bp;\n        }\n        screenp += multi_pitch;\n        screenp2 += multi_pitch;\n        screenp3 += multi_pitch;\n        screenp4 += multi_pitch;\n        screenp5 += multi_pitch;\n        bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_scale_5x = {\n    SCREENWIDTH * 5, SCREENHEIGHT * 5,\n    NULL,\n    I_Scale5x,\n    false,\n};\n\n\n// Search through the given palette, finding the nearest color that matches\n// the given color.\n\nstatic int FindNearestColor(byte *palette, int r, int g, int b)\n{\n    byte *col;\n    int best;\n    int best_diff;\n    int diff;\n    int i;\n\n    best = 0;\n    best_diff = INT_MAX;\n\n    for (i=0; i<256; ++i)\n    {\n        col = palette + i * 3;\n        diff = (r - col[0]) * (r - col[0])\n             + (g - col[1]) * (g - col[1])\n             + (b - col[2]) * (b - col[2]);\n\n        if (diff == 0)\n        {\n            return i;\n        }\n        else if (diff < best_diff)\n        {\n            best = i;\n            best_diff = diff;\n        }\n    }\n\n    return best;\n}\n\n// Create a stretch table.  This is a lookup table for blending colors.\n// pct specifies the bias between the two colors: 0 = all y, 100 = all x.\n// NB: This is identical to the lookup tables used in other ports for\n// translucency.\n\nstatic byte *GenerateStretchTable(byte *palette, int pct)\n{\n    byte *result;\n    int x, y;\n    int r, g, b;\n    byte *col1;\n    byte *col2;\n\n    result = Z_Malloc(256 * 256, PU_STATIC, NULL);\n\n    for (x=0; x<256; ++x)\n    {\n        for (y=0; y<256; ++y)\n        {\n            col1 = palette + x * 3;\n            col2 = palette + y * 3;\n            r = (((int) col1[0]) * pct + ((int) col2[0]) * (100 - pct)) / 100;\n            g = (((int) col1[1]) * pct + ((int) col2[1]) * (100 - pct)) / 100;\n            b = (((int) col1[2]) * pct + ((int) col2[2]) * (100 - pct)) / 100;\n            result[x * 256 + y] = FindNearestColor(palette, r, g, b);\n        }\n    }\n\n    return result;\n}\n\n// Called at startup to generate the lookup tables for aspect ratio\n// correcting scale up.\n\nstatic void I_InitStretchTables(byte *palette)\n{\n    if (stretch_tables[0] != NULL)\n    {\n        return;\n    }\n\n    // We only actually need two lookup tables:\n    //\n    // mix 0%   =  just write line 1\n    // mix 20%  =  stretch_tables[0]\n    // mix 40%  =  stretch_tables[1]\n    // mix 60%  =  stretch_tables[1] used backwards\n    // mix 80%  =  stretch_tables[0] used backwards\n    // mix 100% =  just write line 2\n\n    printf(\"I_InitStretchTables: Generating lookup tables..\");\n    fflush(stdout);\n    stretch_tables[0] = GenerateStretchTable(palette, 20);\n    printf(\"..\"); fflush(stdout);\n    stretch_tables[1] = GenerateStretchTable(palette, 40);\n    puts(\"\");\n}\n\n// Create 50%/50% table for 800x600 squash mode\n\nstatic void I_InitSquashTable(byte *palette)\n{\n    if (half_stretch_table != NULL)\n    {\n        return;\n    }\n\n    printf(\"I_InitSquashTable: Generating lookup table..\");\n    fflush(stdout);\n    half_stretch_table = GenerateStretchTable(palette, 50);\n    puts(\"\");\n}\n\n// Destroy the scaling lookup tables. This should only ever be called\n// if switching to a completely different palette from the normal one\n// (in which case the mappings no longer make any sense).\n\nvoid I_ResetScaleTables(byte *palette)\n{\n    if (stretch_tables[0] != NULL)\n    {\n        Z_Free(stretch_tables[0]);\n        Z_Free(stretch_tables[1]);\n\n        printf(\"I_ResetScaleTables: Regenerating lookup tables..\\n\");\n        stretch_tables[0] = GenerateStretchTable(palette, 20);\n        stretch_tables[1] = GenerateStretchTable(palette, 40);\n    }\n\n    if (half_stretch_table != NULL)\n    {\n        Z_Free(half_stretch_table);\n\n        printf(\"I_ResetScaleTables: Regenerating lookup table..\\n\");\n\n        half_stretch_table = GenerateStretchTable(palette, 50);\n    }\n}\n\n\n// \n// Aspect ratio correcting scale up functions.\n//\n// These double up pixels to stretch the screen when using a 4:3\n// screen mode.\n//\n\nstatic inline void WriteBlendedLine1x(byte *dest, byte *src1, byte *src2, \n                               byte *stretch_table)\n{\n    int x;\n\n    for (x=0; x<SCREENWIDTH; ++x)\n    {\n        *dest = stretch_table[*src1 * 256 + *src2];\n        ++dest;\n        ++src1;\n        ++src2;\n    }\n} \n\n// 1x stretch (320x240)\n\nstatic boolean I_Stretch1x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp;\n    int y;\n\n    // Only works with full screen update\n\n    if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)\n    {\n        return false;\n    }    \n\n    // Need to byte-copy from buffer into the screen buffer\n\n    bufp = src_buffer + y1 * SCREENWIDTH + x1;\n    screenp = (byte *) dest_buffer + y1 * dest_pitch + x1;\n\n    // For every 5 lines of src_buffer, 6 lines are written to dest_buffer\n    // (200 -> 240)\n\n    for (y=0; y<SCREENHEIGHT; y += 5)\n    {\n        // 100% line 0\n        memcpy(screenp, bufp, SCREENWIDTH);\n        screenp += dest_pitch;\n\n        // 20% line 0, 80% line 1\n        WriteBlendedLine1x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[0]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 40% line 1, 60% line 2\n        WriteBlendedLine1x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[1]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 60% line 2, 40% line 3\n        WriteBlendedLine1x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[1]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 80% line 3, 20% line 4\n        WriteBlendedLine1x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[0]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 100% line 4\n        memcpy(screenp, bufp, SCREENWIDTH);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_stretch_1x = {\n    SCREENWIDTH, SCREENHEIGHT_4_3,\n    I_InitStretchTables,\n    I_Stretch1x,\n    true,\n};\n\nstatic inline void WriteLine2x(byte *dest, byte *src)\n{\n    int x;\n\n    for (x=0; x<SCREENWIDTH; ++x)\n    {\n        dest[0] = *src;\n        dest[1] = *src;\n        dest += 2;\n        ++src;\n    }\n}\n\nstatic inline void WriteBlendedLine2x(byte *dest, byte *src1, byte *src2, \n                               byte *stretch_table)\n{\n    int x;\n    int val;\n\n    for (x=0; x<SCREENWIDTH; ++x)\n    {\n        val = stretch_table[*src1 * 256 + *src2];\n        dest[0] = val;\n        dest[1] = val;\n        dest += 2;\n        ++src1;\n        ++src2;\n    }\n} \n\n// 2x stretch (640x480)\n\nstatic boolean I_Stretch2x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp;\n    int y;\n\n    // Only works with full screen update\n\n    if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)\n    {\n        return false;\n    }    \n\n    // Need to byte-copy from buffer into the screen buffer\n\n    bufp = src_buffer + y1 * SCREENWIDTH + x1;\n    screenp = (byte *) dest_buffer + y1 * dest_pitch + x1;\n\n    // For every 5 lines of src_buffer, 12 lines are written to dest_buffer.\n    // (200 -> 480)\n\n    for (y=0; y<SCREENHEIGHT; y += 5)\n    {\n        // 100% line 0\n        WriteLine2x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 0\n        WriteLine2x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 40% line 0, 60% line 1\n        WriteBlendedLine2x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[1]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 100% line 1\n        WriteLine2x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 80% line 1, 20% line 2\n        WriteBlendedLine2x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[0]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 100% line 2\n        WriteLine2x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 2\n        WriteLine2x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 20% line 2, 80% line 3\n        WriteBlendedLine2x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[0]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 100% line 3\n        WriteLine2x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 60% line 3, 40% line 4\n        WriteBlendedLine2x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[1]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 100% line 4\n        WriteLine2x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 4\n        WriteLine2x(screenp, bufp);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_stretch_2x = {\n    SCREENWIDTH * 2, SCREENHEIGHT_4_3 * 2,\n    I_InitStretchTables,\n    I_Stretch2x,\n    false,\n};\n\nstatic inline void WriteLine3x(byte *dest, byte *src)\n{\n    int x;\n\n    for (x=0; x<SCREENWIDTH; ++x)\n    {\n        dest[0] = *src;\n        dest[1] = *src;\n        dest[2] = *src;\n        dest += 3;\n        ++src;\n    }\n}\n\nstatic inline void WriteBlendedLine3x(byte *dest, byte *src1, byte *src2, \n                               byte *stretch_table)\n{\n    int x;\n    int val;\n\n    for (x=0; x<SCREENWIDTH; ++x)\n    {\n        val = stretch_table[*src1 * 256 + *src2];\n        dest[0] = val;\n        dest[1] = val;\n        dest[2] = val;\n        dest += 3;\n        ++src1;\n        ++src2;\n    }\n} \n\n// 3x stretch (960x720)\n\nstatic boolean I_Stretch3x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp;\n    int y;\n\n    // Only works with full screen update\n\n    if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)\n    {\n        return false;\n    }    \n\n    // Need to byte-copy from buffer into the screen buffer\n\n    bufp = src_buffer + y1 * SCREENWIDTH + x1;\n    screenp = (byte *) dest_buffer + y1 * dest_pitch + x1;\n\n    // For every 5 lines of src_buffer, 18 lines are written to dest_buffer.\n    // (200 -> 720)\n\n    for (y=0; y<SCREENHEIGHT; y += 5)\n    {\n        // 100% line 0\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 0\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 0\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 60% line 0, 40% line 1\n        WriteBlendedLine3x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[1]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 100% line 1\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 1\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 1\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 20% line 1, 80% line 2\n        WriteBlendedLine3x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[0]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 100% line 2\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 2\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 80% line 2, 20% line 3\n        WriteBlendedLine3x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[0]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 100% line 3\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 3\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 3\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 40% line 3, 60% line 4\n        WriteBlendedLine3x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[1]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 100% line 4\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 4\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 4\n        WriteLine3x(screenp, bufp);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_stretch_3x = {\n    SCREENWIDTH * 3, SCREENHEIGHT_4_3 * 3,\n    I_InitStretchTables,\n    I_Stretch3x,\n    false,\n};\n\nstatic inline void WriteLine4x(byte *dest, byte *src)\n{\n    int x;\n\n    for (x=0; x<SCREENWIDTH; ++x)\n    {\n        dest[0] = *src;\n        dest[1] = *src;\n        dest[2] = *src;\n        dest[3] = *src;\n        dest += 4;\n        ++src;\n    }\n}\n\nstatic inline void WriteBlendedLine4x(byte *dest, byte *src1, byte *src2, \n                               byte *stretch_table)\n{\n    int x;\n    int val;\n\n    for (x=0; x<SCREENWIDTH; ++x)\n    {\n        val = stretch_table[*src1 * 256 + *src2];\n        dest[0] = val;\n        dest[1] = val;\n        dest[2] = val;\n        dest[3] = val;\n        dest += 4;\n        ++src1;\n        ++src2;\n    }\n} \n\n// 4x stretch (1280x960)\n\nstatic boolean I_Stretch4x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp;\n    int y;\n\n    // Only works with full screen update\n\n    if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)\n    {\n        return false;\n    }    \n\n    // Need to byte-copy from buffer into the screen buffer\n\n    bufp = src_buffer + y1 * SCREENWIDTH + x1;\n    screenp = (byte *) dest_buffer + y1 * dest_pitch + x1;\n\n    // For every 5 lines of src_buffer, 24 lines are written to dest_buffer.\n    // (200 -> 960)\n\n    for (y=0; y<SCREENHEIGHT; y += 5)\n    {\n        // 100% line 0\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 0\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 0\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 0\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 90% line 0, 20% line 1\n        WriteBlendedLine4x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[0]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 100% line 1\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 1\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 1\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 1\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 60% line 1, 40% line 2\n        WriteBlendedLine4x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[1]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 100% line 2\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 2\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 2\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 2\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 40% line 2, 60% line 3\n        WriteBlendedLine4x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[1]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 100% line 3\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 3\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 3\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 3\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 20% line 3, 80% line 4\n        WriteBlendedLine4x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[0]);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n\n        // 100% line 4\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 4\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 4\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 4\n        WriteLine4x(screenp, bufp);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_stretch_4x = {\n    SCREENWIDTH * 4, SCREENHEIGHT_4_3 * 4,\n    I_InitStretchTables,\n    I_Stretch4x,\n    false,\n};\n\nstatic inline void WriteLine5x(byte *dest, byte *src)\n{\n    int x;\n\n    for (x=0; x<SCREENWIDTH; ++x)\n    {\n        dest[0] = *src;\n        dest[1] = *src;\n        dest[2] = *src;\n        dest[3] = *src;\n        dest[4] = *src;\n        dest += 5;\n        ++src;\n    }\n}\n\n// 5x stretch (1600x1200)\n\nstatic boolean I_Stretch5x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp;\n    int y;\n\n    // Only works with full screen update\n\n    if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)\n    {\n        return false;\n    }    \n\n    // Need to byte-copy from buffer into the screen buffer\n\n    bufp = src_buffer + y1 * SCREENWIDTH + x1;\n    screenp = (byte *) dest_buffer + y1 * dest_pitch + x1;\n\n    // For every 1 line of src_buffer, 6 lines are written to dest_buffer.\n    // (200 -> 1200)\n\n    for (y=0; y<SCREENHEIGHT; y += 1)\n    {\n        // 100% line 0\n        WriteLine5x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 0\n        WriteLine5x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 0\n        WriteLine5x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 0\n        WriteLine5x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 0\n        WriteLine5x(screenp, bufp);\n        screenp += dest_pitch;\n\n        // 100% line 0\n        WriteLine5x(screenp, bufp);\n        screenp += dest_pitch; bufp += SCREENWIDTH;\n    }\n\n    // test hack for Porsche Monty... scan line simulation:\n    // See here: http://www.doomworld.com/vb/post/962612\n\n    if (M_CheckParm(\"-scanline\") > 0)\n    {\n        screenp = (byte *) dest_buffer + 2 * dest_pitch;\n\n        for (y=0; y<1198; y += 3)\n        {\n            memset(screenp, 0, 1600);\n\n            screenp += dest_pitch * 3;\n        }\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_stretch_5x = {\n    SCREENWIDTH * 5, SCREENHEIGHT_4_3 * 5,\n    I_InitStretchTables,\n    I_Stretch5x,\n    false,\n};\n\n//\n// Aspect ratio correcting \"squash\" functions. \n//\n// These do the opposite of the \"stretch\" functions above: while the\n// stretch functions increase the vertical dimensions, the squash\n// functions decrease the horizontal dimensions for the same result.\n//\n// The same blend tables from the stretch functions are reused; as \n// a result, the dimensions are *slightly* wrong (eg. 320x200 should\n// squash to 266x200, but actually squashes to 256x200).\n//\n\n// \n// 1x squashed scale (256x200)\n//\n\nstatic inline void WriteSquashedLine1x(byte *dest, byte *src)\n{\n    int x;\n\n    for (x=0; x<SCREENWIDTH; )\n    {\n        // Draw in blocks of 5\n\n        // 80% pixel 0,   20% pixel 1\n\n        *dest++ = stretch_tables[0][src[1] * 256 + src[0]];\n\n        // 60% pixel 1,   40% pixel 2\n\n        *dest++ = stretch_tables[1][src[2] * 256 + src[1]];\n\n        // 40% pixel 2,   60% pixel 3\n\n        *dest++ = stretch_tables[1][src[2] * 256 + src[3]];\n\n        // 20% pixel 3,   80% pixel 4\n\n        *dest++ = stretch_tables[0][src[3] * 256 + src[4]];\n\n        x += 5;\n        src += 5;\n    }\n}\n\n// 1x squashed (256x200)\n\nstatic boolean I_Squash1x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp;\n    int y;\n\n    // Only works with full screen update\n\n    if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)\n    {\n        return false;\n    }    \n\n    bufp = src_buffer;\n    screenp = (byte *) dest_buffer;\n\n    for (y=0; y<SCREENHEIGHT; ++y) \n    {\n        WriteSquashedLine1x(screenp, bufp);\n\n        screenp += dest_pitch;\n        bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_squash_1x = {\n    SCREENWIDTH_4_3, SCREENHEIGHT,\n    I_InitStretchTables,\n    I_Squash1x,\n    true,\n};\n\n\n//\n// 2x squashed scale (512x400)\n//\n\n#define DRAW_PIXEL2 \\\n      *dest++ = *dest2++ = c;\n\nstatic inline void WriteSquashedLine2x(byte *dest, byte *src)\n{\n    byte *dest2;\n    int x, c;\n\n    dest2 = dest + dest_pitch;\n\n    for (x=0; x<SCREENWIDTH; )\n    {\n        // Draw in blocks of 5\n\n        // 100% pixel 0\n\n        c = src[0];\n        DRAW_PIXEL2;\n\n        // 60% pixel 0, 40% pixel 1\n\n        c = stretch_tables[1][src[1] * 256 + src[0]];\n        DRAW_PIXEL2;\n\n        // 100% pixel 1\n\n        c = src[1];\n        DRAW_PIXEL2;\n\n        // 20% pixel 1, 80% pixel 2\n\n        c = stretch_tables[0][src[1] * 256 + src[2]];\n        DRAW_PIXEL2;\n\n        // 80% pixel 2, 20% pixel 3\n\n        c = stretch_tables[0][src[3] * 256 + src[2]];\n        DRAW_PIXEL2;\n\n        // 100% pixel 3\n\n        c = src[3];\n        DRAW_PIXEL2;\n\n        // 40% pixel 3, 60% pixel 4\n\n        c = stretch_tables[1][src[3] * 256 + src[4]];\n        DRAW_PIXEL2;\n\n        // 100% pixel 4\n\n        c = src[4];\n        DRAW_PIXEL2;\n\n        x += 5;\n        src += 5;\n    }\n}\n\n// 2x squash (512x400)\n\nstatic boolean I_Squash2x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp;\n    int y;\n\n    // Only works with full screen update\n\n    if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)\n    {\n        return false;\n    }    \n\n    bufp = src_buffer;\n    screenp = (byte *) dest_buffer;\n\n    for (y=0; y<SCREENHEIGHT; ++y) \n    {\n        WriteSquashedLine2x(screenp, bufp);\n\n        screenp += dest_pitch * 2;\n        bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_squash_2x = {\n    SCREENWIDTH_4_3 * 2, SCREENHEIGHT * 2,\n    I_InitStretchTables,\n    I_Squash2x,\n    false,\n};\n\n\n#define DRAW_PIXEL3 \\\n        *dest++ = *dest2++ = *dest3++ = c\n\nstatic inline void WriteSquashedLine3x(byte *dest, byte *src)\n{\n    byte *dest2, *dest3;\n    int x, c;\n\n    dest2 = dest + dest_pitch;\n    dest3 = dest + dest_pitch * 2;\n\n    for (x=0; x<SCREENWIDTH; )\n    {\n        // Every 2 pixels is expanded to 5 pixels\n\n        // 100% pixel 0 x2\n\n        c = src[0];\n\n        DRAW_PIXEL3;\n        DRAW_PIXEL3;\n\n        // 50% pixel 0, 50% pixel 1\n\n        c = half_stretch_table[src[0] * 256 + src[1]];\n\n        DRAW_PIXEL3;\n\n        // 100% pixel 1\n\n        c = src[1];\n\n        DRAW_PIXEL3;\n        DRAW_PIXEL3;\n\n        x += 2;\n        src += 2;\n    }\n}\n\n\n//\n// 3x scale squashed (800x600)\n//\n// This is a special case that uses the half_stretch_table (50%) rather\n// than the normal stretch_tables(20,40%), to scale up to 800x600 \n// exactly.\n//\n\nstatic boolean I_Squash3x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp;\n    int y;\n\n    // Only works with full screen update\n\n    if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)\n    {\n        return false;\n    }    \n\n    bufp = src_buffer;\n    screenp = (byte *) dest_buffer;\n\n    for (y=0; y<SCREENHEIGHT; ++y) \n    {\n        WriteSquashedLine3x(screenp, bufp);\n\n        screenp += dest_pitch * 3;\n        bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_squash_3x = {\n    800, 600,\n    I_InitSquashTable,\n    I_Squash3x,\n    false,\n};\n\n#define DRAW_PIXEL4 \\\n        *dest++ = *dest2++ = *dest3++ = *dest4++ = c;\n      \nstatic inline void WriteSquashedLine4x(byte *dest, byte *src)\n{\n    int x;\n    int c;\n    byte *dest2, *dest3, *dest4;\n\n    dest2 = dest + dest_pitch;\n    dest3 = dest + dest_pitch * 2;\n    dest4 = dest + dest_pitch * 3;\n\n    for (x=0; x<SCREENWIDTH; )\n    {\n        // Draw in blocks of 5\n\n        // 100% pixel 0  x3\n\n        c = src[0];\n        DRAW_PIXEL4;\n        DRAW_PIXEL4;\n        DRAW_PIXEL4;\n\n        // 20% pixel 0,  80% pixel 1\n\n        c = stretch_tables[0][src[0] * 256 + src[1]];\n        DRAW_PIXEL4;\n\n        // 100% pixel 1 x2\n\n        c = src[1];\n        DRAW_PIXEL4;\n        DRAW_PIXEL4;\n\n        // 40% pixel 1, 60% pixel 2\n\n        c = stretch_tables[1][src[1] * 256 + src[2]];\n        DRAW_PIXEL4;\n\n        // 100% pixel 2 x2\n\n        c = src[2];\n        DRAW_PIXEL4;\n        DRAW_PIXEL4;\n\n        // 60% pixel 2, 40% pixel 3\n\n        c = stretch_tables[1][src[3] * 256 + src[2]];\n        DRAW_PIXEL4;\n\n        // 100% pixel 3 x2\n\n        c = src[3];\n        DRAW_PIXEL4;\n        DRAW_PIXEL4;\n\n        // 80% pixel 3, 20% pixel 4\n\n        c = stretch_tables[0][src[4] * 256 + src[3]];\n        DRAW_PIXEL4;\n\n        // 100% pixel 4\n\n        c = src[4];\n        DRAW_PIXEL4;\n        DRAW_PIXEL4;\n        DRAW_PIXEL4;\n\n        x += 5;\n        src += 5;\n    }\n}\n\n//\n// 4x squashed (1024x800)\n//\n\nstatic boolean I_Squash4x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp;\n    int y;\n\n    // Only works with full screen update\n\n    if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)\n    {\n        return false;\n    }    \n\n    bufp = src_buffer;\n    screenp = (byte *) dest_buffer;\n\n    for (y=0; y<SCREENHEIGHT; ++y) \n    {\n        WriteSquashedLine4x(screenp, bufp);\n\n        screenp += dest_pitch * 4;\n        bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_squash_4x = {\n    SCREENWIDTH_4_3 * 4, SCREENHEIGHT * 4,\n    I_InitStretchTables,\n    I_Squash4x,\n    false,\n};\n\n#define DRAW_PIXEL5 \\\n        *dest++ = *dest2++ = *dest3++ = *dest4++ = *dest5++ = c\n\nstatic inline void WriteSquashedLine5x(byte *dest, byte *src)\n{\n    int x;\n    int c;\n    byte *dest2, *dest3, *dest4, *dest5;\n\n    dest2 = dest + dest_pitch;\n    dest3 = dest + dest_pitch * 2;\n    dest4 = dest + dest_pitch * 3;\n    dest5 = dest + dest_pitch * 4;\n\n    for (x=0; x<SCREENWIDTH; ++x)\n    {\n        // Draw in blocks of 5\n\n        // 100% pixel 0  x4\n\n        c = *src++;\n        DRAW_PIXEL5;\n        DRAW_PIXEL5;\n        DRAW_PIXEL5;\n        DRAW_PIXEL5;\n    }\n}\n\n//\n// 5x squashed (1280x1000)\n//\n\nstatic boolean I_Squash5x(int x1, int y1, int x2, int y2)\n{\n    byte *bufp, *screenp;\n    int y;\n\n    // Only works with full screen update\n\n    if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)\n    {\n        return false;\n    }    \n\n    bufp = src_buffer;\n    screenp = (byte *) dest_buffer;\n\n    for (y=0; y<SCREENHEIGHT; ++y) \n    {\n        WriteSquashedLine5x(screenp, bufp);\n\n        screenp += dest_pitch * 5;\n        bufp += SCREENWIDTH;\n    }\n\n    return true;\n}\n\nscreen_mode_t mode_squash_5x = {\n    SCREENWIDTH_4_3 * 5, SCREENHEIGHT * 5,\n    I_InitStretchTables,\n    I_Squash5x,\n    false,\n};\n\n\n"
  },
  {
    "path": "fbdoom/i_scale.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//      Pixel-doubling scale up functions.\n//\n\n\n#ifndef __I_SCALE__\n#define __I_SCALE__\n\n#include \"doomtype.h\"\n\nvoid I_InitScale(byte *_src_buffer, byte *_dest_buffer, int _dest_pitch);\nvoid I_ResetScaleTables(byte *palette);\n\n// Scaled modes (direct multiples of 320x200)\n\nextern screen_mode_t mode_scale_1x;\nextern screen_mode_t mode_scale_2x;\nextern screen_mode_t mode_scale_3x;\nextern screen_mode_t mode_scale_4x;\nextern screen_mode_t mode_scale_5x;\n\n// Vertically stretched modes (320x200 -> multiples of 320x240)\n\nextern screen_mode_t mode_stretch_1x;\nextern screen_mode_t mode_stretch_2x;\nextern screen_mode_t mode_stretch_3x;\nextern screen_mode_t mode_stretch_4x;\nextern screen_mode_t mode_stretch_5x;\n\n// Horizontally squashed modes (320x200 -> multiples of 256x200)\n\nextern screen_mode_t mode_squash_1x;\nextern screen_mode_t mode_squash_2x;\nextern screen_mode_t mode_squash_3x;\nextern screen_mode_t mode_squash_4x;\nextern screen_mode_t mode_squash_5x;\n\n#endif /* #ifndef __I_SCALE__ */\n\n"
  },
  {
    "path": "fbdoom/i_sdlmusic.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tSystem interface for music.\n//\n\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"SDL.h\"\n#include \"SDL_mixer.h\"\n\n#include \"config.h\"\n#include \"doomtype.h\"\n#include \"memio.h\"\n#include \"mus2mid.h\"\n\n#include \"deh_str.h\"\n#include \"gusconf.h\"\n#include \"i_sound.h\"\n#include \"i_system.h\"\n#include \"i_swap.h\"\n#include \"m_argv.h\"\n#include \"m_config.h\"\n#include \"m_misc.h\"\n#include \"sha1.h\"\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\n#define MAXMIDLENGTH (96 * 1024)\n#define MID_HEADER_MAGIC \"MThd\"\n#define MUS_HEADER_MAGIC \"MUS\\x1a\"\n\n#define FLAC_HEADER \"fLaC\"\n#define OGG_HEADER \"OggS\"\n\n// Looping Vorbis metadata tag names. These have been defined by ZDoom\n// for specifying the start and end positions for looping music tracks\n// in .ogg and .flac files.\n// More information is here: http://zdoom.org/wiki/Audio_loop\n#define LOOP_START_TAG \"LOOP_START\"\n#define LOOP_END_TAG   \"LOOP_END\"\n\n// FLAC metadata headers that we care about.\n#define FLAC_STREAMINFO      0\n#define FLAC_VORBIS_COMMENT  4\n\n// Ogg metadata headers that we care about.\n#define OGG_ID_HEADER        1\n#define OGG_COMMENT_HEADER   3\n\n// Structure for music substitution.\n// We store a mapping based on SHA1 checksum -> filename of substitute music\n// file to play, so that substitution occurs based on content rather than\n// lump name. This has some inherent advantages:\n//  * Music for Plutonia (reused from Doom 1) works automatically.\n//  * If a PWAD replaces music, the replacement music is used rather than\n//    the substitute music for the IWAD.\n//  * If a PWAD reuses music from an IWAD (even from a different game), we get\n//    the high quality version of the music automatically (neat!)\n\ntypedef struct\n{\n    sha1_digest_t hash;\n    char *filename;\n} subst_music_t;\n\n// Structure containing parsed metadata read from a digital music track:\ntypedef struct\n{\n    boolean valid;\n    unsigned int samplerate_hz;\n    int start_time, end_time;\n} file_metadata_t;\n\nstatic subst_music_t *subst_music = NULL;\nstatic unsigned int subst_music_len = 0;\n\nstatic const char *subst_config_filenames[] =\n{\n    \"doom1-music.cfg\",\n    \"doom2-music.cfg\",\n    \"tnt-music.cfg\",\n    \"heretic-music.cfg\",\n    \"hexen-music.cfg\",\n    \"strife-music.cfg\",\n};\n\nstatic boolean music_initialized = false;\n\n// If this is true, this module initialized SDL sound and has the \n// responsibility to shut it down\n\nstatic boolean sdl_was_initialized = false;\n\nstatic boolean musicpaused = false;\nstatic int current_music_volume;\n\nchar *timidity_cfg_path = \"\";\n\nstatic char *temp_timidity_cfg = NULL;\n\n// If true, we are playing a substitute digital track rather than in-WAD\n// MIDI/MUS track, and file_metadata contains loop metadata.\nstatic boolean playing_substitute = false;\nstatic file_metadata_t file_metadata;\n\n// Position (in samples) that we have reached in the current track.\n// This is updated by the TrackPositionCallback function.\nstatic unsigned int current_track_pos;\n\n// Currently playing music track.\nstatic Mix_Music *current_track_music = NULL;\n\n// If true, the currently playing track is being played on loop.\nstatic boolean current_track_loop;\n\n// Given a time string (for LOOP_START/LOOP_END), parse it and return\n// the time (in # samples since start of track) it represents.\nstatic unsigned int ParseVorbisTime(unsigned int samplerate_hz, char *value)\n{\n    char *num_start, *p;\n    unsigned int result = 0;\n    char c;\n\n    if (strchr(value, ':') == NULL)\n    {\n\treturn atoi(value);\n    }\n\n    result = 0;\n    num_start = value;\n\n    for (p = value; *p != '\\0'; ++p)\n    {\n        if (*p == '.' || *p == ':')\n        {\n            c = *p; *p = '\\0';\n            result = result * 60 + atoi(num_start);\n            num_start = p + 1;\n            *p = c;\n        }\n\n        if (*p == '.')\n        {\n            return result * samplerate_hz\n\t         + (unsigned int) (atof(p) * samplerate_hz);\n        }\n    }\n\n    return (result * 60 + atoi(num_start)) * samplerate_hz;\n}\n\n// Given a vorbis comment string (eg. \"LOOP_START=12345\"), set fields\n// in the metadata structure as appropriate.\nstatic void ParseVorbisComment(file_metadata_t *metadata, char *comment)\n{\n    char *eq, *key, *value;\n\n    eq = strchr(comment, '=');\n\n    if (eq == NULL)\n    {\n        return;\n    }\n\n    key = comment;\n    *eq = '\\0';\n    value = eq + 1;\n\n    if (!strcmp(key, LOOP_START_TAG))\n    {\n        metadata->start_time = ParseVorbisTime(metadata->samplerate_hz, value);\n    }\n    else if (!strcmp(key, LOOP_END_TAG))\n    {\n        metadata->end_time = ParseVorbisTime(metadata->samplerate_hz, value);\n    }\n}\n\n// Parse a vorbis comments structure, reading from the given file.\nstatic void ParseVorbisComments(file_metadata_t *metadata, FILE *fs)\n{\n    uint32_t buf;\n    unsigned int num_comments, i, comment_len;\n    char *comment;\n\n    // We must have read the sample rate already from an earlier header.\n    if (metadata->samplerate_hz == 0)\n    {\n\treturn;\n    }\n\n    // Skip the starting part we don't care about.\n    if (fread(&buf, 4, 1, fs) < 1)\n    {\n        return;\n    }\n    if (fseek(fs, LONG(buf), SEEK_CUR) != 0)\n    {\n\treturn;\n    }\n\n    // Read count field for number of comments.\n    if (fread(&buf, 4, 1, fs) < 1)\n    {\n        return;\n    }\n    num_comments = LONG(buf);\n\n    // Read each individual comment.\n    for (i = 0; i < num_comments; ++i)\n    {\n        // Read length of comment.\n        if (fread(&buf, 4, 1, fs) < 1)\n\t{\n            return;\n\t}\n\n        comment_len = LONG(buf);\n\n        // Read actual comment data into string buffer.\n        comment = calloc(1, comment_len + 1);\n        if (comment == NULL\n         || fread(comment, 1, comment_len, fs) < comment_len)\n        {\n            free(comment);\n            break;\n        }\n\n        // Parse comment string.\n        ParseVorbisComment(metadata, comment);\n        free(comment);\n    }\n}\n\nstatic void ParseFlacStreaminfo(file_metadata_t *metadata, FILE *fs)\n{\n    byte buf[34];\n\n    // Read block data.\n    if (fread(buf, sizeof(buf), 1, fs) < 1)\n    {\n        return;\n    }\n\n    // We only care about sample rate and song length.\n    metadata->samplerate_hz = (buf[10] << 12) | (buf[11] << 4)\n                            | (buf[12] >> 4);\n    // Song length is actually a 36 bit field, but 32 bits should be\n    // enough for everybody.\n    //metadata->song_length = (buf[14] << 24) | (buf[15] << 16)\n    //                      | (buf[16] << 8) | buf[17];\n}\n\nstatic void ParseFlacFile(file_metadata_t *metadata, FILE *fs)\n{\n    byte header[4];\n    unsigned int block_type;\n    size_t block_len;\n    boolean last_block;\n\n    for (;;)\n    {\n        long pos = -1;\n\n        // Read METADATA_BLOCK_HEADER:\n        if (fread(header, 4, 1, fs) < 1)\n        {\n            return;\n        }\n\n        block_type = header[0] & ~0x80;\n        last_block = (header[0] & 0x80) != 0;\n        block_len = (header[1] << 16) | (header[2] << 8) | header[3];\n\n        pos = ftell(fs);\n        if (pos < 0)\n        {\n            return;\n        }\n\n        if (block_type == FLAC_STREAMINFO)\n        {\n            ParseFlacStreaminfo(metadata, fs);\n        }\n        else if (block_type == FLAC_VORBIS_COMMENT)\n        {\n            ParseVorbisComments(metadata, fs);\n        }\n\n        if (last_block)\n        {\n            break;\n        }\n\n        // Seek to start of next block.\n        if (fseek(fs, pos + block_len, SEEK_SET) != 0)\n        {\n            return;\n        }\n    }\n}\n\nstatic void ParseOggIdHeader(file_metadata_t *metadata, FILE *fs)\n{\n    byte buf[21];\n\n    if (fread(buf, sizeof(buf), 1, fs) < 1)\n    {\n        return;\n    }\n\n    metadata->samplerate_hz = (buf[8] << 24) | (buf[7] << 16)\n                            | (buf[6] << 8) | buf[5];\n}\n\nstatic void ParseOggFile(file_metadata_t *metadata, FILE *fs)\n{\n    byte buf[7];\n    unsigned int offset;\n\n    // Scan through the start of the file looking for headers. They\n    // begin '[byte]vorbis' where the byte value indicates header type.\n    memset(buf, 0, sizeof(buf));\n\n    for (offset = 0; offset < 100 * 1024; ++offset)\n    {\n\t// buf[] is used as a sliding window. Each iteration, we\n\t// move the buffer one byte to the left and read an extra\n\t// byte onto the end.\n        memmove(buf, buf + 1, sizeof(buf) - 1);\n\n        if (fread(&buf[6], 1, 1, fs) < 1)\n        {\n            return;\n        }\n\n        if (!memcmp(buf + 1, \"vorbis\", 6))\n        {\n            switch (buf[0])\n            {\n                case OGG_ID_HEADER:\n                    ParseOggIdHeader(metadata, fs);\n                    break;\n                case OGG_COMMENT_HEADER:\n\t\t    ParseVorbisComments(metadata, fs);\n                    break;\n                default:\n                    break;\n            }\n        }\n    }\n}\n\nstatic void ReadLoopPoints(char *filename, file_metadata_t *metadata)\n{\n    FILE *fs;\n    char header[4];\n\n    metadata->valid = false;\n    metadata->samplerate_hz = 0;\n    metadata->start_time = 0;\n    metadata->end_time = -1;\n\n    fs = fopen(filename, \"r\");\n\n    if (fs == NULL)\n    {\n        return;\n    }\n\n    // Check for a recognized file format; use the first four bytes\n    // of the file.\n\n    if (fread(header, 4, 1, fs) < 1)\n    {\n        fclose(fs);\n        return;\n    }\n\n    if (memcmp(header, FLAC_HEADER, 4) == 0)\n    {\n        ParseFlacFile(metadata, fs);\n    }\n    else if (memcmp(header, OGG_HEADER, 4) == 0)\n    {\n        ParseOggFile(metadata, fs);\n    }\n\n    fclose(fs);\n\n    // Only valid if at the very least we read the sample rate.\n    metadata->valid = metadata->samplerate_hz > 0;\n}\n\n// Given a MUS lump, look up a substitute MUS file to play instead\n// (or NULL to just use normal MIDI playback).\n\nstatic char *GetSubstituteMusicFile(void *data, size_t data_len)\n{\n    sha1_context_t context;\n    sha1_digest_t hash;\n    char *filename;\n    int i;\n\n    // Don't bother doing a hash if we're never going to find anything.\n    if (subst_music_len == 0)\n    {\n        return NULL;\n    }\n\n    SHA1_Init(&context);\n    SHA1_Update(&context, data, data_len);\n    SHA1_Final(hash, &context);\n\n    // Look for a hash that matches.\n    // The substitute mapping list can (intentionally) contain multiple\n    // filename mappings for the same hash. This allows us to try\n    // different files and fall back if our first choice isn't found.\n\n    filename = NULL;\n\n    for (i = 0; i < subst_music_len; ++i)\n    {\n        if (memcmp(hash, subst_music[i].hash, sizeof(hash)) == 0)\n        {\n            filename = subst_music[i].filename;\n\n            // If the file exists, then use this file in preference to\n            // any fallbacks. But we always return a filename if it's\n            // in the list, even if it's just so we can print an error\n            // message to the user saying it doesn't exist.\n            if (M_FileExists(filename))\n            {\n                break;\n            }\n        }\n    }\n\n    return filename;\n}\n\n// Add a substitute music file to the lookup list.\n\nstatic void AddSubstituteMusic(subst_music_t *subst)\n{\n    ++subst_music_len;\n    subst_music =\n        realloc(subst_music, sizeof(subst_music_t) * subst_music_len);\n    memcpy(&subst_music[subst_music_len - 1], subst, sizeof(subst_music_t));\n}\n\nstatic int ParseHexDigit(char c)\n{\n    c = tolower(c);\n\n    if (c >= '0' && c <= '9')\n    {\n        return c - '0';\n    }\n    else if (c >= 'a' && c <= 'f')\n    {\n        return 10 + (c - 'a');\n    }\n    else\n    {\n        return -1;\n    }\n}\n\nstatic char *GetFullPath(char *base_filename, char *path)\n{\n    char *basedir, *result;\n    char *p;\n\n    // Starting with directory separator means we have an absolute path,\n    // so just return it.\n    if (path[0] == DIR_SEPARATOR)\n    {\n        return strdup(path);\n    }\n\n#ifdef _WIN32\n    // d:\\path\\...\n    if (isalpha(path[0]) && path[1] == ':' && path[2] == DIR_SEPARATOR)\n    {\n        return strdup(path);\n    }\n#endif\n\n    // Paths in the substitute filenames can contain Unix-style /\n    // path separators, but we should convert this to the separator\n    // for the native platform.\n    path = M_StringReplace(path, \"/\", DIR_SEPARATOR_S);\n\n    // Copy config filename and cut off the filename to just get the\n    // parent dir.\n    basedir = strdup(base_filename);\n    p = strrchr(basedir, DIR_SEPARATOR);\n    if (p != NULL)\n    {\n        p[1] = '\\0';\n        result = M_StringJoin(basedir, path, NULL);\n    }\n    else\n    {\n        result = strdup(path);\n    }\n    free(basedir);\n    free(path);\n\n    return result;\n}\n\n// Parse a line from substitute music configuration file; returns error\n// message or NULL for no error.\n\nstatic char *ParseSubstituteLine(char *filename, char *line)\n{\n    subst_music_t subst;\n    char *p;\n    int hash_index;\n\n    // Strip out comments if present.\n    p = strchr(line, '#');\n    if (p != NULL)\n    {\n        while (p > line && isspace(*(p - 1)))\n        {\n            --p;\n        }\n        *p = '\\0';\n    }\n\n    // Skip leading spaces.\n    for (p = line; *p != '\\0' && isspace(*p); ++p);\n\n    // Empty line? This includes comment lines now that comments have\n    // been stripped.\n    if (*p == '\\0')\n    {\n        return NULL;\n    }\n\n    // Read hash.\n    hash_index = 0;\n    while (*p != '\\0' && *p != '=' && !isspace(*p))\n    {\n        int d1, d2;\n\n        d1 = ParseHexDigit(p[0]);\n        d2 = ParseHexDigit(p[1]);\n\n        if (d1 < 0 || d2 < 0)\n        {\n            return \"Invalid hex digit in SHA1 hash\";\n        }\n        else if (hash_index >= sizeof(sha1_digest_t))\n        {\n            return \"SHA1 hash too long\";\n        }\n\n        subst.hash[hash_index] = (d1 << 4) | d2;\n        ++hash_index;\n\n        p += 2;\n    }\n\n    if (hash_index != sizeof(sha1_digest_t))\n    {\n        return \"SHA1 hash too short\";\n    }\n\n    // Skip spaces.\n    for (; *p != '\\0' && isspace(*p); ++p);\n\n    if (*p != '=')\n    {\n        return \"Expected '='\";\n    }\n\n    ++p;\n\n    // Skip spaces.\n    for (; *p != '\\0' && isspace(*p); ++p);\n\n    // We're now at the filename. Cut off trailing space characters.\n    while (strlen(p) > 0 && isspace(p[strlen(p) - 1]))\n    {\n        p[strlen(p) - 1] = '\\0';\n    }\n\n    if (strlen(p) == 0)\n    {\n        return \"No filename specified for music substitution\";\n    }\n\n    // Expand full path and add to our database of substitutes.\n    subst.filename = GetFullPath(filename, p);\n    AddSubstituteMusic(&subst);\n\n    return NULL;\n}\n\n// Read a substitute music configuration file.\n\nstatic boolean ReadSubstituteConfig(char *filename)\n{\n    char line[128];\n    FILE *fs;\n    char *error;\n    int linenum = 1;\n    int old_subst_music_len;\n\n    fs = fopen(filename, \"r\");\n\n    if (fs == NULL)\n    {\n        return false;\n    }\n\n    old_subst_music_len = subst_music_len;\n\n    while (!feof(fs))\n    {\n        M_StringCopy(line, \"\", sizeof(line));\n        fgets(line, sizeof(line), fs);\n\n        error = ParseSubstituteLine(filename, line);\n\n        if (error != NULL)\n        {\n            fprintf(stderr, \"%s:%i: Error: %s\\n\", filename, linenum, error);\n        }\n\n        ++linenum;\n    }\n\n    fclose(fs);\n\n    return true;\n}\n\n// Find substitute configs and try to load them.\n\nstatic void LoadSubstituteConfigs(void)\n{\n    char *musicdir;\n    char *path;\n    unsigned int i;\n\n    if (!strcmp(configdir, \"\"))\n    {\n        musicdir = strdup(\"\");\n    }\n    else\n    {\n        musicdir = M_StringJoin(configdir, \"music\", DIR_SEPARATOR_S, NULL);\n    }\n\n    // Load all music packs. We always load all music substitution packs for\n    // all games. Why? Suppose we have a Doom PWAD that reuses some music from\n    // Heretic. If we have the Heretic music pack loaded, then we get an\n    // automatic substitution.\n    for (i = 0; i < arrlen(subst_config_filenames); ++i)\n    {\n        path = M_StringJoin(musicdir, subst_config_filenames[i], NULL);\n        ReadSubstituteConfig(path);\n        free(path);\n    }\n\n    free(musicdir);\n\n    if (subst_music_len > 0)\n    {\n        printf(\"Loaded %i music substitutions from config files.\\n\",\n               subst_music_len);\n    }\n}\n\n// Returns true if the given lump number is a music lump that should\n// be included in substitute configs.\n// Identifying music lumps by name is not feasible; some games (eg.\n// Heretic, Hexen) don't have a common naming pattern for music lumps.\n\nstatic boolean IsMusicLump(int lumpnum)\n{\n    byte *data;\n    boolean result;\n\n    if (W_LumpLength(lumpnum) < 4)\n    {\n        return false;\n    }\n\n    data = W_CacheLumpNum(lumpnum, PU_STATIC);\n\n    result = memcmp(data, MUS_HEADER_MAGIC, 4) == 0\n          || memcmp(data, MID_HEADER_MAGIC, 4) == 0;\n\n    W_ReleaseLumpNum(lumpnum);\n\n    return result;\n}\n\n// Dump an example config file containing checksums for all MIDI music\n// found in the WAD directory.\n\nstatic void DumpSubstituteConfig(char *filename)\n{\n    sha1_context_t context;\n    sha1_digest_t digest;\n    char name[9];\n    byte *data;\n    FILE *fs;\n    int lumpnum, h;\n\n    fs = fopen(filename, \"w\");\n\n    if (fs == NULL)\n    {\n        I_Error(\"Failed to open %s for writing\", filename);\n        return;\n    }\n\n    fprintf(fs, \"# Example %s substitute MIDI file.\\n\\n\", PACKAGE_NAME);\n    fprintf(fs, \"# SHA1 hash                              = filename\\n\");\n\n    for (lumpnum = 0; lumpnum < numlumps; ++lumpnum)\n    {\n        strncpy(name, lumpinfo[lumpnum].name, 8);\n        name[8] = '\\0';\n\n        if (!IsMusicLump(lumpnum))\n        {\n            continue;\n        }\n\n        // Calculate hash.\n        data = W_CacheLumpNum(lumpnum, PU_STATIC);\n        SHA1_Init(&context);\n        SHA1_Update(&context, data, W_LumpLength(lumpnum));\n        SHA1_Final(digest, &context);\n        W_ReleaseLumpNum(lumpnum);\n\n        // Print line.\n        for (h = 0; h < sizeof(sha1_digest_t); ++h)\n        {\n            fprintf(fs, \"%02x\", digest[h]);\n        }\n\n        fprintf(fs, \" = %s.ogg\\n\", name);\n    }\n\n    fprintf(fs, \"\\n\");\n    fclose(fs);\n\n    printf(\"Substitute MIDI config file written to %s.\\n\", filename);\n    I_Quit();\n}\n\n// If the temp_timidity_cfg config variable is set, generate a \"wrapper\"\n// config file for Timidity to point to the actual config file. This\n// is needed to inject a \"dir\" command so that the patches are read\n// relative to the actual config file.\n\nstatic boolean WriteWrapperTimidityConfig(char *write_path)\n{\n    char *p, *path;\n    FILE *fstream;\n\n    if (!strcmp(timidity_cfg_path, \"\"))\n    {\n        return false;\n    }\n\n    fstream = fopen(write_path, \"w\");\n\n    if (fstream == NULL)\n    {\n        return false;\n    }\n\n    p = strrchr(timidity_cfg_path, DIR_SEPARATOR);\n    if (p != NULL)\n    {\n        path = strdup(timidity_cfg_path);\n        path[p - timidity_cfg_path] = '\\0';\n        fprintf(fstream, \"dir %s\\n\", path);\n        free(path);\n    }\n\n    fprintf(fstream, \"source %s\\n\", timidity_cfg_path);\n    fclose(fstream);\n\n    return true;\n}\n\nvoid I_InitTimidityConfig(void)\n{\n    char *env_string;\n    boolean success;\n\n    temp_timidity_cfg = M_TempFile(\"timidity.cfg\");\n\n    if (snd_musicdevice == SNDDEVICE_GUS)\n    {\n        success = GUS_WriteConfig(temp_timidity_cfg);\n    }\n    else\n    {\n        success = WriteWrapperTimidityConfig(temp_timidity_cfg);\n    }\n\n    // Set the TIMIDITY_CFG environment variable to point to the temporary\n    // config file.\n\n    if (success)\n    {\n        env_string = M_StringJoin(\"TIMIDITY_CFG=\", temp_timidity_cfg, NULL);\n        putenv(env_string);\n    }\n    else\n    {\n        free(temp_timidity_cfg);\n        temp_timidity_cfg = NULL;\n    }\n}\n\n// Remove the temporary config file generated by I_InitTimidityConfig().\n\nstatic void RemoveTimidityConfig(void)\n{\n    if (temp_timidity_cfg != NULL)\n    {\n        remove(temp_timidity_cfg);\n        free(temp_timidity_cfg);\n    }\n}\n\n// Shutdown music\n\nstatic void I_SDL_ShutdownMusic(void)\n{\n    if (music_initialized)\n    {\n        Mix_HaltMusic();\n        music_initialized = false;\n\n        if (sdl_was_initialized)\n        {\n            Mix_CloseAudio();\n            SDL_QuitSubSystem(SDL_INIT_AUDIO);\n            sdl_was_initialized = false;\n        }\n    }\n}\n\nstatic boolean SDLIsInitialized(void)\n{\n    int freq, channels;\n    Uint16 format;\n\n    return Mix_QuerySpec(&freq, &format, &channels) != 0;\n}\n\n// Callback function that is invoked to track current track position.\nvoid TrackPositionCallback(int chan, void *stream, int len, void *udata)\n{\n    // Position is doubled up twice: for 16-bit samples and for stereo.\n    current_track_pos += len / 4;\n}\n\n// Initialize music subsystem\nstatic boolean I_SDL_InitMusic(void)\n{\n    int i;\n\n    // SDL_mixer prior to v1.2.11 has a bug that causes crashes\n    // with MIDI playback.  Print a warning message if we are\n    // using an old version.\n\n#ifdef __MACOSX__\n    {\n        const SDL_version *v = Mix_Linked_Version();\n\n        if (SDL_VERSIONNUM(v->major, v->minor, v->patch)\n          < SDL_VERSIONNUM(1, 2, 11))\n        {\n            printf(\"\\n\"\n               \"                   *** WARNING ***\\n\"\n               \"      You are using an old version of SDL_mixer.\\n\"\n               \"      Music playback on this version may cause crashes\\n\"\n               \"      under OS X and is disabled by default.\\n\"\n               \"\\n\");\n        }\n    }\n#endif\n\n    //!\n    // @arg <output filename>\n    //\n    // Read all MIDI files from loaded WAD files, dump an example substitution\n    // music config file to the specified filename and quit.\n    //\n\n    i = M_CheckParmWithArgs(\"-dumpsubstconfig\", 1);\n\n    if (i > 0)\n    {\n        DumpSubstituteConfig(myargv[i + 1]);\n    }\n\n    // If SDL_mixer is not initialized, we have to initialize it\n    // and have the responsibility to shut it down later on.\n\n    if (SDLIsInitialized())\n    {\n        music_initialized = true;\n    }\n    else\n    {\n        if (SDL_Init(SDL_INIT_AUDIO) < 0)\n        {\n            fprintf(stderr, \"Unable to set up sound.\\n\");\n        }\n        else if (Mix_OpenAudio(snd_samplerate, AUDIO_S16SYS, 2, 1024) < 0)\n        {\n            fprintf(stderr, \"Error initializing SDL_mixer: %s\\n\",\n                    Mix_GetError());\n            SDL_QuitSubSystem(SDL_INIT_AUDIO);\n        }\n        else\n        {\n            SDL_PauseAudio(0);\n\n            sdl_was_initialized = true;\n            music_initialized = true;\n        }\n    }\n\n    // Once initialization is complete, the temporary Timidity config\n    // file can be removed.\n\n    RemoveTimidityConfig();\n\n    // If snd_musiccmd is set, we need to call Mix_SetMusicCMD to\n    // configure an external music playback program.\n\n    if (strlen(snd_musiccmd) > 0)\n    {\n        Mix_SetMusicCMD(snd_musiccmd);\n    }\n\n    // Register an effect function to track the music position.\n    Mix_RegisterEffect(MIX_CHANNEL_POST, TrackPositionCallback, NULL, NULL);\n\n    // If we're in GENMIDI mode, try to load sound packs.\n    if (snd_musicdevice == SNDDEVICE_GENMIDI)\n    {\n        LoadSubstituteConfigs();\n    }\n\n    return music_initialized;\n}\n\n//\n// SDL_mixer's native MIDI music playing does not pause properly.\n// As a workaround, set the volume to 0 when paused.\n//\n\nstatic void UpdateMusicVolume(void)\n{\n    int vol;\n\n    if (musicpaused)\n    {\n        vol = 0;\n    }\n    else\n    {\n        vol = (current_music_volume * MIX_MAX_VOLUME) / 127;\n    }\n\n    Mix_VolumeMusic(vol);\n}\n\n// Set music volume (0 - 127)\n\nstatic void I_SDL_SetMusicVolume(int volume)\n{\n    // Internal state variable.\n    current_music_volume = volume;\n\n    UpdateMusicVolume();\n}\n\n// Start playing a mid\n\nstatic void I_SDL_PlaySong(void *handle, boolean looping)\n{\n    int loops;\n\n    if (!music_initialized)\n    {\n        return;\n    }\n\n    if (handle == NULL)\n    {\n        return;\n    }\n\n    current_track_music = (Mix_Music *) handle;\n    current_track_loop = looping;\n\n    if (looping)\n    {\n        loops = -1;\n    }\n    else\n    {\n        loops = 1;\n    }\n\n    // Don't loop when playing substitute music, as we do it\n    // ourselves instead.\n    if (playing_substitute && file_metadata.valid)\n    {\n        loops = 1;\n        SDL_LockAudio();\n        current_track_pos = 0;  // start of track\n        SDL_UnlockAudio();\n    }\n\n    Mix_PlayMusic(current_track_music, loops);\n}\n\nstatic void I_SDL_PauseSong(void)\n{\n    if (!music_initialized)\n    {\n        return;\n    }\n\n    musicpaused = true;\n\n    UpdateMusicVolume();\n}\n\nstatic void I_SDL_ResumeSong(void)\n{\n    if (!music_initialized)\n    {\n        return;\n    }\n\n    musicpaused = false;\n\n    UpdateMusicVolume();\n}\n\nstatic void I_SDL_StopSong(void)\n{\n    if (!music_initialized)\n    {\n        return;\n    }\n\n    Mix_HaltMusic();\n    playing_substitute = false;\n    current_track_music = NULL;\n}\n\nstatic void I_SDL_UnRegisterSong(void *handle)\n{\n    Mix_Music *music = (Mix_Music *) handle;\n\n    if (!music_initialized)\n    {\n        return;\n    }\n\n    if (handle == NULL)\n    {\n        return;\n    }\n\n    Mix_FreeMusic(music);\n}\n\n// Determine whether memory block is a .mid file \n\nstatic boolean IsMid(byte *mem, int len)\n{\n    return len > 4 && !memcmp(mem, \"MThd\", 4);\n}\n\nstatic boolean ConvertMus(byte *musdata, int len, char *filename)\n{\n    MEMFILE *instream;\n    MEMFILE *outstream;\n    void *outbuf;\n    size_t outbuf_len;\n    int result;\n\n    instream = mem_fopen_read(musdata, len);\n    outstream = mem_fopen_write();\n\n    result = mus2mid(instream, outstream);\n\n    if (result == 0)\n    {\n        mem_get_buf(outstream, &outbuf, &outbuf_len);\n\n        M_WriteFile(filename, outbuf, outbuf_len);\n    }\n\n    mem_fclose(instream);\n    mem_fclose(outstream);\n\n    return result;\n}\n\nstatic void *I_SDL_RegisterSong(void *data, int len)\n{\n    char *filename;\n    Mix_Music *music;\n\n    if (!music_initialized)\n    {\n        return NULL;\n    }\n\n    playing_substitute = false;\n\n    // See if we're substituting this MUS for a high-quality replacement.\n    filename = GetSubstituteMusicFile(data, len);\n\n    if (filename != NULL)\n    {\n        music = Mix_LoadMUS(filename);\n\n        if (music == NULL)\n        {\n            // Fall through and play MIDI normally, but print an error\n            // message.\n            fprintf(stderr, \"Failed to load substitute music file: %s: %s\\n\",\n                    filename, Mix_GetError());\n        }\n        else\n        {\n            // Read loop point metadata from the file so that we know where\n            // to loop the music.\n            playing_substitute = true;\n            ReadLoopPoints(filename, &file_metadata);\n            return music;\n        }\n    }\n\n    // MUS files begin with \"MUS\"\n    // Reject anything which doesnt have this signature\n\n    filename = M_TempFile(\"doom.mid\");\n\n    if (IsMid(data, len) && len < MAXMIDLENGTH)\n    {\n        M_WriteFile(filename, data, len);\n    }\n    else\n    {\n\t// Assume a MUS file and try to convert\n\n        ConvertMus(data, len, filename);\n    }\n\n    // Load the MIDI. In an ideal world we'd be using Mix_LoadMUS_RW()\n    // by now, but Mix_SetMusicCMD() only works with Mix_LoadMUS(), so\n    // we have to generate a temporary file.\n\n    music = Mix_LoadMUS(filename);\n\n    if (music == NULL)\n    {\n        // Failed to load\n\n        fprintf(stderr, \"Error loading midi: %s\\n\", Mix_GetError());\n    }\n\n    // Remove the temporary MIDI file; however, when using an external\n    // MIDI program we can't delete the file. Otherwise, the program\n    // won't find the file to play. This means we leave a mess on\n    // disk :(\n\n    if (strlen(snd_musiccmd) == 0)\n    {\n        remove(filename);\n    }\n\n    free(filename);\n\n    return music;\n}\n\n// Is the song playing?\nstatic boolean I_SDL_MusicIsPlaying(void)\n{\n    if (!music_initialized)\n    {\n        return false;\n    }\n\n    return Mix_PlayingMusic();\n}\n\n// Get position in substitute music track, in seconds since start of track.\nstatic double GetMusicPosition(void)\n{\n    unsigned int music_pos;\n    int freq;\n\n    Mix_QuerySpec(&freq, NULL, NULL);\n\n    SDL_LockAudio();\n    music_pos = current_track_pos;\n    SDL_UnlockAudio();\n\n    return (double) music_pos / freq;\n}\n\nstatic void RestartCurrentTrack(void)\n{\n    double start = (double) file_metadata.start_time\n                 / file_metadata.samplerate_hz;\n\n    // If the track is playing on loop then reset to the start point.\n    // Otherwise we need to stop the track.\n    if (current_track_loop)\n    {\n        // If the track finished we need to restart it.\n        if (current_track_music != NULL)\n        {\n            Mix_PlayMusic(current_track_music, 1);\n        }\n\n        Mix_SetMusicPosition(start);\n        SDL_LockAudio();\n        current_track_pos = file_metadata.start_time;\n        SDL_UnlockAudio();\n    }\n    else\n    {\n        Mix_HaltMusic();\n        current_track_music = NULL;\n        playing_substitute = false;\n    }\n}\n\n// Poll music position; if we have passed the loop point end position\n// then we need to go back.\nstatic void I_SDL_PollMusic(void)\n{\n    if (playing_substitute && file_metadata.valid)\n    {\n        double end = (double) file_metadata.end_time\n                   / file_metadata.samplerate_hz;\n\n        // If we have reached the loop end point then we have to take action.\n        if (file_metadata.end_time >= 0 && GetMusicPosition() >= end)\n        {\n            RestartCurrentTrack();\n        }\n\n        // Have we reached the actual end of track (not loop end)?\n        if (!Mix_PlayingMusic() && current_track_loop)\n        {\n            RestartCurrentTrack();\n        }\n    }\n}\n\nstatic snddevice_t music_sdl_devices[] =\n{\n    SNDDEVICE_PAS,\n    SNDDEVICE_GUS,\n    SNDDEVICE_WAVEBLASTER,\n    SNDDEVICE_SOUNDCANVAS,\n    SNDDEVICE_GENMIDI,\n    SNDDEVICE_AWE32,\n};\n\nmusic_module_t music_sdl_module =\n{\n    music_sdl_devices,\n    arrlen(music_sdl_devices),\n    I_SDL_InitMusic,\n    I_SDL_ShutdownMusic,\n    I_SDL_SetMusicVolume,\n    I_SDL_PauseSong,\n    I_SDL_ResumeSong,\n    I_SDL_RegisterSong,\n    I_SDL_UnRegisterSong,\n    I_SDL_PlaySong,\n    I_SDL_StopSong,\n    I_SDL_MusicIsPlaying,\n    I_SDL_PollMusic,\n};\n\n"
  },
  {
    "path": "fbdoom/i_sdlsound.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n// Copyright(C) 2008 David Flater\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tSystem interface for sound.\n//\n\n#include \"config.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n#include \"SDL.h\"\n#include \"SDL_mixer.h\"\n\n#ifdef HAVE_LIBSAMPLERATE\n#include <samplerate.h>\n#endif\n\n#include \"deh_str.h\"\n#include \"i_sound.h\"\n#include \"i_system.h\"\n#include \"i_swap.h\"\n#include \"m_argv.h\"\n#include \"m_misc.h\"\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\n#include \"doomtype.h\"\n\n#define LOW_PASS_FILTER\n//#define DEBUG_DUMP_WAVS\n#define NUM_CHANNELS 16\n\ntypedef struct allocated_sound_s allocated_sound_t;\n\nstruct allocated_sound_s\n{\n    sfxinfo_t *sfxinfo;\n    Mix_Chunk chunk;\n    int use_count;\n    allocated_sound_t *prev, *next;\n};\n\nstatic boolean setpanning_workaround = false;\n\nstatic boolean sound_initialized = false;\n\nstatic sfxinfo_t *channels_playing[NUM_CHANNELS];\n\nstatic int mixer_freq;\nstatic Uint16 mixer_format;\nstatic int mixer_channels;\nstatic boolean use_sfx_prefix;\nstatic boolean (*ExpandSoundData)(sfxinfo_t *sfxinfo,\n                                  byte *data,\n                                  int samplerate,\n                                  int length) = NULL;\n\n// Doubly-linked list of allocated sounds.\n// When a sound is played, it is moved to the head, so that the oldest\n// sounds not used recently are at the tail.\n\nstatic allocated_sound_t *allocated_sounds_head = NULL;\nstatic allocated_sound_t *allocated_sounds_tail = NULL;\nstatic int allocated_sounds_size = 0;\n\nint use_libsamplerate = 0;\n\n// Scale factor used when converting libsamplerate floating point numbers\n// to integers. Too high means the sounds can clip; too low means they\n// will be too quiet. This is an amount that should avoid clipping most\n// of the time: with all the Doom IWAD sound effects, at least. If a PWAD\n// is used, clipping might occur.\n\nfloat libsamplerate_scale = 0.65f;\n\n// Hook a sound into the linked list at the head.\n\nstatic void AllocatedSoundLink(allocated_sound_t *snd)\n{\n    snd->prev = NULL;\n\n    snd->next = allocated_sounds_head;\n    allocated_sounds_head = snd;\n\n    if (allocated_sounds_tail == NULL)\n    {\n        allocated_sounds_tail = snd;\n    }\n    else\n    {\n        snd->next->prev = snd;\n    }\n}\n\n// Unlink a sound from the linked list.\n\nstatic void AllocatedSoundUnlink(allocated_sound_t *snd)\n{\n    if (snd->prev == NULL)\n    {\n        allocated_sounds_head = snd->next;\n    }\n    else\n    {\n        snd->prev->next = snd->next;\n    }\n\n    if (snd->next == NULL)\n    {\n        allocated_sounds_tail = snd->prev;\n    }\n    else\n    {\n        snd->next->prev = snd->prev;\n    }\n}\n\nstatic void FreeAllocatedSound(allocated_sound_t *snd)\n{\n    // Unlink from linked list.\n\n    AllocatedSoundUnlink(snd);\n\n    // Unlink from higher-level code.\n\n    snd->sfxinfo->driver_data = NULL;\n\n    // Keep track of the amount of allocated sound data:\n\n    allocated_sounds_size -= snd->chunk.alen;\n\n    free(snd);\n}\n\n// Search from the tail backwards along the allocated sounds list, find\n// and free a sound that is not in use, to free up memory.  Return true\n// for success.\n\nstatic boolean FindAndFreeSound(void)\n{\n    allocated_sound_t *snd;\n\n    snd = allocated_sounds_tail;\n\n    while (snd != NULL)\n    {\n        if (snd->use_count == 0)\n        {\n            FreeAllocatedSound(snd);\n            return true;\n        }\n\n        snd = snd->prev;\n    }\n\n    // No available sounds to free...\n\n    return false;\n}\n\n// Enforce SFX cache size limit.  We are just about to allocate \"len\"\n// bytes on the heap for a new sound effect, so free up some space\n// so that we keep allocated_sounds_size < snd_cachesize\n\nstatic void ReserveCacheSpace(size_t len)\n{\n    if (snd_cachesize <= 0)\n    {\n        return;\n    }\n\n    // Keep freeing sound effects that aren't currently being played,\n    // until there is enough space for the new sound.\n\n    while (allocated_sounds_size + len > snd_cachesize)\n    {\n        // Free a sound.  If there is nothing more to free, stop.\n\n        if (!FindAndFreeSound())\n        {\n            break;\n        }\n    }\n}\n\n// Allocate a block for a new sound effect.\n\nstatic Mix_Chunk *AllocateSound(sfxinfo_t *sfxinfo, size_t len)\n{\n    allocated_sound_t *snd;\n\n    // Keep allocated sounds within the cache size.\n\n    ReserveCacheSpace(len);\n\n    // Allocate the sound structure and data.  The data will immediately\n    // follow the structure, which acts as a header.\n\n    do\n    {\n        snd = malloc(sizeof(allocated_sound_t) + len);\n\n        // Out of memory?  Try to free an old sound, then loop round\n        // and try again.\n\n        if (snd == NULL && !FindAndFreeSound())\n        {\n            return NULL;\n        }\n\n    } while (snd == NULL);\n\n    // Skip past the chunk structure for the audio buffer\n\n    snd->chunk.abuf = (byte *) (snd + 1);\n    snd->chunk.alen = len;\n    snd->chunk.allocated = 1;\n    snd->chunk.volume = MIX_MAX_VOLUME;\n\n    snd->sfxinfo = sfxinfo;\n    snd->use_count = 0;\n\n    // driver_data pointer points to the allocated_sound structure.\n\n    sfxinfo->driver_data = snd;\n\n    // Keep track of how much memory all these cached sounds are using...\n\n    allocated_sounds_size += len;\n\n    AllocatedSoundLink(snd);\n\n    return &snd->chunk;\n}\n\n// Lock a sound, to indicate that it may not be freed.\n\nstatic void LockAllocatedSound(allocated_sound_t *snd)\n{\n    // Increase use count, to stop the sound being freed.\n\n    ++snd->use_count;\n\n    //printf(\"++ %s: Use count=%i\\n\", snd->sfxinfo->name, snd->use_count);\n\n    // When we use a sound, re-link it into the list at the head, so\n    // that the oldest sounds fall to the end of the list for freeing.\n\n    AllocatedSoundUnlink(snd);\n    AllocatedSoundLink(snd);\n}\n\n// Unlock a sound to indicate that it may now be freed.\n\nstatic void UnlockAllocatedSound(allocated_sound_t *snd)\n{\n    if (snd->use_count <= 0)\n    {\n        I_Error(\"Sound effect released more times than it was locked...\");\n    }\n\n    --snd->use_count;\n\n    //printf(\"-- %s: Use count=%i\\n\", snd->sfxinfo->name, snd->use_count);\n}\n\n// When a sound stops, check if it is still playing.  If it is not, \n// we can mark the sound data as CACHE to be freed back for other\n// means.\n\nstatic void ReleaseSoundOnChannel(int channel)\n{\n    sfxinfo_t *sfxinfo = channels_playing[channel];\n\n    if (sfxinfo == NULL)\n    {\n        return;\n    }\n\n    channels_playing[channel] = NULL;\n\n    UnlockAllocatedSound(sfxinfo->driver_data);\n}\n\n#ifdef HAVE_LIBSAMPLERATE\n\n// Returns the conversion mode for libsamplerate to use.\n\nstatic int SRC_ConversionMode(void)\n{\n    switch (use_libsamplerate)\n    {\n        // 0 = disabled\n\n        default:\n        case 0:\n            return -1;\n\n        // Ascending numbers give higher quality\n\n        case 1:\n            return SRC_LINEAR;\n        case 2:\n            return SRC_ZERO_ORDER_HOLD;\n        case 3:\n            return SRC_SINC_FASTEST;\n        case 4:\n            return SRC_SINC_MEDIUM_QUALITY;\n        case 5:\n            return SRC_SINC_BEST_QUALITY;\n    }\n}\n\n// libsamplerate-based generic sound expansion function for any sample rate\n//   unsigned 8 bits --> signed 16 bits\n//   mono --> stereo\n//   samplerate --> mixer_freq\n// Returns number of clipped samples.\n// DWF 2008-02-10 with cleanups by Simon Howard.\n\nstatic boolean ExpandSoundData_SRC(sfxinfo_t *sfxinfo,\n                                   byte *data,\n                                   int samplerate,\n                                   int length)\n{\n    SRC_DATA src_data;\n    uint32_t i, abuf_index=0, clipped=0;\n    uint32_t alen;\n    int retn;\n    int16_t *expanded;\n    Mix_Chunk *chunk;\n\n    src_data.input_frames = length;\n    src_data.data_in = malloc(length * sizeof(float));\n    src_data.src_ratio = (double)mixer_freq / samplerate;\n\n    // We include some extra space here in case of rounding-up.\n    src_data.output_frames = src_data.src_ratio * length + (mixer_freq / 4);\n    src_data.data_out = malloc(src_data.output_frames * sizeof(float));\n\n    assert(src_data.data_in != NULL && src_data.data_out != NULL);\n\n    // Convert input data to floats\n\n    for (i=0; i<length; ++i)\n    {\n        // Unclear whether 128 should be interpreted as \"zero\" or whether a\n        // symmetrical range should be assumed.  The following assumes a\n        // symmetrical range.\n        src_data.data_in[i] = data[i] / 127.5 - 1;\n    }\n\n    // Do the sound conversion\n\n    retn = src_simple(&src_data, SRC_ConversionMode(), 1);\n    assert(retn == 0);\n\n    // Allocate the new chunk.\n\n    alen = src_data.output_frames_gen * 4;\n\n    chunk = AllocateSound(sfxinfo, src_data.output_frames_gen * 4);\n\n    if (chunk == NULL)\n    {\n        return false;\n    }\n\n    expanded = (int16_t *) chunk->abuf;\n\n    // Convert the result back into 16-bit integers.\n\n    for (i=0; i<src_data.output_frames_gen; ++i)\n    {\n        // libsamplerate does not limit itself to the -1.0 .. 1.0 range on\n        // output, so a multiplier less than INT16_MAX (32767) is required\n        // to avoid overflows or clipping.  However, the smaller the\n        // multiplier, the quieter the sound effects get, and the more you\n        // have to turn down the music to keep it in balance.\n\n        // 22265 is the largest multiplier that can be used to resample all\n        // of the Vanilla DOOM sound effects to 48 kHz without clipping\n        // using SRC_SINC_BEST_QUALITY.  It is close enough (only slightly\n        // too conservative) for SRC_SINC_MEDIUM_QUALITY and\n        // SRC_SINC_FASTEST.  PWADs with interestingly different sound\n        // effects or target rates other than 48 kHz might still result in\n        // clipping--I don't know if there's a limit to it.\n\n        // As the number of clipped samples increases, the signal is\n        // gradually overtaken by noise, with the loudest parts going first.\n        // However, a moderate amount of clipping is often tolerated in the\n        // quest for the loudest possible sound overall.  The results of\n        // using INT16_MAX as the multiplier are not all that bad, but\n        // artifacts are noticeable during the loudest parts.\n\n        float cvtval_f =\n            src_data.data_out[i] * libsamplerate_scale * INT16_MAX;\n        int32_t cvtval_i = cvtval_f + (cvtval_f < 0 ? -0.5 : 0.5);\n\n        // Asymmetrical sound worries me, so we won't use -32768.\n        if (cvtval_i < -INT16_MAX)\n        {\n            cvtval_i = -INT16_MAX;\n            ++clipped;\n        }\n        else if (cvtval_i > INT16_MAX)\n        {\n            cvtval_i = INT16_MAX;\n            ++clipped;\n        }\n\n        // Left and right channels\n\n        expanded[abuf_index++] = cvtval_i;\n        expanded[abuf_index++] = cvtval_i;\n    }\n\n    free(src_data.data_in);\n    free(src_data.data_out);\n\n    if (clipped > 0)\n    {\n        fprintf(stderr, \"Sound '%s': clipped %u samples (%0.2f %%)\\n\", \n                        sfxinfo->name, clipped,\n                        400.0 * clipped / chunk->alen);\n    }\n\n    return true;\n}\n\n#endif\n\nstatic boolean ConvertibleRatio(int freq1, int freq2)\n{\n    int ratio;\n\n    if (freq1 > freq2)\n    {\n        return ConvertibleRatio(freq2, freq1);\n    }\n    else if ((freq2 % freq1) != 0)\n    {\n        // Not in a direct ratio\n\n        return false;\n    }\n    else\n    {\n        // Check the ratio is a power of 2\n\n        ratio = freq2 / freq1;\n\n        while ((ratio & 1) == 0)\n        {\n            ratio = ratio >> 1;\n        }\n\n        return ratio == 1;\n    }\n}\n\n#ifdef DEBUG_DUMP_WAVS\n\n// Debug code to dump resampled sound effects to WAV files for analysis.\n\nstatic void WriteWAV(char *filename, byte *data,\n                     uint32_t length, int samplerate)\n{\n    FILE *wav;\n    unsigned int i;\n    unsigned short s;\n\n    wav = fopen(filename, \"wb\");\n\n    // Header\n\n    fwrite(\"RIFF\", 1, 4, wav);\n    i = LONG(36 + samplerate);\n    fwrite(&i, 4, 1, wav);\n    fwrite(\"WAVE\", 1, 4, wav);\n\n    // Subchunk 1\n\n    fwrite(\"fmt \", 1, 4, wav);\n    i = LONG(16);\n    fwrite(&i, 4, 1, wav);           // Length\n    s = SHORT(1);\n    fwrite(&s, 2, 1, wav);           // Format (PCM)\n    s = SHORT(2);\n    fwrite(&s, 2, 1, wav);           // Channels (2=stereo)\n    i = LONG(samplerate);\n    fwrite(&i, 4, 1, wav);           // Sample rate\n    i = LONG(samplerate * 2 * 2);\n    fwrite(&i, 4, 1, wav);           // Byte rate (samplerate * stereo * 16 bit)\n    s = SHORT(2 * 2);\n    fwrite(&s, 2, 1, wav);           // Block align (stereo * 16 bit)\n    s = SHORT(16);\n    fwrite(&s, 2, 1, wav);           // Bits per sample (16 bit)\n\n    // Data subchunk\n\n    fwrite(\"data\", 1, 4, wav);\n    i = LONG(length);\n    fwrite(&i, 4, 1, wav);           // Data length\n    fwrite(data, 1, length, wav);    // Data\n\n    fclose(wav);\n}\n\n#endif\n\n// Generic sound expansion function for any sample rate.\n// Returns number of clipped samples (always 0).\n\nstatic boolean ExpandSoundData_SDL(sfxinfo_t *sfxinfo,\n                                   byte *data,\n                                   int samplerate,\n                                   int length)\n{\n    SDL_AudioCVT convertor;\n    Mix_Chunk *chunk;\n    uint32_t expanded_length;\n \n    // Calculate the length of the expanded version of the sample.    \n\n    expanded_length = (uint32_t) ((((uint64_t) length) * mixer_freq) / samplerate);\n\n    // Double up twice: 8 -> 16 bit and mono -> stereo\n\n    expanded_length *= 4;\n\n    // Allocate a chunk in which to expand the sound\n\n    chunk = AllocateSound(sfxinfo, expanded_length);\n\n    if (chunk == NULL)\n    {\n        return false;\n    }\n\n    // If we can, use the standard / optimized SDL conversion routines.\n\n    if (samplerate <= mixer_freq\n     && ConvertibleRatio(samplerate, mixer_freq)\n     && SDL_BuildAudioCVT(&convertor,\n                          AUDIO_U8, 1, samplerate,\n                          mixer_format, mixer_channels, mixer_freq))\n    {\n        convertor.buf = chunk->abuf;\n        convertor.len = length;\n        memcpy(convertor.buf, data, length);\n\n        SDL_ConvertAudio(&convertor);\n    }\n    else\n    {\n        Sint16 *expanded = (Sint16 *) chunk->abuf;\n        int expanded_length;\n        int expand_ratio;\n        int i;\n\n        // Generic expansion if conversion does not work:\n        //\n        // SDL's audio conversion only works for rate conversions that are\n        // powers of 2; if the two formats are not in a direct power of 2\n        // ratio, do this naive conversion instead.\n\n        // number of samples in the converted sound\n\n        expanded_length = ((uint64_t) length * mixer_freq) / samplerate;\n        expand_ratio = (length << 8) / expanded_length;\n\n        for (i=0; i<expanded_length; ++i)\n        {\n            Sint16 sample;\n            int src;\n\n            src = (i * expand_ratio) >> 8;\n\n            sample = data[src] | (data[src] << 8);\n            sample -= 32768;\n\n            // expand 8->16 bits, mono->stereo\n\n            expanded[i * 2] = expanded[i * 2 + 1] = sample;\n        }\n\n#ifdef LOW_PASS_FILTER\n        // Perform a low-pass filter on the upscaled sound to filter\n        // out high-frequency noise from the conversion process.\n\n        {\n            float rc, dt, alpha;\n\n            // Low-pass filter for cutoff frequency f:\n            //\n            // For sampling rate r, dt = 1 / r\n            // rc = 1 / 2*pi*f\n            // alpha = dt / (rc + dt)\n\n            // Filter to the half sample rate of the original sound effect\n            // (maximum frequency, by nyquist)\n\n            dt = 1.0f / mixer_freq;\n            rc = 1.0f / (3.14f * samplerate);\n            alpha = dt / (rc + dt);\n\n            // Both channels are processed in parallel, hence [i-2]:\n\n            for (i=2; i<expanded_length * 2; ++i)\n            {\n                expanded[i] = (Sint16) (alpha * expanded[i]\n                                      + (1 - alpha) * expanded[i-2]);\n            }\n        }\n#endif /* #ifdef LOW_PASS_FILTER */\n    }\n\n    return true;\n}\n\n// Load and convert a sound effect\n// Returns true if successful\n\nstatic boolean CacheSFX(sfxinfo_t *sfxinfo)\n{\n    int lumpnum;\n    unsigned int lumplen;\n    int samplerate;\n    unsigned int length;\n    byte *data;\n\n    // need to load the sound\n\n    lumpnum = sfxinfo->lumpnum;\n    data = W_CacheLumpNum(lumpnum, PU_STATIC);\n    lumplen = W_LumpLength(lumpnum);\n\n    // Check the header, and ensure this is a valid sound\n\n    if (lumplen < 8\n     || data[0] != 0x03 || data[1] != 0x00)\n    {\n        // Invalid sound\n\n        return false;\n    }\n\n    // 16 bit sample rate field, 32 bit length field\n\n    samplerate = (data[3] << 8) | data[2];\n    length = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4];\n\n    // If the header specifies that the length of the sound is greater than\n    // the length of the lump itself, this is an invalid sound lump\n\n    // We also discard sound lumps that are less than 49 samples long,\n    // as this is how DMX behaves - although the actual cut-off length\n    // seems to vary slightly depending on the sample rate.  This needs\n    // further investigation to better understand the correct\n    // behavior.\n\n    if (length > lumplen - 8 || length <= 48)\n    {\n        return false;\n    }\n\n    // The DMX sound library seems to skip the first 16 and last 16\n    // bytes of the lump - reason unknown.\n\n    data += 16;\n    length -= 32;\n\n    // Sample rate conversion\n\n    if (!ExpandSoundData(sfxinfo, data + 8, samplerate, length))\n    {\n        return false;\n    }\n\n#ifdef DEBUG_DUMP_WAVS\n    {\n        char filename[16];\n\n        M_snprintf(filename, sizeof(filename), \"%s.wav\",\n                   DEH_String(S_sfx[sound].name));\n        WriteWAV(filename, sound_chunks[sound].abuf,\n                 sound_chunks[sound].alen, mixer_freq);\n    }\n#endif\n\n    // don't need the original lump any more\n  \n    W_ReleaseLumpNum(lumpnum);\n\n    return true;\n}\n\nstatic void GetSfxLumpName(sfxinfo_t *sfx, char *buf, size_t buf_len)\n{\n    // Linked sfx lumps? Get the lump number for the sound linked to.\n\n    if (sfx->link != NULL)\n    {\n        sfx = sfx->link;\n    }\n\n    // Doom adds a DS* prefix to sound lumps; Heretic and Hexen don't\n    // do this.\n\n    if (use_sfx_prefix)\n    {\n        M_snprintf(buf, buf_len, \"ds%s\", DEH_String(sfx->name));\n    }\n    else\n    {\n        M_StringCopy(buf, DEH_String(sfx->name), buf_len);\n    }\n}\n\n#ifdef HAVE_LIBSAMPLERATE\n\n// Preload all the sound effects - stops nasty ingame freezes\n\nstatic void I_SDL_PrecacheSounds(sfxinfo_t *sounds, int num_sounds)\n{\n    char namebuf[9];\n    int i;\n\n    // Don't need to precache the sounds unless we are using libsamplerate.\n\n    if (use_libsamplerate == 0)\n    {\n\treturn;\n    }\n\n    printf(\"I_SDL_PrecacheSounds: Precaching all sound effects..\");\n\n    for (i=0; i<num_sounds; ++i)\n    {\n        if ((i % 6) == 0)\n        {\n            printf(\".\");\n            fflush(stdout);\n        }\n\n        GetSfxLumpName(&sounds[i], namebuf, sizeof(namebuf));\n\n        sounds[i].lumpnum = W_CheckNumForName(namebuf);\n\n        if (sounds[i].lumpnum != -1)\n        {\n            CacheSFX(&sounds[i]);\n        }\n    }\n\n    printf(\"\\n\");\n}\n\n#else\n\nstatic void I_SDL_PrecacheSounds(sfxinfo_t *sounds, int num_sounds)\n{\n    // no-op\n}\n\n#endif\n\n// Load a SFX chunk into memory and ensure that it is locked.\n\nstatic boolean LockSound(sfxinfo_t *sfxinfo)\n{\n    // If the sound isn't loaded, load it now\n\n    if (sfxinfo->driver_data == NULL)\n    {\n        if (!CacheSFX(sfxinfo))\n        {\n            return false;\n        }\n    }\n\n    LockAllocatedSound(sfxinfo->driver_data);\n\n    return true;\n}\n\n//\n// Retrieve the raw data lump index\n//  for a given SFX name.\n//\n\nstatic int I_SDL_GetSfxLumpNum(sfxinfo_t *sfx)\n{\n    char namebuf[9];\n\n    GetSfxLumpName(sfx, namebuf, sizeof(namebuf));\n\n    return W_GetNumForName(namebuf);\n}\n\nstatic void I_SDL_UpdateSoundParams(int handle, int vol, int sep)\n{\n    int left, right;\n\n    if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS)\n    {\n        return;\n    }\n\n    left = ((254 - sep) * vol) / 127;\n    right = ((sep) * vol) / 127;\n\n    if (left < 0) left = 0;\n    else if ( left > 255) left = 255;\n    if (right < 0) right = 0;\n    else if (right > 255) right = 255;\n\n    // SDL_mixer version 1.2.8 and earlier has a bug in the Mix_SetPanning\n    // function.  A workaround is to call Mix_UnregisterAllEffects for\n    // the channel before calling it.  This is undesirable as it may lead\n    // to the channel volumes resetting briefly.\n\n    if (setpanning_workaround)\n    {\n        Mix_UnregisterAllEffects(handle);\n    }\n\n    Mix_SetPanning(handle, left, right);\n}\n\n//\n// Starting a sound means adding it\n//  to the current list of active sounds\n//  in the internal channels.\n// As the SFX info struct contains\n//  e.g. a pointer to the raw data,\n//  it is ignored.\n// As our sound handling does not handle\n//  priority, it is ignored.\n// Pitching (that is, increased speed of playback)\n//  is set, but currently not used by mixing.\n//\n\nstatic int I_SDL_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep)\n{\n    allocated_sound_t *snd;\n\n    if (!sound_initialized || channel < 0 || channel >= NUM_CHANNELS)\n    {\n        return -1;\n    }\n\n    // Release a sound effect if there is already one playing\n    // on this channel\n\n    ReleaseSoundOnChannel(channel);\n\n    // Get the sound data\n\n    if (!LockSound(sfxinfo))\n    {\n\treturn -1;\n    }\n\n    snd = sfxinfo->driver_data;\n\n    // play sound\n\n    Mix_PlayChannelTimed(channel, &snd->chunk, 0, -1);\n\n    channels_playing[channel] = sfxinfo;\n\n    // set separation, etc.\n \n    I_SDL_UpdateSoundParams(channel, vol, sep);\n\n    return channel;\n}\n\nstatic void I_SDL_StopSound(int handle)\n{\n    if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS)\n    {\n        return;\n    }\n\n    Mix_HaltChannel(handle);\n\n    // Sound data is no longer needed; release the\n    // sound data being used for this channel\n\n    ReleaseSoundOnChannel(handle);\n}\n\n\nstatic boolean I_SDL_SoundIsPlaying(int handle)\n{\n    if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS)\n    {\n        return false;\n    }\n\n    return Mix_Playing(handle);\n}\n\n// \n// Periodically called to update the sound system\n//\n\nstatic void I_SDL_UpdateSound(void)\n{\n    int i;\n\n    // Check all channels to see if a sound has finished\n\n    for (i=0; i<NUM_CHANNELS; ++i)\n    {\n        if (channels_playing[i] && !I_SDL_SoundIsPlaying(i))\n        {\n            // Sound has finished playing on this channel,\n            // but sound data has not been released to cache\n            \n            ReleaseSoundOnChannel(i);\n        }\n    }\n}\n\nstatic void I_SDL_ShutdownSound(void)\n{    \n    if (!sound_initialized)\n    {\n        return;\n    }\n\n    Mix_CloseAudio();\n    SDL_QuitSubSystem(SDL_INIT_AUDIO);\n\n    sound_initialized = false;\n}\n\n// Calculate slice size, based on snd_maxslicetime_ms.\n// The result must be a power of two.\n\nstatic int GetSliceSize(void)\n{\n    int limit;\n    int n;\n\n    limit = (snd_samplerate * snd_maxslicetime_ms) / 1000;\n\n    // Try all powers of two, not exceeding the limit.\n\n    for (n=0;; ++n)\n    {\n        // 2^n <= limit < 2^n+1 ?\n\n        if ((1 << (n + 1)) > limit)\n        {\n            return (1 << n);\n        }\n    }\n\n    // Should never happen?\n\n    return 1024;\n}\n\nstatic boolean I_SDL_InitSound(boolean _use_sfx_prefix)\n{\n    int i;\n\n    use_sfx_prefix = _use_sfx_prefix;\n\n    // No sounds yet\n\n    for (i=0; i<NUM_CHANNELS; ++i)\n    {\n        channels_playing[i] = NULL;\n    }\n\n    if (SDL_Init(SDL_INIT_AUDIO) < 0)\n    {\n        fprintf(stderr, \"Unable to set up sound.\\n\");\n        return false;\n    }\n\n    if (Mix_OpenAudio(snd_samplerate, AUDIO_S16SYS, 2, GetSliceSize()) < 0)\n    {\n        fprintf(stderr, \"Error initialising SDL_mixer: %s\\n\", Mix_GetError());\n        return false;\n    }\n\n    ExpandSoundData = ExpandSoundData_SDL;\n\n    Mix_QuerySpec(&mixer_freq, &mixer_format, &mixer_channels);\n\n#ifdef HAVE_LIBSAMPLERATE\n    if (use_libsamplerate != 0)\n    {\n        if (SRC_ConversionMode() < 0)\n        {\n            I_Error(\"I_SDL_InitSound: Invalid value for use_libsamplerate: %i\",\n                    use_libsamplerate);\n        }\n\n        ExpandSoundData = ExpandSoundData_SRC;\n    }\n#else\n    if (use_libsamplerate != 0)\n    {\n        fprintf(stderr, \"I_SDL_InitSound: use_libsamplerate=%i, but \"\n                        \"libsamplerate support not compiled in.\\n\",\n                        use_libsamplerate);\n    }\n#endif\n\n    // SDL_mixer version 1.2.8 and earlier has a bug in the Mix_SetPanning\n    // function that can cause the game to lock up.  If we're using an old\n    // version, we need to apply a workaround.  But the workaround has its\n    // own drawbacks ...\n\n    {\n        const SDL_version *mixer_version;\n        int v;\n\n        mixer_version = Mix_Linked_Version();\n        v = SDL_VERSIONNUM(mixer_version->major,\n                           mixer_version->minor,\n                           mixer_version->patch);\n\n        if (v <= SDL_VERSIONNUM(1, 2, 8))\n        {\n            setpanning_workaround = true;\n            fprintf(stderr, \"\\n\"\n              \"ATTENTION: You are using an old version of SDL_mixer!\\n\"\n              \"           This version has a bug that may cause \"\n                          \"your sound to stutter.\\n\"\n              \"           Please upgrade to a newer version!\\n\"\n              \"\\n\");\n        }\n    }\n\n    Mix_AllocateChannels(NUM_CHANNELS);\n\n    SDL_PauseAudio(0);\n\n    sound_initialized = true;\n\n    return true;\n}\n\nstatic snddevice_t sound_sdl_devices[] = \n{\n    SNDDEVICE_SB,\n    SNDDEVICE_PAS,\n    SNDDEVICE_GUS,\n    SNDDEVICE_WAVEBLASTER,\n    SNDDEVICE_SOUNDCANVAS,\n    SNDDEVICE_AWE32,\n};\n\nsound_module_t sound_sdl_module = \n{\n    sound_sdl_devices,\n    arrlen(sound_sdl_devices),\n    I_SDL_InitSound,\n    I_SDL_ShutdownSound,\n    I_SDL_GetSfxLumpNum,\n    I_SDL_UpdateSound,\n    I_SDL_UpdateSoundParams,\n    I_SDL_StartSound,\n    I_SDL_StopSound,\n    I_SDL_SoundIsPlaying,\n    I_SDL_PrecacheSounds,\n};\n\n"
  },
  {
    "path": "fbdoom/i_sound.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:  none\n//\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#ifdef ORIGCODE\n#include \"SDL_mixer.h\"\n#endif\n\n#include \"config.h\"\n#include \"doomfeatures.h\"\n#include \"doomtype.h\"\n\n#ifdef ORIGCODE\n#include \"gusconf.h\"\n#endif\n#include \"i_sound.h\"\n#include \"i_video.h\"\n#include \"m_argv.h\"\n#include \"m_config.h\"\n\n// Sound sample rate to use for digital output (Hz)\n\nint snd_samplerate = 44100;\n\n// Maximum number of bytes to dedicate to allocated sound effects.\n// (Default: 64MB)\n\nint snd_cachesize = 64 * 1024 * 1024;\n\n// Config variable that controls the sound buffer size.\n// We default to 28ms (1000 / 35fps = 1 buffer per tic).\n\nint snd_maxslicetime_ms = 28;\n\n// External command to invoke to play back music.\n\nchar *snd_musiccmd = \"\";\n\n// Low-level sound and music modules we are using\n\nstatic sound_module_t *sound_module;\nstatic music_module_t *music_module;\n\nint snd_musicdevice = SNDDEVICE_SB;\nint snd_sfxdevice = SNDDEVICE_SB;\n\n// Sound modules\n\nextern void I_InitTimidityConfig(void);\nextern sound_module_t sound_sdl_module;\nextern sound_module_t sound_pcsound_module;\nextern music_module_t music_sdl_module;\nextern music_module_t music_opl_module;\n\n// For OPL module:\n\nextern int opl_io_port;\n\n// For native music module:\n\nextern char *timidity_cfg_path;\n\n// DOS-specific options: These are unused but should be maintained\n// so that the config file can be shared between chocolate\n// doom and doom.exe\n\n#if ORIGCODE\nstatic int snd_sbport = 0;\nstatic int snd_sbirq = 0;\nstatic int snd_sbdma = 0;\nstatic int snd_mport = 0;\n#endif\n\n// Compiled-in sound modules:\n\nstatic sound_module_t *sound_modules[] = \n{\n#ifdef FEATURE_SOUND\n    &sound_sdl_module,\n    &sound_pcsound_module,\n#endif\n    NULL,\n};\n\n// Compiled-in music modules:\n\nstatic music_module_t *music_modules[] =\n{\n#ifdef FEATURE_SOUND\n    &music_sdl_module,\n    &music_opl_module,\n#endif\n    NULL,\n};\n\n// Check if a sound device is in the given list of devices\n\nstatic boolean SndDeviceInList(snddevice_t device, snddevice_t *list,\n                               int len)\n{\n    int i;\n\n    for (i=0; i<len; ++i)\n    {\n        if (device == list[i])\n        {\n            return true;\n        }\n    }\n\n    return false;\n}\n\n// Find and initialize a sound_module_t appropriate for the setting\n// in snd_sfxdevice.\n\nstatic void InitSfxModule(boolean use_sfx_prefix)\n{\n    int i;\n\n    sound_module = NULL;\n\n    for (i=0; sound_modules[i] != NULL; ++i)\n    {\n        // Is the sfx device in the list of devices supported by\n        // this module?\n\n        if (SndDeviceInList(snd_sfxdevice, \n                            sound_modules[i]->sound_devices,\n                            sound_modules[i]->num_sound_devices))\n        {\n            // Initialize the module\n\n            if (sound_modules[i]->Init(use_sfx_prefix))\n            {\n                sound_module = sound_modules[i];\n                return;\n            }\n        }\n    }\n}\n\n// Initialize music according to snd_musicdevice.\n\nstatic void InitMusicModule(void)\n{\n    int i;\n\n    music_module = NULL;\n\n    for (i=0; music_modules[i] != NULL; ++i)\n    {\n        // Is the music device in the list of devices supported\n        // by this module?\n\n        if (SndDeviceInList(snd_musicdevice, \n                            music_modules[i]->sound_devices,\n                            music_modules[i]->num_sound_devices))\n        {\n            // Initialize the module\n\n            if (music_modules[i]->Init())\n            {\n                music_module = music_modules[i];\n                return;\n            }\n        }\n    }\n}\n\n//\n// Initializes sound stuff, including volume\n// Sets channels, SFX and music volume,\n//  allocates channel buffer, sets S_sfx lookup.\n//\n\nvoid I_InitSound(boolean use_sfx_prefix)\n{  \n    boolean nosound, nosfx, nomusic;\n\n    //!\n    // @vanilla\n    //\n    // Disable all sound output.\n    //\n\n    nosound = M_CheckParm(\"-nosound\") > 0;\n\n    //!\n    // @vanilla\n    //\n    // Disable sound effects. \n    //\n\n    nosfx = M_CheckParm(\"-nosfx\") > 0;\n\n    //!\n    // @vanilla\n    //\n    // Disable music.\n    //\n\n    nomusic = M_CheckParm(\"-nomusic\") > 0;\n\n    // Initialize the sound and music subsystems.\n\n    if (!nosound && !screensaver_mode)\n    {\n        // This is kind of a hack. If native MIDI is enabled, set up\n        // the TIMIDITY_CFG environment variable here before SDL_mixer\n        // is opened.\n\n        if (!nomusic\n         && (snd_musicdevice == SNDDEVICE_GENMIDI\n          || snd_musicdevice == SNDDEVICE_GUS))\n        {\n            I_InitTimidityConfig();\n        }\n\n        if (!nosfx)\n        {\n            InitSfxModule(use_sfx_prefix);\n        }\n\n        if (!nomusic)\n        {\n            InitMusicModule();\n        }\n    }\n}\n\nvoid I_ShutdownSound(void)\n{\n    if (sound_module != NULL)\n    {\n        sound_module->Shutdown();\n    }\n\n    if (music_module != NULL)\n    {\n        music_module->Shutdown();\n    }\n}\n\nint I_GetSfxLumpNum(sfxinfo_t *sfxinfo)\n{\n    if (sound_module != NULL) \n    {\n        return sound_module->GetSfxLumpNum(sfxinfo);\n    }\n    else\n    {\n        return 0;\n    }\n}\n\nvoid I_UpdateSound(void)\n{\n    if (sound_module != NULL)\n    {\n        sound_module->Update();\n    }\n\n    if (music_module != NULL && music_module->Poll != NULL)\n    {\n        music_module->Poll();\n    }\n}\n\nstatic void CheckVolumeSeparation(int *vol, int *sep)\n{\n    if (*sep < 0)\n    {\n        *sep = 0;\n    }\n    else if (*sep > 254)\n    {\n        *sep = 254;\n    }\n\n    if (*vol < 0)\n    {\n        *vol = 0;\n    }\n    else if (*vol > 127)\n    {\n        *vol = 127;\n    }\n}\n\nvoid I_UpdateSoundParams(int channel, int vol, int sep)\n{\n    if (sound_module != NULL)\n    {\n        CheckVolumeSeparation(&vol, &sep);\n        sound_module->UpdateSoundParams(channel, vol, sep);\n    }\n}\n\nint I_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep)\n{\n    if (sound_module != NULL)\n    {\n        CheckVolumeSeparation(&vol, &sep);\n        return sound_module->StartSound(sfxinfo, channel, vol, sep);\n    }\n    else\n    {\n        return 0;\n    }\n}\n\nvoid I_StopSound(int channel)\n{\n    if (sound_module != NULL)\n    {\n        sound_module->StopSound(channel);\n    }\n}\n\nboolean I_SoundIsPlaying(int channel)\n{\n    if (sound_module != NULL)\n    {\n        return sound_module->SoundIsPlaying(channel);\n    }\n    else\n    {\n        return false;\n    }\n}\n\nvoid I_PrecacheSounds(sfxinfo_t *sounds, int num_sounds)\n{\n    if (sound_module != NULL && sound_module->CacheSounds != NULL)\n    {\n\tsound_module->CacheSounds(sounds, num_sounds);\n    }\n}\n\nvoid I_InitMusic(void)\n{\n}\n\nvoid I_ShutdownMusic(void)\n{\n\n}\n\nvoid I_SetMusicVolume(int volume)\n{\n    if (music_module != NULL)\n    {\n        music_module->SetMusicVolume(volume);\n    }\n}\n\nvoid I_PauseSong(void)\n{\n    if (music_module != NULL)\n    {\n        music_module->PauseMusic();\n    }\n}\n\nvoid I_ResumeSong(void)\n{\n    if (music_module != NULL)\n    {\n        music_module->ResumeMusic();\n    }\n}\n\nvoid *I_RegisterSong(void *data, int len)\n{\n    if (music_module != NULL)\n    {\n        return music_module->RegisterSong(data, len);\n    }\n    else\n    {\n        return NULL;\n    }\n}\n\nvoid I_UnRegisterSong(void *handle)\n{\n    if (music_module != NULL)\n    {\n        music_module->UnRegisterSong(handle);\n    }\n}\n\nvoid I_PlaySong(void *handle, boolean looping)\n{\n    if (music_module != NULL)\n    {\n        music_module->PlaySong(handle, looping);\n    }\n}\n\nvoid I_StopSong(void)\n{\n    if (music_module != NULL)\n    {\n        music_module->StopSong();\n    }\n}\n\nboolean I_MusicIsPlaying(void)\n{\n    if (music_module != NULL)\n    {\n        return music_module->MusicIsPlaying();\n    }\n    else\n    {\n        return false;\n    }\n}\n\nvoid I_BindSoundVariables(void)\n{\n#ifdef ORIGCODE\n    extern int use_libsamplerate;\n    extern float libsamplerate_scale;\n\n    M_BindVariable(\"snd_musicdevice\",   &snd_musicdevice);\n    M_BindVariable(\"snd_sfxdevice\",     &snd_sfxdevice);\n    M_BindVariable(\"snd_sbport\",        &snd_sbport);\n    M_BindVariable(\"snd_sbirq\",         &snd_sbirq);\n    M_BindVariable(\"snd_sbdma\",         &snd_sbdma);\n    M_BindVariable(\"snd_mport\",         &snd_mport);\n    M_BindVariable(\"snd_maxslicetime_ms\", &snd_maxslicetime_ms);\n    M_BindVariable(\"snd_musiccmd\",      &snd_musiccmd);\n    M_BindVariable(\"snd_samplerate\",    &snd_samplerate);\n    M_BindVariable(\"snd_cachesize\",     &snd_cachesize);\n    M_BindVariable(\"opl_io_port\",       &opl_io_port);\n\n    M_BindVariable(\"timidity_cfg_path\", &timidity_cfg_path);\n    M_BindVariable(\"gus_patch_path\",    &gus_patch_path);\n    M_BindVariable(\"gus_ram_kb\",        &gus_ram_kb);\n\n#ifdef FEATURE_SOUND\n    M_BindVariable(\"use_libsamplerate\",   &use_libsamplerate);\n    M_BindVariable(\"libsamplerate_scale\", &libsamplerate_scale);\n#endif\n\n    // Before SDL_mixer version 1.2.11, MIDI music caused the game\n    // to crash when it looped.  If this is an old SDL_mixer version,\n    // disable MIDI.\n\n#ifdef __MACOSX__\n    {\n        const SDL_version *v = Mix_Linked_Version();\n\n        if (SDL_VERSIONNUM(v->major, v->minor, v->patch)\n          < SDL_VERSIONNUM(1, 2, 11))\n        {\n            snd_musicdevice = SNDDEVICE_NONE;\n        }\n    }\n#endif\n#endif\n}\n\n"
  },
  {
    "path": "fbdoom/i_sound.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tThe not so system specific sound interface.\n//\n\n\n#ifndef __I_SOUND__\n#define __I_SOUND__\n\n#include \"doomtype.h\"\n\n\n//\n// SoundFX struct.\n//\ntypedef struct sfxinfo_struct\tsfxinfo_t;\n\nstruct sfxinfo_struct\n{\n    // tag name, used for hexen.\n    char *tagname;\n    \n    // lump name.  If we are running with use_sfx_prefix=true, a\n    // 'DS' (or 'DP' for PC speaker sounds) is prepended to this.\n\n    char name[9];\n\n    // Sfx priority\n    int priority;\n\n    // referenced sound if a link\n    sfxinfo_t *link;\n\n    // pitch if a link\n    int pitch;\n\n    // volume if a link\n    int volume;\n\n    // this is checked every second to see if sound\n    // can be thrown out (if 0, then decrement, if -1,\n    // then throw out, if > 0, then it is in use)\n    int usefulness;\n\n    // lump number of sfx\n    int lumpnum;\t\t\n\n    // Maximum number of channels that the sound can be played on \n    // (Heretic)\n    int numchannels;\n\n    // data used by the low level code\n    void *driver_data;\n};\n\n//\n// MusicInfo struct.\n//\ntypedef struct\n{\n    // up to 6-character name\n    char *name;\n\n    // lump number of music\n    int lumpnum;\n    \n    // music data\n    void *data;\n\n    // music handle once registered\n    void *handle;\n    \n} musicinfo_t;\n\ntypedef enum \n{\n    SNDDEVICE_NONE = 0,\n    SNDDEVICE_PCSPEAKER = 1,\n    SNDDEVICE_ADLIB = 2,\n    SNDDEVICE_SB = 3,\n    SNDDEVICE_PAS = 4,\n    SNDDEVICE_GUS = 5,\n    SNDDEVICE_WAVEBLASTER = 6,\n    SNDDEVICE_SOUNDCANVAS = 7,\n    SNDDEVICE_GENMIDI = 8,\n    SNDDEVICE_AWE32 = 9,\n    SNDDEVICE_CD = 10,\n} snddevice_t;\n\n// Interface for sound modules\n\ntypedef struct\n{\n    // List of sound devices that this sound module is used for.\n\n    snddevice_t *sound_devices;\n    int num_sound_devices;\n\n    // Initialise sound module\n    // Returns true if successfully initialised\n\n    boolean (*Init)(boolean use_sfx_prefix);\n\n    // Shutdown sound module\n\n    void (*Shutdown)(void);\n\n    // Returns the lump index of the given sound.\n\n    int (*GetSfxLumpNum)(sfxinfo_t *sfxinfo);\n\n    // Called periodically to update the subsystem.\n\n    void (*Update)(void);\n\n    // Update the sound settings on the given channel.\n\n    void (*UpdateSoundParams)(int channel, int vol, int sep);\n\n    // Start a sound on a given channel.  Returns the channel id\n    // or -1 on failure.\n\n    int (*StartSound)(sfxinfo_t *sfxinfo, int channel, int vol, int sep);\n\n    // Stop the sound playing on the given channel.\n\n    void (*StopSound)(int channel);\n\n    // Query if a sound is playing on the given channel\n\n    boolean (*SoundIsPlaying)(int channel);\n\n    // Called on startup to precache sound effects (if necessary)\n\n    void (*CacheSounds)(sfxinfo_t *sounds, int num_sounds);\n\n} sound_module_t;\n\nvoid I_InitSound(boolean use_sfx_prefix);\nvoid I_ShutdownSound(void);\nint I_GetSfxLumpNum(sfxinfo_t *sfxinfo);\nvoid I_UpdateSound(void);\nvoid I_UpdateSoundParams(int channel, int vol, int sep);\nint I_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep);\nvoid I_StopSound(int channel);\nboolean I_SoundIsPlaying(int channel);\nvoid I_PrecacheSounds(sfxinfo_t *sounds, int num_sounds);\n\n// Interface for music modules\n\ntypedef struct\n{\n    // List of sound devices that this music module is used for.\n\n    snddevice_t *sound_devices;\n    int num_sound_devices;\n\n    // Initialise the music subsystem\n\n    boolean (*Init)(void);\n\n    // Shutdown the music subsystem\n\n    void (*Shutdown)(void);\n\n    // Set music volume - range 0-127\n\n    void (*SetMusicVolume)(int volume);\n\n    // Pause music\n\n    void (*PauseMusic)(void);\n\n    // Un-pause music\n\n    void (*ResumeMusic)(void);\n\n    // Register a song handle from data\n    // Returns a handle that can be used to play the song\n\n    void *(*RegisterSong)(void *data, int len);\n\n    // Un-register (free) song data\n\n    void (*UnRegisterSong)(void *handle);\n\n    // Play the song\n\n    void (*PlaySong)(void *handle, boolean looping);\n\n    // Stop playing the current song.\n\n    void (*StopSong)(void);\n\n    // Query if music is playing.\n\n    boolean (*MusicIsPlaying)(void);\n\n    // Invoked periodically to poll.\n\n    void (*Poll)(void);\n} music_module_t;\n\nvoid I_InitMusic(void);\nvoid I_ShutdownMusic(void);\nvoid I_SetMusicVolume(int volume);\nvoid I_PauseSong(void);\nvoid I_ResumeSong(void);\nvoid *I_RegisterSong(void *data, int len);\nvoid I_UnRegisterSong(void *handle);\nvoid I_PlaySong(void *handle, boolean looping);\nvoid I_StopSong(void);\nboolean I_MusicIsPlaying(void);\n\nextern int snd_sfxdevice;\nextern int snd_musicdevice;\nextern int snd_samplerate;\nextern int snd_cachesize;\nextern int snd_maxslicetime_ms;\nextern char *snd_musiccmd;\n\nvoid I_BindSoundVariables(void);\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/i_sound_dummy.c",
    "content": "// Emacs style mode select   -*- C++ -*- \n//-----------------------------------------------------------------------------\n//\n// $Id:$\n//\n// Copyright (C) 1993-1996 by id Software, Inc.\n//\n// This source is available for distribution and/or modification\n// only under the terms of the DOOM Source Code License as\n// published by id Software. All rights reserved.\n//\n// The source is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License\n// for more details.\n//\n// $Log:$\n//\n// DESCRIPTION:\n//\tSystem interface for sound.\n//\n//-----------------------------------------------------------------------------\n\nstatic const char\nrcsid[] = \"$Id: i_unix.c,v 1.5 1997/02/03 22:45:10 b1 Exp $\";\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdarg.h>\n\n#include <math.h>\n#include <errno.h>\n\n#include <sys/time.h>\n#include <sys/types.h>\n\n#ifndef LINUX\n#include <sys/filio.h>\n#endif\n\n#include <fcntl.h>\n#include <unistd.h>\n#include <sys/ioctl.h>\n\n// Timer stuff. Experimental.\n#include <time.h>\n#include <signal.h>\n\n#include \"z_zone.h\"\n\n#include \"i_system.h\"\n#include \"i_sound.h\"\n#include \"m_argv.h\"\n#include \"m_misc.h\"\n#include \"w_wad.h\"\n\n#include \"doomdef.h\"\n\n// UNIX hack, to be removed.\n#ifdef SNDSERV\n// Separate sound server process.\nFILE*\tsndserver=0;\nchar*\tsndserver_filename = \"./sndserver \";\n#elif SNDINTR\n\n// Update all 30 millisecs, approx. 30fps synchronized.\n// Linux resolution is allegedly 10 millisecs,\n//  scale is microseconds.\n#define SOUND_INTERVAL     500\n\n// Get the interrupt. Set duration in millisecs.\nint I_SoundSetTimer( int duration_of_tick );\nvoid I_SoundDelTimer( void );\n#else\n// None?\n#endif\n\n\n// A quick hack to establish a protocol between\n// synchronous mix buffer updates and asynchronous\n// audio writes. Probably redundant with gametic.\nstatic int flag = 0;\n\n// The number of internal mixing channels,\n//  the samples calculated for each mixing step,\n//  the size of the 16bit, 2 hardware channel (stereo)\n//  mixing buffer, and the samplerate of the raw data.\n\n\n// Needed for calling the actual sound output.\n#define SAMPLECOUNT\t\t512\n#define NUM_CHANNELS\t\t8\n// It is 2 for 16bit, and 2 for two channels.\n#define BUFMUL                  4\n#define MIXBUFFERSIZE\t\t(SAMPLECOUNT*BUFMUL)\n\n#define SAMPLERATE\t\t11025\t// Hz\n#define SAMPLESIZE\t\t2   \t// 16bit\n\n// The actual lengths of all sound effects.\nint \t\tlengths[NUMSFX];\n\n// The actual output device.\nint\taudio_fd;\n\n// The global mixing buffer.\n// Basically, samples from all active internal channels\n//  are modifed and added, and stored in the buffer\n//  that is submitted to the audio device.\nsigned short\tmixbuffer[MIXBUFFERSIZE];\n\n\n// The channel step amount...\nunsigned int\tchannelstep[NUM_CHANNELS];\n// ... and a 0.16 bit remainder of last step.\nunsigned int\tchannelstepremainder[NUM_CHANNELS];\n\n\n// The channel data pointers, start and end.\nunsigned char*\tchannels[NUM_CHANNELS];\nunsigned char*\tchannelsend[NUM_CHANNELS];\n\n\n// Time/gametic that the channel started playing,\n//  used to determine oldest, which automatically\n//  has lowest priority.\n// In case number of active sounds exceeds\n//  available channels.\nint\t\tchannelstart[NUM_CHANNELS];\n\n// The sound in channel handles,\n//  determined on registration,\n//  might be used to unregister/stop/modify,\n//  currently unused.\nint \t\tchannelhandles[NUM_CHANNELS];\n\n// SFX id of the playing sound effect.\n// Used to catch duplicates (like chainsaw).\nint\t\tchannelids[NUM_CHANNELS];\t\t\t\n\n// Pitch to stepping lookup, unused.\nint\t\tsteptable[256];\n\n// Volume lookups.\nint\t\tvol_lookup[128*256];\n\n// Hardware left and right channel volume lookup.\nint*\t\tchannelleftvol_lookup[NUM_CHANNELS];\nint*\t\tchannelrightvol_lookup[NUM_CHANNELS];\n\n\n\n\n//\n// Safe ioctl, convenience.\n//\nvoid\nmyioctl\n( int\tfd,\n  int\tcommand,\n  int*\targ )\n{   \n    int\t\trc;\n    \n    rc = ioctl(fd, command, arg);  \n    if (rc < 0)\n    {\n\tfprintf(stderr, \"ioctl(dsp,%d,arg) failed\\n\", command);\n\tfprintf(stderr, \"errno=%d\\n\", errno);\n\texit(-1);\n    }\n}\n\n\n\n\n\n//\n// This function loads the sound data from the WAD lump,\n//  for single sound.\n//\nvoid*\ngetsfx\n( char*         sfxname,\n  int*          len )\n{\n    unsigned char*      sfx;\n    unsigned char*      paddedsfx;\n    int                 i;\n    int                 size;\n    int                 paddedsize;\n    char                name[20];\n    int                 sfxlump;\n\n    \n    // Get the sound data from the WAD, allocate lump\n    //  in zone memory.\n    sprintf(name, \"ds%s\", sfxname);\n\n    // Now, there is a severe problem with the\n    //  sound handling, in it is not (yet/anymore)\n    //  gamemode aware. That means, sounds from\n    //  DOOM II will be requested even with DOOM\n    //  shareware.\n    // The sound list is wired into sounds.c,\n    //  which sets the external variable.\n    // I do not do runtime patches to that\n    //  variable. Instead, we will use a\n    //  default sound for replacement.\n    if ( W_CheckNumForName(name) == -1 )\n      sfxlump = W_GetNumForName(\"dspistol\");\n    else\n      sfxlump = W_GetNumForName(name);\n    \n    size = W_LumpLength( sfxlump );\n\n    // Debug.\n    // fprintf( stderr, \".\" );\n    //fprintf( stderr, \" -loading  %s (lump %d, %d bytes)\\n\",\n    //\t     sfxname, sfxlump, size );\n    //fflush( stderr );\n    \n    sfx = (unsigned char*)W_CacheLumpNum( sfxlump, PU_STATIC );\n\n    // Pads the sound effect out to the mixing buffer size.\n    // The original realloc would interfere with zone memory.\n    paddedsize = ((size-8 + (SAMPLECOUNT-1)) / SAMPLECOUNT) * SAMPLECOUNT;\n\n    // Allocate from zone memory.\n    paddedsfx = (unsigned char*)Z_Malloc( paddedsize+8, PU_STATIC, 0 );\n    // ddt: (unsigned char *) realloc(sfx, paddedsize+8);\n    // This should interfere with zone memory handling,\n    //  which does not kick in in the soundserver.\n\n    // Now copy and pad.\n    memcpy(  paddedsfx, sfx, size );\n    for (i=size ; i<paddedsize+8 ; i++)\n        paddedsfx[i] = 128;\n\n    // Remove the cached lump.\n    Z_Free( sfx );\n    \n    // Preserve padded length.\n    *len = paddedsize;\n\n    // Return allocated padded data.\n    return (void *) (paddedsfx + 8);\n}\n\n\n\n\n\n//\n// This function adds a sound to the\n//  list of currently active sounds,\n//  which is maintained as a given number\n//  (eight, usually) of internal channels.\n// Returns a handle.\n//\nint\naddsfx\n( int\t\tsfxid,\n  int\t\tvolume,\n  int\t\tstep,\n  int\t\tseperation )\n{\n    static unsigned short\thandlenums = 0;\n \n    int\t\ti;\n    int\t\trc = -1;\n    \n    int\t\toldest = gametic;\n    int\t\toldestnum = 0;\n    int\t\tslot;\n\n    int\t\trightvol;\n    int\t\tleftvol;\n\n    // Chainsaw troubles.\n    // Play these sound effects only one at a time.\n    if ( sfxid == sfx_sawup\n\t || sfxid == sfx_sawidl\n\t || sfxid == sfx_sawful\n\t || sfxid == sfx_sawhit\n\t || sfxid == sfx_stnmov\n\t || sfxid == sfx_pistol\t )\n    {\n\t// Loop all channels, check.\n\tfor (i=0 ; i<NUM_CHANNELS ; i++)\n\t{\n\t    // Active, and using the same SFX?\n\t    if ( (channels[i])\n\t\t && (channelids[i] == sfxid) )\n\t    {\n\t\t// Reset.\n\t\tchannels[i] = 0;\n\t\t// We are sure that iff,\n\t\t//  there will only be one.\n\t\tbreak;\n\t    }\n\t}\n    }\n\n    // Loop all channels to find oldest SFX.\n    for (i=0; (i<NUM_CHANNELS) && (channels[i]); i++)\n    {\n\tif (channelstart[i] < oldest)\n\t{\n\t    oldestnum = i;\n\t    oldest = channelstart[i];\n\t}\n    }\n\n    // Tales from the cryptic.\n    // If we found a channel, fine.\n    // If not, we simply overwrite the first one, 0.\n    // Probably only happens at startup.\n    if (i == NUM_CHANNELS)\n\tslot = oldestnum;\n    else\n\tslot = i;\n\n    // Okay, in the less recent channel,\n    //  we will handle the new SFX.\n    // Set pointer to raw data.\n    channels[slot] = (unsigned char *) S_sfx[sfxid].data;\n    // Set pointer to end of raw data.\n    channelsend[slot] = channels[slot] + lengths[sfxid];\n\n    // Reset current handle number, limited to 0..100.\n    if (!handlenums)\n\thandlenums = 100;\n\n    // Assign current handle number.\n    // Preserved so sounds could be stopped (unused).\n    channelhandles[slot] = rc = handlenums++;\n\n    // Set stepping???\n    // Kinda getting the impression this is never used.\n    channelstep[slot] = step;\n    // ???\n    channelstepremainder[slot] = 0;\n    // Should be gametic, I presume.\n    channelstart[slot] = gametic;\n\n    // Separation, that is, orientation/stereo.\n    //  range is: 1 - 256\n    seperation += 1;\n\n    // Per left/right channel.\n    //  x^2 seperation,\n    //  adjust volume properly.\n    leftvol =\n\tvolume - ((volume*seperation*seperation) >> 16); ///(256*256);\n    seperation = seperation - 257;\n    rightvol =\n\tvolume - ((volume*seperation*seperation) >> 16);\t\n\n    // Sanity check, clamp volume.\n    if (rightvol < 0 || rightvol > 127)\n\tI_Error(\"rightvol out of bounds\");\n    \n    if (leftvol < 0 || leftvol > 127)\n\tI_Error(\"leftvol out of bounds\");\n    \n    // Get the proper lookup table piece\n    //  for this volume level???\n    channelleftvol_lookup[slot] = &vol_lookup[leftvol*256];\n    channelrightvol_lookup[slot] = &vol_lookup[rightvol*256];\n\n    // Preserve sound SFX id,\n    //  e.g. for avoiding duplicates of chainsaw.\n    channelids[slot] = sfxid;\n\n    // You tell me.\n    return rc;\n}\n\n\n\n\n\n//\n// SFX API\n// Note: this was called by S_Init.\n// However, whatever they did in the\n// old DPMS based DOS version, this\n// were simply dummies in the Linux\n// version.\n// See soundserver initdata().\n//\nvoid I_SetChannels()\n{\n  // Init internal lookups (raw data, mixing buffer, channels).\n  // This function sets up internal lookups used during\n  //  the mixing process. \n  int\t\ti;\n  int\t\tj;\n    \n  int*\tsteptablemid = steptable + 128;\n  \n  // Okay, reset internal mixing channels to zero.\n  /*for (i=0; i<NUM_CHANNELS; i++)\n  {\n    channels[i] = 0;\n  }*/\n\n  // This table provides step widths for pitch parameters.\n  // I fail to see that this is currently used.\n  for (i=-128 ; i<128 ; i++)\n    steptablemid[i] = (int)(pow(2.0, (i/64.0))*65536.0);\n  \n  \n  // Generates volume lookup tables\n  //  which also turn the unsigned samples\n  //  into signed samples.\n  for (i=0 ; i<128 ; i++)\n    for (j=0 ; j<256 ; j++)\n      vol_lookup[i*256+j] = (i*(j-128)*256)/127;\n}\t\n\n \nvoid I_SetSfxVolume(int volume)\n{\n  // Identical to DOS.\n  // Basically, this should propagate\n  //  the menu/config file setting\n  //  to the state variable used in\n  //  the mixing.\n  snd_SfxVolume = volume;\n}\n\n// MUSIC API - dummy. Some code from DOS version.\nvoid I_SetMusicVolume(int volume)\n{\n  // Internal state variable.\n  snd_MusicVolume = volume;\n  // Now set volume on output device.\n  // Whatever( snd_MusciVolume );\n}\n\n\n//\n// Retrieve the raw data lump index\n//  for a given SFX name.\n//\nint I_GetSfxLumpNum(sfxinfo_t* sfx)\n{\n    char namebuf[9];\n    sprintf(namebuf, \"ds%s\", sfx->name);\n    return W_GetNumForName(namebuf);\n}\n\n//\n// Starting a sound means adding it\n//  to the current list of active sounds\n//  in the internal channels.\n// As the SFX info struct contains\n//  e.g. a pointer to the raw data,\n//  it is ignored.\n// As our sound handling does not handle\n//  priority, it is ignored.\n// Pitching (that is, increased speed of playback)\n//  is set, but currently not used by mixing.\n//\nint\nI_StartSound\n( int\t\tid,\n  int\t\tvol,\n  int\t\tsep,\n  int\t\tpitch,\n  int\t\tpriority )\n{\n\n  // UNUSED\n  priority = 0;\n  \n#ifdef SNDSERV \n    if (sndserver)\n    {\n\tfprintf(sndserver, \"p%2.2x%2.2x%2.2x%2.2x\\n\", id, pitch, vol, sep);\n\tfflush(sndserver);\n    }\n    // warning: control reaches end of non-void function.\n    return id;\n#else\n    // Debug.\n    //fprintf( stderr, \"starting sound %d\", id );\n    \n    // Returns a handle (not used).\n    id = addsfx( id, vol, steptable[pitch], sep );\n\n    // fprintf( stderr, \"/handle is %d\\n\", id );\n    \n    return id;\n#endif\n}\n\n\n\nvoid I_StopSound (int handle)\n{\n  // You need the handle returned by StartSound.\n  // Would be looping all channels,\n  //  tracking down the handle,\n  //  an setting the channel to zero.\n  \n  // UNUSED.\n  handle = 0;\n}\n\n\nint I_SoundIsPlaying(int handle)\n{\n    // Ouch.\n    return gametic < handle;\n}\n\n\n\n\n//\n// This function loops all active (internal) sound\n//  channels, retrieves a given number of samples\n//  from the raw sound data, modifies it according\n//  to the current (internal) channel parameters,\n//  mixes the per channel samples into the global\n//  mixbuffer, clamping it to the allowed range,\n//  and sets up everything for transferring the\n//  contents of the mixbuffer to the (two)\n//  hardware channels (left and right, that is).\n//\n// This function currently supports only 16bit.\n//\nvoid I_UpdateSound( void )\n{\n#ifdef SNDINTR\n  // Debug. Count buffer misses with interrupt.\n  static int misses = 0;\n#endif\n\n  \n  // Mix current sound data.\n  // Data, from raw sound, for right and left.\n  register unsigned int\tsample;\n  register int\t\tdl;\n  register int\t\tdr;\n  \n  // Pointers in global mixbuffer, left, right, end.\n  signed short*\t\tleftout;\n  signed short*\t\trightout;\n  signed short*\t\tleftend;\n  // Step in mixbuffer, left and right, thus two.\n  int\t\t\t\tstep;\n\n  // Mixing channel index.\n  int\t\t\t\tchan;\n    \n    // Left and right channel\n    //  are in global mixbuffer, alternating.\n    leftout = mixbuffer;\n    rightout = mixbuffer+1;\n    step = 2;\n\n    // Determine end, for left channel only\n    //  (right channel is implicit).\n    leftend = mixbuffer + SAMPLECOUNT*step;\n\n    // Mix sounds into the mixing buffer.\n    // Loop over step*SAMPLECOUNT,\n    //  that is 512 values for two channels.\n    while (leftout != leftend)\n    {\n\t// Reset left/right value. \n\tdl = 0;\n\tdr = 0;\n\n\t// Love thy L2 chache - made this a loop.\n\t// Now more channels could be set at compile time\n\t//  as well. Thus loop those  channels.\n\tfor ( chan = 0; chan < NUM_CHANNELS; chan++ )\n\t{\n\t    // Check channel, if active.\n\t    if (channels[ chan ])\n\t    {\n\t\t// Get the raw data from the channel. \n\t\tsample = *channels[ chan ];\n\t\t// Add left and right part\n\t\t//  for this channel (sound)\n\t\t//  to the current data.\n\t\t// Adjust volume accordingly.\n\t\tdl += channelleftvol_lookup[ chan ][sample];\n\t\tdr += channelrightvol_lookup[ chan ][sample];\n\t\t// Increment index ???\n\t\tchannelstepremainder[ chan ] += channelstep[ chan ];\n\t\t// MSB is next sample???\n\t\tchannels[ chan ] += channelstepremainder[ chan ] >> 16;\n\t\t// Limit to LSB???\n\t\tchannelstepremainder[ chan ] &= 65536-1;\n\n\t\t// Check whether we are done.\n\t\tif (channels[ chan ] >= channelsend[ chan ])\n\t\t    channels[ chan ] = 0;\n\t    }\n\t}\n\t\n\t// Clamp to range. Left hardware channel.\n\t// Has been char instead of short.\n\t// if (dl > 127) *leftout = 127;\n\t// else if (dl < -128) *leftout = -128;\n\t// else *leftout = dl;\n\n\tif (dl > 0x7fff)\n\t    *leftout = 0x7fff;\n\telse if (dl < -0x8000)\n\t    *leftout = -0x8000;\n\telse\n\t    *leftout = dl;\n\n\t// Same for right hardware channel.\n\tif (dr > 0x7fff)\n\t    *rightout = 0x7fff;\n\telse if (dr < -0x8000)\n\t    *rightout = -0x8000;\n\telse\n\t    *rightout = dr;\n\n\t// Increment current pointers in mixbuffer.\n\tleftout += step;\n\trightout += step;\n    }\n\n#ifdef SNDINTR\n    // Debug check.\n    if ( flag )\n    {\n      misses += flag;\n      flag = 0;\n    }\n    \n    if ( misses > 10 )\n    {\n      fprintf( stderr, \"I_SoundUpdate: missed 10 buffer writes\\n\");\n      misses = 0;\n    }\n    \n    // Increment flag for update.\n    flag++;\n#endif\n}\n\n\n// \n// This would be used to write out the mixbuffer\n//  during each game loop update.\n// Updates sound buffer and audio device at runtime. \n// It is called during Timer interrupt with SNDINTR.\n// Mixing now done synchronous, and\n//  only output be done asynchronous?\n//\nvoid\nI_SubmitSound(void)\n{\n  // Write it to DSP device.\n  write(audio_fd, mixbuffer, SAMPLECOUNT*BUFMUL);\n}\n\n\n\nvoid\nI_UpdateSoundParams\n( int\thandle,\n  int\tvol,\n  int\tsep,\n  int\tpitch)\n{\n  // I fail too see that this is used.\n  // Would be using the handle to identify\n  //  on which channel the sound might be active,\n  //  and resetting the channel parameters.\n\n  // UNUSED.\n  handle = vol = sep = pitch = 0;\n}\n\n\n\n\nvoid I_ShutdownSound(void)\n{    \n#ifdef SNDSERV\n  if (sndserver)\n  {\n    // Send a \"quit\" command.\n    fprintf(sndserver, \"q\\n\");\n    fflush(sndserver);\n  }\n#else\n  // Wait till all pending sounds are finished.\n  int done = 0;\n  int i;\n  \n\n  // FIXME (below).\n  fprintf( stderr, \"I_ShutdownSound: NOT finishing pending sounds\\n\");\n  fflush( stderr );\n  \n  while ( !done )\n  {\n    for( i=0 ; i<8 && !channels[i] ; i++);\n    \n    // FIXME. No proper channel output.\n    //if (i==8)\n    done=1;\n  }\n#ifdef SNDINTR\n  I_SoundDelTimer();\n#endif\n  \n  // Cleaning up -releasing the DSP device.\n  close ( audio_fd );\n#endif\n\n  // Done.\n  return;\n}\n\n\n\n\n\n\nvoid\nI_InitSound()\n{ \n#ifdef SNDSERV\n  char buffer[256];\n  \n  if (getenv(\"DOOMWADDIR\"))\n    sprintf(buffer, \"%s/%s\",\n\t    getenv(\"DOOMWADDIR\"),\n\t    sndserver_filename);\n  else\n    sprintf(buffer, \"%s\", sndserver_filename);\n  \n  // start sound process\n  if ( !access(buffer, X_OK) )\n  {\n    strcat(buffer, \" -quiet\");\n    sndserver = NULL; //popen(buffer, \"w\");\n  }\n  else\n    fprintf(stderr, \"Could not start sound server [%s]\\n\", buffer);\n#else\n    \n  int i;\n  \n#ifdef SNDINTR\n  fprintf( stderr, \"I_SoundSetTimer: %d microsecs\\n\", SOUND_INTERVAL );\n  I_SoundSetTimer( SOUND_INTERVAL );\n#endif\n    \n  // Secure and configure sound device first.\n  fprintf( stderr, \"I_InitSound: \");\n  \n  audio_fd = open(\"/dev/dsp\", O_WRONLY);\n  if (audio_fd<0)\n    fprintf(stderr, \"Could not open /dev/dsp\\n\");\n  \n                     \n  i = 11 | (2<<16);                                           \n  myioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &i);\n  myioctl(audio_fd, SNDCTL_DSP_RESET, 0);\n  \n  i=SAMPLERATE;\n  \n  myioctl(audio_fd, SNDCTL_DSP_SPEED, &i);\n  \n  i=1;\n  myioctl(audio_fd, SNDCTL_DSP_STEREO, &i);\n  \n  myioctl(audio_fd, SNDCTL_DSP_GETFMTS, &i);\n  \n  if (i&=AFMT_S16_LE)    \n    myioctl(audio_fd, SNDCTL_DSP_SETFMT, &i);\n  else\n    fprintf(stderr, \"Could not play signed 16 data\\n\");\n\n  fprintf(stderr, \" configured audio device\\n\" );\n\n    \n  // Initialize external data (all sounds) at start, keep static.\n  fprintf( stderr, \"I_InitSound: \");\n  \n  for (i=1 ; i<NUMSFX ; i++)\n  { \n    // Alias? Example is the chaingun sound linked to pistol.\n    if (!S_sfx[i].link)\n    {\n      // Load data from WAD file.\n      S_sfx[i].data = getsfx( S_sfx[i].name, &lengths[i] );\n    }\t\n    else\n    {\n      // Previously loaded already?\n      S_sfx[i].data = S_sfx[i].link->data;\n      lengths[i] = lengths[(S_sfx[i].link - S_sfx)/sizeof(sfxinfo_t)];\n    }\n  }\n\n  fprintf( stderr, \" pre-cached all sound data\\n\");\n  \n  // Now initialize mixbuffer with zero.\n  for ( i = 0; i< MIXBUFFERSIZE; i++ )\n    mixbuffer[i] = 0;\n  \n  // Finished initialization.\n  fprintf(stderr, \"I_InitSound: sound module ready\\n\");\n    \n#endif\n}\n\n\n\n\n//\n// MUSIC API.\n// Still no music done.\n// Remains. Dummies.\n//\nvoid I_InitMusic(void)\t\t{ }\nvoid I_ShutdownMusic(void)\t{ }\n\nstatic int\tlooping=0;\nstatic int\tmusicdies=-1;\n\nvoid I_PlaySong(int handle, int looping)\n{\n  // UNUSED.\n  handle = looping = 0;\n  musicdies = gametic + TICRATE*30;\n}\n\nvoid I_PauseSong (int handle)\n{\n  // UNUSED.\n  handle = 0;\n}\n\nvoid I_ResumeSong (int handle)\n{\n  // UNUSED.\n  handle = 0;\n}\n\nvoid I_StopSong(int handle)\n{\n  // UNUSED.\n  handle = 0;\n  \n  looping = 0;\n  musicdies = 0;\n}\n\nvoid I_UnRegisterSong(int handle)\n{\n  // UNUSED.\n  handle = 0;\n}\n\nint I_RegisterSong(void* data)\n{\n  // UNUSED.\n  data = NULL;\n  \n  return 1;\n}\n\n// Is the song playing?\nint I_QrySongPlaying(int handle)\n{\n  // UNUSED.\n  handle = 0;\n  return looping || musicdies > gametic;\n}\n\n\n\n//\n// Experimental stuff.\n// A Linux timer interrupt, for asynchronous\n//  sound output.\n// I ripped this out of the Timer class in\n//  our Difference Engine, including a few\n//  SUN remains...\n//  \n#ifdef sun\n    typedef     sigset_t        tSigSet;\n#else    \n    typedef     int             tSigSet;\n#endif\n\n\n// We might use SIGVTALRM and ITIMER_VIRTUAL, if the process\n//  time independend timer happens to get lost due to heavy load.\n// SIGALRM and ITIMER_REAL doesn't really work well.\n// There are issues with profiling as well.\n\n// Interrupt handler.\nvoid I_HandleSoundTimer( int ignore )\n{\n  // Debug.\n  //fprintf( stderr, \"%c\", '+' ); fflush( stderr );\n  \n  // Feed sound device if necesary.\n  if ( flag )\n  {\n    // See I_SubmitSound().\n    // Write it to DSP device.\n    write(audio_fd, mixbuffer, SAMPLECOUNT*BUFMUL);\n\n    // Reset flag counter.\n    flag = 0;\n  }\n  else\n    return;\n  \n  // UNUSED, but required.\n  ignore = 0;\n  return;\n}\n\n// Get the interrupt. Set duration in millisecs.\nint I_SoundSetTimer( int duration_of_tick )\n{\n  return -1;\n}\n\n\n// Remove the interrupt. Set duration to zero.\nvoid I_SoundDelTimer()\n{\n  // Debug.\n  if ( I_SoundSetTimer( 0 ) == -1)\n    fprintf( stderr, \"I_SoundDelTimer: failed to remove interrupt. Doh!\\n\");\n}\n"
  },
  {
    "path": "fbdoom/i_swap.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tEndianess handling, swapping 16bit and 32bit.\n//\n\n\n#ifndef __I_SWAP__\n#define __I_SWAP__\n\n#ifdef ORIGCODE\n#include \"SDL_endian.h\"\n\n// Endianess handling.\n// WAD files are stored little endian.\n\n// Just use SDL's endianness swapping functions.\n\n// These are deliberately cast to signed values; this is the behaviour\n// of the macros in the original source and some code relies on it.\n\n#define SHORT(x)  ((signed short) SDL_SwapLE16(x))\n#define LONG(x)   ((signed int) SDL_SwapLE32(x))\n\n// Defines for checking the endianness of the system.\n\n#if SDL_BYTEORDER == SYS_LIL_ENDIAN\n#define SYS_LITTLE_ENDIAN\n#elif SDL_BYTEORDER == SYS_BIG_ENDIAN\n#define SYS_BIG_ENDIAN\n#endif\n\n#else\n\t\n#define SHORT(x)  ((signed short) (x))\n#define LONG(x)   ((signed int) (x))\n\n#define SYS_LITTLE_ENDIAN\n\n#endif\n#endif\n\n"
  },
  {
    "path": "fbdoom/i_system.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\n\n\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\n#include <stdarg.h>\n\n#ifdef _WIN32\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n#else\n#include <unistd.h>\n#endif\n\n#ifdef ORIGCODE\n#include \"SDL.h\"\n#endif\n\n#include \"config.h\"\n\n#include \"deh_str.h\"\n#include \"doomtype.h\"\n#include \"m_argv.h\"\n#include \"m_config.h\"\n#include \"m_misc.h\"\n#include \"i_joystick.h\"\n#include \"i_sound.h\"\n#include \"i_timer.h\"\n#include \"i_video.h\"\n\n#include \"i_system.h\"\n\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\n#ifdef __MACOSX__\n#include <CoreFoundation/CFUserNotification.h>\n#endif\n\n#define DEFAULT_RAM 6 /* MiB */\n#define MIN_RAM     6  /* MiB */\n\n\ntypedef struct atexit_listentry_s atexit_listentry_t;\n\nstruct atexit_listentry_s\n{\n    atexit_func_t func;\n    boolean run_on_error;\n    atexit_listentry_t *next;\n};\n\nstatic atexit_listentry_t *exit_funcs = NULL;\n\nvoid I_AtExit(atexit_func_t func, boolean run_on_error)\n{\n    atexit_listentry_t *entry;\n\n    entry = malloc(sizeof(*entry));\n\n    entry->func = func;\n    entry->run_on_error = run_on_error;\n    entry->next = exit_funcs;\n    exit_funcs = entry;\n}\n\n// Tactile feedback function, probably used for the Logitech Cyberman\n\nvoid I_Tactile(int on, int off, int total)\n{\n}\n\n// Zone memory auto-allocation function that allocates the zone size\n// by trying progressively smaller zone sizes until one is found that\n// works.\n\nstatic byte *AutoAllocMemory(int *size, int default_ram, int min_ram)\n{\n    byte *zonemem;\n\n    // Allocate the zone memory.  This loop tries progressively smaller\n    // zone sizes until a size is found that can be allocated.\n    // If we used the -mb command line parameter, only the parameter\n    // provided is accepted.\n\n    zonemem = NULL;\n\n    while (zonemem == NULL)\n    {\n        // We need a reasonable minimum amount of RAM to start.\n\n        if (default_ram < min_ram)\n        {\n            I_Error(\"Unable to allocate %i MiB of RAM for zone\", default_ram);\n        }\n\n        // Try to allocate the zone memory.\n\n        *size = default_ram * 1024 * 1024;\n\n        zonemem = malloc(*size);\n\n        // Failed to allocate?  Reduce zone size until we reach a size\n        // that is acceptable.\n\n        if (zonemem == NULL)\n        {\n            default_ram -= 1;\n        }\n    }\n\n    return zonemem;\n}\n\nbyte *I_ZoneBase (int *size)\n{\n    byte *zonemem;\n    int min_ram, default_ram;\n    int p;\n\n    //!\n    // @arg <mb>\n    //\n    // Specify the heap size, in MiB (default 16).\n    //\n\n    p = M_CheckParmWithArgs(\"-mb\", 1);\n\n    if (p > 0)\n    {\n        default_ram = atoi(myargv[p+1]);\n        min_ram = default_ram;\n    }\n    else\n    {\n        default_ram = DEFAULT_RAM;\n        min_ram = MIN_RAM;\n    }\n\n    zonemem = AutoAllocMemory(size, default_ram, min_ram);\n\n    printf(\"zone memory: %p, %x allocated for zone\\n\", \n           zonemem, *size);\n\n    return zonemem;\n}\n\nvoid I_PrintBanner(char *msg)\n{\n    int i;\n    int spaces = 35 - (strlen(msg) / 2);\n\n    for (i=0; i<spaces; ++i)\n        putchar(' ');\n\n    puts(msg);\n}\n\nvoid I_PrintDivider(void)\n{\n    int i;\n\n    for (i=0; i<75; ++i)\n    {\n        putchar('=');\n    }\n\n    putchar('\\n');\n}\n\nvoid I_PrintStartupBanner(char *gamedescription)\n{\n    I_PrintDivider();\n    I_PrintBanner(gamedescription);\n    I_PrintDivider();\n    \n    printf(\n    \" \" PACKAGE_NAME \" is free software, covered by the GNU General Public\\n\"\n    \" License.  There is NO warranty; not even for MERCHANTABILITY or FITNESS\\n\"\n    \" FOR A PARTICULAR PURPOSE. You are welcome to change and distribute\\n\"\n    \" copies under certain conditions. See the source for more information.\\n\");\n\n    I_PrintDivider();\n}\n\n// \n// I_ConsoleStdout\n//\n// Returns true if stdout is a real console, false if it is a file\n//\n\nboolean I_ConsoleStdout(void)\n{\n#ifdef _WIN32\n    // SDL \"helpfully\" always redirects stdout to a file.\n    return 0;\n#else\n#if ORIGCODE\n    return isatty(fileno(stdout));\n#else\n\treturn 0;\n#endif\n#endif\n}\n\n//\n// I_Init\n//\n/*\nvoid I_Init (void)\n{\n    I_CheckIsScreensaver();\n    I_InitTimer();\n    I_InitJoystick();\n}\nvoid I_BindVariables(void)\n{\n    I_BindVideoVariables();\n    I_BindJoystickVariables();\n    I_BindSoundVariables();\n}\n*/\n\n//\n// I_Quit\n//\n\nvoid I_Quit (void)\n{\n    atexit_listentry_t *entry;\n\n    // Run through all exit functions\n \n    entry = exit_funcs; \n\n    while (entry != NULL)\n    {\n        entry->func();\n        entry = entry->next;\n    }\n\n#if ORIGCODE\n    SDL_Quit();\n\n    exit(0);\n#endif\n}\n\n#if !defined(_WIN32) && !defined(__MACOSX__)\n#define ZENITY_BINARY \"/usr/bin/zenity\"\n\n// returns non-zero if zenity is available\n\nstatic int ZenityAvailable(void)\n{\n    return system(ZENITY_BINARY \" --help >/dev/null 2>&1\") == 0;\n}\n\n// Escape special characters in the given string so that they can be\n// safely enclosed in shell quotes.\n\nstatic char *EscapeShellString(char *string)\n{\n    char *result;\n    char *r, *s;\n\n    // In the worst case, every character might be escaped.\n    result = malloc(strlen(string) * 2 + 3);\n    r = result;\n\n    // Enclosing quotes.\n    *r = '\"';\n    ++r;\n\n    for (s = string; *s != '\\0'; ++s)\n    {\n        // From the bash manual:\n        //\n        //  \"Enclosing characters in double quotes preserves the literal\n        //   value of all characters within the quotes, with the exception\n        //   of $, `, \\, and, when history expansion is enabled, !.\"\n        //\n        // Therefore, escape these characters by prefixing with a backslash.\n\n        if (strchr(\"$`\\\\!\", *s) != NULL)\n        {\n            *r = '\\\\';\n            ++r;\n        }\n\n        *r = *s;\n        ++r;\n    }\n\n    // Enclosing quotes.\n    *r = '\"';\n    ++r;\n    *r = '\\0';\n\n    return result;\n}\n\n// Open a native error box with a message using zenity\n\nstatic int ZenityErrorBox(char *message)\n{\n    int result;\n    char *escaped_message;\n    char *errorboxpath;\n    static size_t errorboxpath_size;\n\n    if (!ZenityAvailable())\n    {\n        return 0;\n    }\n\n    escaped_message = EscapeShellString(message);\n\n    errorboxpath_size = strlen(ZENITY_BINARY) + strlen(escaped_message) + 19;\n    errorboxpath = malloc(errorboxpath_size);\n    M_snprintf(errorboxpath, errorboxpath_size, \"%s --error --text=%s\",\n               ZENITY_BINARY, escaped_message);\n\n    result = system(errorboxpath);\n\n    free(errorboxpath);\n    free(escaped_message);\n\n    return result;\n}\n\n#endif /* !defined(_WIN32) && !defined(__MACOSX__) */\n\n\n//\n// I_Error\n//\n\nstatic boolean already_quitting = false;\n\nvoid I_Error (char *error, ...)\n{\n    char msgbuf[512];\n    va_list argptr;\n    atexit_listentry_t *entry;\n    boolean exit_gui_popup;\n\n    if (already_quitting)\n    {\n        fprintf(stderr, \"Warning: recursive call to I_Error detected.\\n\");\n#if ORIGCODE\n        exit(-1);\n#endif\n    }\n    else\n    {\n        already_quitting = true;\n    }\n\n    // Message first.\n    va_start(argptr, error);\n    //fprintf(stderr, \"\\nError: \");\n    vfprintf(stderr, error, argptr);\n    fprintf(stderr, \"\\n\\n\");\n    va_end(argptr);\n    fflush(stderr);\n\n    // Write a copy of the message into buffer.\n    va_start(argptr, error);\n    memset(msgbuf, 0, sizeof(msgbuf));\n    M_vsnprintf(msgbuf, sizeof(msgbuf), error, argptr);\n    va_end(argptr);\n\n    // Shutdown. Here might be other errors.\n\n    entry = exit_funcs;\n\n    while (entry != NULL)\n    {\n        if (entry->run_on_error)\n        {\n            entry->func();\n        }\n\n        entry = entry->next;\n    }\n\n    exit_gui_popup = !M_ParmExists(\"-nogui\");\n\n    // Pop up a GUI dialog box to show the error message, if the\n    // game was not run from the console (and the user will\n    // therefore be unable to otherwise see the message).\n    if (exit_gui_popup && !I_ConsoleStdout())\n#ifdef _WIN32\n    {\n        wchar_t wmsgbuf[512];\n\n        MultiByteToWideChar(CP_ACP, 0,\n                            msgbuf, strlen(msgbuf) + 1,\n                            wmsgbuf, sizeof(wmsgbuf));\n\n        MessageBoxW(NULL, wmsgbuf, L\"\", MB_OK);\n    }\n#elif defined(__MACOSX__)\n    {\n        CFStringRef message;\n\tint i;\n\n\t// The CoreFoundation message box wraps text lines, so replace\n\t// newline characters with spaces so that multiline messages\n\t// are continuous.\n\n\tfor (i = 0; msgbuf[i] != '\\0'; ++i)\n        {\n            if (msgbuf[i] == '\\n')\n            {\n                msgbuf[i] = ' ';\n            }\n        }\n\n        message = CFStringCreateWithCString(NULL, msgbuf,\n                                            kCFStringEncodingUTF8);\n\n        CFUserNotificationDisplayNotice(0,\n                                        kCFUserNotificationCautionAlertLevel,\n                                        NULL,\n                                        NULL,\n                                        NULL,\n                                        CFSTR(PACKAGE_STRING),\n                                        message,\n                                        NULL);\n    }\n#else\n    {\n        ZenityErrorBox(msgbuf);\n    }\n#endif\n\n    // abort();\n#if ORIGCODE\n    SDL_Quit();\n\n    exit(-1);\n#else\n    while (true)\n    {\n    }\n#endif\n}\n\n//\n// Read Access Violation emulation.\n//\n// From PrBoom+, by entryway.\n//\n\n// C:\\>debug\n// -d 0:0\n//\n// DOS 6.22:\n// 0000:0000  (57 92 19 00) F4 06 70 00-(16 00)\n// DOS 7.1:\n// 0000:0000  (9E 0F C9 00) 65 04 70 00-(16 00)\n// Win98:\n// 0000:0000  (9E 0F C9 00) 65 04 70 00-(16 00)\n// DOSBox under XP:\n// 0000:0000  (00 00 00 F1) ?? ?? ?? 00-(07 00)\n\n#define DOS_MEM_DUMP_SIZE 10\n\nstatic const unsigned char mem_dump_dos622[DOS_MEM_DUMP_SIZE] = {\n  0x57, 0x92, 0x19, 0x00, 0xF4, 0x06, 0x70, 0x00, 0x16, 0x00};\nstatic const unsigned char mem_dump_win98[DOS_MEM_DUMP_SIZE] = {\n  0x9E, 0x0F, 0xC9, 0x00, 0x65, 0x04, 0x70, 0x00, 0x16, 0x00};\nstatic const unsigned char mem_dump_dosbox[DOS_MEM_DUMP_SIZE] = {\n  0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00};\nstatic unsigned char mem_dump_custom[DOS_MEM_DUMP_SIZE];\n\nstatic const unsigned char *dos_mem_dump = mem_dump_dos622;\n\nboolean I_GetMemoryValue(unsigned int offset, void *value, int size)\n{\n    static boolean firsttime = true;\n\n    if (firsttime)\n    {\n        int p, i, val;\n\n        firsttime = false;\n        i = 0;\n\n        //!\n        // @category compat\n        // @arg <version>\n        //\n        // Specify DOS version to emulate for NULL pointer dereference\n        // emulation.  Supported versions are: dos622, dos71, dosbox.\n        // The default is to emulate DOS 7.1 (Windows 98).\n        //\n\n        p = M_CheckParmWithArgs(\"-setmem\", 1);\n\n        if (p > 0)\n        {\n            if (!strcasecmp(myargv[p + 1], \"dos622\"))\n            {\n                dos_mem_dump = mem_dump_dos622;\n            }\n            if (!strcasecmp(myargv[p + 1], \"dos71\"))\n            {\n                dos_mem_dump = mem_dump_win98;\n            }\n            else if (!strcasecmp(myargv[p + 1], \"dosbox\"))\n            {\n                dos_mem_dump = mem_dump_dosbox;\n            }\n            else\n            {\n                for (i = 0; i < DOS_MEM_DUMP_SIZE; ++i)\n                {\n                    ++p;\n\n                    if (p >= myargc || myargv[p][0] == '-')\n                    {\n                        break;\n                    }\n\n                    M_StrToInt(myargv[p], &val);\n                    mem_dump_custom[i++] = (unsigned char) val;\n                }\n\n                dos_mem_dump = mem_dump_custom;\n            }\n        }\n    }\n\n    switch (size)\n    {\n    case 1:\n        *((unsigned char *) value) = dos_mem_dump[offset];\n        return true;\n    case 2:\n        *((unsigned short *) value) = dos_mem_dump[offset]\n                                    | (dos_mem_dump[offset + 1] << 8);\n        return true;\n    case 4:\n        *((unsigned int *) value) = dos_mem_dump[offset]\n                                  | (dos_mem_dump[offset + 1] << 8)\n                                  | (dos_mem_dump[offset + 2] << 16)\n                                  | (dos_mem_dump[offset + 3] << 24);\n        return true;\n    }\n\n    return false;\n}\n\n"
  },
  {
    "path": "fbdoom/i_system.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tSystem specific interface stuff.\n//\n\n\n#ifndef __I_SYSTEM__\n#define __I_SYSTEM__\n\n#include \"d_ticcmd.h\"\n#include \"d_event.h\"\n\n\ntypedef void (*atexit_func_t)(void);\n\n// Called by DoomMain.\nvoid I_Init (void);\n\n// Called by startup code\n// to get the ammount of memory to malloc\n// for the zone management.\nbyte*\tI_ZoneBase (int *size);\n\nboolean I_ConsoleStdout(void);\n\n\n// Asynchronous interrupt functions should maintain private queues\n// that are read by the synchronous functions\n// to be converted into events.\n\n// Either returns a null ticcmd,\n// or calls a loadable driver to build it.\n// This ticcmd will then be modified by the gameloop\n// for normal input.\nticcmd_t* I_BaseTiccmd (void);\n\n\n// Called by M_Responder when quit is selected.\n// Clean exit, displays sell blurb.\nvoid I_Quit (void);\n\nvoid I_Error (char *error, ...);\n\nvoid I_Tactile (int on, int off, int total);\n\nboolean I_GetMemoryValue(unsigned int offset, void *value, int size);\n\n// Schedule a function to be called when the program exits.\n// If run_if_error is true, the function is called if the exit\n// is due to an error (I_Error)\n\nvoid I_AtExit(atexit_func_t func, boolean run_if_error);\n\n// Add all system-specific config file variable bindings.\n\nvoid I_BindVariables(void);\n\n// Print startup banner copyright message.\n\nvoid I_PrintStartupBanner(char *gamedescription);\n\n// Print a centered text banner displaying the given string.\n\nvoid I_PrintBanner(char *text);\n\n// Print a dividing line for startup banners.\n\nvoid I_PrintDivider(void);\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/i_timer.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//      Timer functions.\n//\n\n#include \"i_timer.h\"\n#include \"doomtype.h\"\n\n#include <stdarg.h>\n#include <sys/time.h>\n#include <unistd.h>\n\n//\n// I_GetTime\n// returns time in 1/35th second tics\n//\n\nstatic uint32_t basetime = 0;\n\nint I_GetTicks(void)\n{\n    struct timeval  tp;\n    struct timezone tzp;\n  \n    gettimeofday(&tp, &tzp);\n    return (tp.tv_sec * 1000) + (tp.tv_usec / 1000); /* return milliseconds */\n}\n\nint  I_GetTime (void)\n{\n    uint32_t ticks;\n\n    ticks = I_GetTicks();\n\n    if (basetime == 0)\n        basetime = ticks;\n\n    ticks -= basetime;\n\n    return (ticks * TICRATE) / 1000;    \n}\n\n\n//\n// Same as I_GetTime, but returns time in milliseconds\n//\n\nint I_GetTimeMS(void)\n{\n    uint32_t ticks;\n\n    ticks = I_GetTicks();\n\n    if (basetime == 0)\n        basetime = ticks;\n\n    return ticks - basetime;\n}\n\n// Sleep for a specified number of ms\n\nvoid I_Sleep(int ms)\n{\n    //SDL_Delay(ms);\n    usleep (ms * 1000);    \n}\n\nvoid I_WaitVBL(int count)\n{\n    //I_Sleep((count * 1000) / 70);\n}\n\n\nvoid I_InitTimer(void)\n{\n    // initialize timer\n\n    //SDL_Init(SDL_INIT_TIMER);\n}\n\n"
  },
  {
    "path": "fbdoom/i_timer.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//      System-specific timer interface\n//\n\n\n#ifndef __I_TIMER__\n#define __I_TIMER__\n\n#define TICRATE 35\n\n// Called by D_DoomLoop,\n// returns current time in tics.\nint I_GetTime (void);\n\n// returns current time in ms\nint I_GetTimeMS (void);\n\n// Pause for a specified number of ms\nvoid I_Sleep(int ms);\n\n// Initialize timer\nvoid I_InitTimer(void);\n\n// Wait for vertical retrace or pause a bit.\nvoid I_WaitVBL(int count);\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/i_video.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tDOOM graphics stuff for SDL.\n//\n\n\n#include \"SDL/SDL.h\"\n#include <stdlib.h>\n#include <ctype.h>\n#include <math.h>\n#include <string.h>\n\n#ifdef _WIN32\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n#endif\n\n#include \"icon.c\"\n\n#include \"config.h\"\n#include \"deh_str.h\"\n#include \"doomtype.h\"\n#include \"doomkeys.h\"\n#include \"i_joystick.h\"\n#include \"i_system.h\"\n#include \"i_swap.h\"\n#include \"i_timer.h\"\n#include \"i_video.h\"\n#include \"i_scale.h\"\n#include \"m_argv.h\"\n#include \"m_config.h\"\n#include \"m_misc.h\"\n#include \"tables.h\"\n#include \"v_video.h\"\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\n// Lookup table for mapping ASCII characters to their equivalent when\n// shift is pressed on an American layout keyboard:\n\nstatic const char shiftxform[] =\n{\n    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,\n    11, 12, 13, 14, 15, 16, 17, 18, 19, 20,\n    21, 22, 23, 24, 25, 26, 27, 28, 29, 30,\n    31, ' ', '!', '\"', '#', '$', '%', '&',\n    '\"', // shift-'\n    '(', ')', '*', '+',\n    '<', // shift-,\n    '_', // shift--\n    '>', // shift-.\n    '?', // shift-/\n    ')', // shift-0\n    '!', // shift-1\n    '@', // shift-2\n    '#', // shift-3\n    '$', // shift-4\n    '%', // shift-5\n    '^', // shift-6\n    '&', // shift-7\n    '*', // shift-8\n    '(', // shift-9\n    ':',\n    ':', // shift-;\n    '<',\n    '+', // shift-=\n    '>', '?', '@',\n    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',\n    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',\n    '[', // shift-[\n    '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK\n    ']', // shift-]\n    '\"', '_',\n    '\\'', // shift-`\n    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',\n    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',\n    '{', '|', '}', '~', 127\n};\n\n\n#define LOADING_DISK_W 16\n#define LOADING_DISK_H 16\n\n// Non aspect ratio-corrected modes (direct multiples of 320x200)\n\nstatic screen_mode_t *screen_modes[] = {\n    &mode_scale_1x,\n    &mode_scale_2x,\n    &mode_scale_3x,\n    &mode_scale_4x,\n    &mode_scale_5x,\n};\n\n// Aspect ratio corrected modes (4:3 ratio)\n\nstatic screen_mode_t *screen_modes_corrected[] = {\n\n    // Vertically stretched modes (320x200 -> 320x240 and multiples)\n\n    &mode_stretch_1x,\n    &mode_stretch_2x,\n    &mode_stretch_3x,\n    &mode_stretch_4x,\n    &mode_stretch_5x,\n\n    // Horizontally squashed modes (320x200 -> 256x200 and multiples)\n\n    &mode_squash_1x,\n    &mode_squash_2x,\n    &mode_squash_3x,\n    &mode_squash_4x,\n    &mode_squash_5x,\n};\n\n// SDL video driver name\n\nchar *video_driver = \"\";\n\n// Window position:\n\nstatic char *window_position = \"\";\n\n// SDL surface for the screen.\n\nstatic SDL_Surface *screen;\n\n// Window title\n\nstatic char *window_title = \"\";\n\n// Intermediate 8-bit buffer that we draw to instead of 'screen'.\n// This is used when we are rendering in 32-bit screen mode.\n// When in a real 8-bit screen mode, screenbuffer == screen.\n\nstatic SDL_Surface *screenbuffer = NULL;\n\n// palette\n\nstatic SDL_Color palette[256];\nstatic boolean palette_to_set;\n\n// display has been set up?\n\nstatic boolean initialized = false;\n\n// disable mouse?\n\nstatic boolean nomouse = false;\nint usemouse = 1;\n\n// Bit mask of mouse button state.\n\nstatic unsigned int mouse_button_state = 0;\n\n// Disallow mouse and joystick movement to cause forward/backward\n// motion.  Specified with the '-novert' command line parameter.\n// This is an int to allow saving to config file\n\nint novert = 0;\n\n// Save screenshots in PNG format.\n\nint png_screenshots = 0;\n\n// if true, I_VideoBuffer is screen->pixels\n\nstatic boolean native_surface;\n\n// Screen width and height, from configuration file.\n\nint screen_width = SCREENWIDTH;\nint screen_height = SCREENHEIGHT;\n\n// Color depth.\n\nint screen_bpp = 0;\n\n// Automatically adjust video settings if the selected mode is \n// not a valid video mode.\n\nstatic int autoadjust_video_settings = 1;\n\n// Run in full screen mode?  (int type for config code)\n\nint fullscreen = true;\n\n// Aspect ratio correction mode\n\nint aspect_ratio_correct = true;\n\n// Time to wait for the screen to settle on startup before starting the\n// game (ms)\n\nstatic int startup_delay = 1000;\n\n// Grab the mouse? (int type for config code)\n\nstatic int grabmouse = true;\n\n// The screen buffer; this is modified to draw things to the screen\n\nbyte *I_VideoBuffer = NULL;\n\n// If true, game is running as a screensaver\n\nboolean screensaver_mode = false;\n\n// Flag indicating whether the screen is currently visible:\n// when the screen isnt visible, don't render the screen\n\nboolean screenvisible;\n\n// If true, we display dots at the bottom of the screen to \n// indicate FPS.\n\nstatic boolean display_fps_dots;\n\n// If this is true, the screen is rendered but not blitted to the\n// video buffer.\n\nstatic boolean noblit;\n\n// Callback function to invoke to determine whether to grab the \n// mouse pointer.\n\nstatic grabmouse_callback_t grabmouse_callback = NULL;\n\n// disk image data and background overwritten by the disk to be\n// restored by EndRead\n\nstatic byte *disk_image = NULL;\nstatic byte *saved_background;\nstatic boolean window_focused;\n\n// Empty mouse cursor\n\nstatic SDL_Cursor *cursors[2];\n\n// The screen mode and scale functions being used\n\nstatic screen_mode_t *screen_mode;\n\n// Window resize state.\n\nstatic boolean need_resize = false;\nstatic unsigned int resize_w, resize_h;\nstatic unsigned int last_resize_time;\n\n// If true, keyboard mapping is ignored, like in Vanilla Doom.\n// The sensible thing to do is to disable this if you have a non-US\n// keyboard.\n\nint vanilla_keyboard_mapping = true;\n\n// Is the shift key currently down?\n\nstatic int shiftdown = 0;\n\n// Mouse acceleration\n//\n// This emulates some of the behavior of DOS mouse drivers by increasing\n// the speed when the mouse is moved fast.\n//\n// The mouse input values are input directly to the game, but when\n// the values exceed the value of mouse_threshold, they are multiplied\n// by mouse_acceleration to increase the speed.\n\nfloat mouse_acceleration = 2.0;\nint mouse_threshold = 10;\n\n// Gamma correction level to use\n\nint usegamma = 0;\n\nstatic void ApplyWindowResize(unsigned int w, unsigned int h);\n\nstatic boolean MouseShouldBeGrabbed()\n{\n    // never grab the mouse when in screensaver mode\n   \n    if (screensaver_mode)\n        return false;\n\n    // if the window doesn't have focus, never grab it\n\n    if (!window_focused)\n        return false;\n\n    // always grab the mouse when full screen (dont want to \n    // see the mouse pointer)\n\n    if (fullscreen)\n        return true;\n\n    // Don't grab the mouse if mouse input is disabled\n\n    if (!usemouse || nomouse)\n        return false;\n\n    // if we specify not to grab the mouse, never grab\n\n    if (!grabmouse)\n        return false;\n\n    // Invoke the grabmouse callback function to determine whether\n    // the mouse should be grabbed\n\n    if (grabmouse_callback != NULL)\n    {\n        return grabmouse_callback();\n    }\n    else\n    {\n        return true;\n    }\n}\n\nvoid I_SetGrabMouseCallback(grabmouse_callback_t func)\n{\n    grabmouse_callback = func;\n}\n\n// Set the variable controlling FPS dots.\n\nvoid I_DisplayFPSDots(boolean dots_on)\n{\n    display_fps_dots = dots_on;\n}\n\n// Update the value of window_focused when we get a focus event\n//\n// We try to make ourselves be well-behaved: the grab on the mouse\n// is removed if we lose focus (such as a popup window appearing),\n// and we dont move the mouse around if we aren't focused either.\n\nstatic void UpdateFocus(void)\n{\n    Uint8 state;\n\n    state = SDL_GetAppState();\n\n    // We should have input (keyboard) focus and be visible \n    // (not minimized)\n\n    window_focused = (state & SDL_APPINPUTFOCUS) && (state & SDL_APPACTIVE);\n\n    // Should the screen be grabbed?\n\n    screenvisible = (state & SDL_APPACTIVE) != 0;\n}\n\n// Show or hide the mouse cursor. We have to use different techniques\n// depending on the OS.\n\nstatic void SetShowCursor(boolean show)\n{\n    // On Windows, using SDL_ShowCursor() adds lag to the mouse input,\n    // so work around this by setting an invisible cursor instead. On\n    // other systems, it isn't possible to change the cursor, so this\n    // hack has to be Windows-only. (Thanks to entryway for this)\n\n#ifdef _WIN32\n    if (show)\n    {\n        SDL_SetCursor(cursors[1]);\n    }\n    else\n    {\n        SDL_SetCursor(cursors[0]);\n    }\n#else\n    SDL_ShowCursor(show);\n#endif\n\n    // When the cursor is hidden, grab the input.\n\n    if (!screensaver_mode)\n    {\n        SDL_WM_GrabInput(!show);\n    }\n}\n\nvoid I_EnableLoadingDisk(void)\n{\n    patch_t *disk;\n    byte *tmpbuf;\n    char *disk_name;\n    int y;\n    char buf[20];\n\n    SDL_VideoDriverName(buf, 15);\n\n    if (!strcmp(buf, \"Quartz\"))\n    {\n        // MacOS Quartz gives us pageflipped graphics that screw up the \n        // display when we use the loading disk.  Disable it.\n        // This is a gross hack.\n\n        return;\n    }\n\n    if (M_CheckParm(\"-cdrom\") > 0)\n        disk_name = DEH_String(\"STCDROM\");\n    else\n        disk_name = DEH_String(\"STDISK\");\n\n    disk = W_CacheLumpName(disk_name, PU_STATIC);\n\n    // Draw the patch into a temporary buffer\n\n    tmpbuf = Z_Malloc(SCREENWIDTH * (disk->height + 1), PU_STATIC, NULL);\n    V_UseBuffer(tmpbuf);\n\n    // Draw the disk to the screen:\n\n    V_DrawPatch(0, 0, disk);\n\n    disk_image = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H, PU_STATIC, NULL);\n    saved_background = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H, PU_STATIC, NULL);\n\n    for (y=0; y<LOADING_DISK_H; ++y) \n    {\n        memcpy(disk_image + LOADING_DISK_W * y,\n               tmpbuf + SCREENWIDTH * y,\n               LOADING_DISK_W);\n    }\n\n    // All done - free the screen buffer and restore the normal \n    // video buffer.\n\n    W_ReleaseLumpName(disk_name);\n    V_RestoreBuffer();\n    Z_Free(tmpbuf);\n}\n\n//\n// Translates the SDL key\n//\n\nstatic int TranslateKey(SDL_keysym *sym)\n{\n    switch(sym->sym)\n    {\n      case SDLK_LEFT:\treturn KEY_LEFTARROW;\n      case SDLK_RIGHT:\treturn KEY_RIGHTARROW;\n      case SDLK_DOWN:\treturn KEY_DOWNARROW;\n      case SDLK_UP:\treturn KEY_UPARROW;\n      case SDLK_ESCAPE:\treturn KEY_ESCAPE;\n      case SDLK_RETURN:\treturn KEY_ENTER;\n      case SDLK_TAB:\treturn KEY_TAB;\n      case SDLK_F1:\treturn KEY_F1;\n      case SDLK_F2:\treturn KEY_F2;\n      case SDLK_F3:\treturn KEY_F3;\n      case SDLK_F4:\treturn KEY_F4;\n      case SDLK_F5:\treturn KEY_F5;\n      case SDLK_F6:\treturn KEY_F6;\n      case SDLK_F7:\treturn KEY_F7;\n      case SDLK_F8:\treturn KEY_F8;\n      case SDLK_F9:\treturn KEY_F9;\n      case SDLK_F10:\treturn KEY_F10;\n      case SDLK_F11:\treturn KEY_F11;\n      case SDLK_F12:\treturn KEY_F12;\n      case SDLK_PRINT:  return KEY_PRTSCR;\n\n      case SDLK_BACKSPACE: return KEY_BACKSPACE;\n      case SDLK_DELETE:\treturn KEY_DEL;\n\n      case SDLK_PAUSE:\treturn KEY_PAUSE;\n\n#if !SDL_VERSION_ATLEAST(1, 3, 0)\n      case SDLK_EQUALS: return KEY_EQUALS;\n#endif\n\n      case SDLK_MINUS:          return KEY_MINUS;\n\n      case SDLK_LSHIFT:\n      case SDLK_RSHIFT:\n\treturn KEY_RSHIFT;\n\t\n      case SDLK_LCTRL:\n      case SDLK_RCTRL:\n\treturn KEY_RCTRL;\n\t\n      case SDLK_LALT:\n      case SDLK_RALT:\n#if !SDL_VERSION_ATLEAST(1, 3, 0)\n      case SDLK_LMETA:\n      case SDLK_RMETA:\n#endif\n        return KEY_RALT;\n\n      case SDLK_CAPSLOCK: return KEY_CAPSLOCK;\n      case SDLK_SCROLLOCK: return KEY_SCRLCK;\n      case SDLK_NUMLOCK: return KEY_NUMLOCK;\n\n      case SDLK_KP0: return KEYP_0;\n      case SDLK_KP1: return KEYP_1;\n      case SDLK_KP2: return KEYP_2;\n      case SDLK_KP3: return KEYP_3;\n      case SDLK_KP4: return KEYP_4;\n      case SDLK_KP5: return KEYP_5;\n      case SDLK_KP6: return KEYP_6;\n      case SDLK_KP7: return KEYP_7;\n      case SDLK_KP8: return KEYP_8;\n      case SDLK_KP9: return KEYP_9;\n\n      case SDLK_KP_PERIOD:   return KEYP_PERIOD;\n      case SDLK_KP_MULTIPLY: return KEYP_MULTIPLY;\n      case SDLK_KP_PLUS:     return KEYP_PLUS;\n      case SDLK_KP_MINUS:    return KEYP_MINUS;\n      case SDLK_KP_DIVIDE:   return KEYP_DIVIDE;\n      case SDLK_KP_EQUALS:   return KEYP_EQUALS;\n      case SDLK_KP_ENTER:    return KEYP_ENTER;\n\n      case SDLK_HOME: return KEY_HOME;\n      case SDLK_INSERT: return KEY_INS;\n      case SDLK_END: return KEY_END;\n      case SDLK_PAGEUP: return KEY_PGUP;\n      case SDLK_PAGEDOWN: return KEY_PGDN;\n\n#ifdef SDL_HAVE_APP_KEYS\n        case SDLK_APP1:        return KEY_F1;\n        case SDLK_APP2:        return KEY_F2;\n        case SDLK_APP3:        return KEY_F3;\n        case SDLK_APP4:        return KEY_F4;\n        case SDLK_APP5:        return KEY_F5;\n        case SDLK_APP6:        return KEY_F6;\n#endif\n\n      default:\n        return tolower(sym->sym);\n    }\n}\n\nvoid I_ShutdownGraphics(void)\n{\n    if (initialized)\n    {\n        SetShowCursor(true);\n\n        SDL_QuitSubSystem(SDL_INIT_VIDEO);\n\n        initialized = false;\n    }\n}\n\n\nvoid __attribute__ ((weak)) I_GetEvent(void)\n{\n\n}\n\n//\n// I_StartFrame\n//\nvoid I_StartFrame (void)\n{\n    I_GetEvent();\n}\n\nstatic void UpdateMouseButtonState(unsigned int button, boolean on)\n{\n    event_t event;\n\n    if (button < SDL_BUTTON_LEFT || button > MAX_MOUSE_BUTTONS)\n    {\n        return;\n    }\n\n    // Note: button \"0\" is left, button \"1\" is right,\n    // button \"2\" is middle for Doom.  This is different\n    // to how SDL sees things.\n\n    switch (button)\n    {\n        case SDL_BUTTON_LEFT:\n            button = 0;\n            break;\n\n        case SDL_BUTTON_RIGHT:\n            button = 1;\n            break;\n\n        case SDL_BUTTON_MIDDLE:\n            button = 2;\n            break;\n\n        default:\n            // SDL buttons are indexed from 1.\n            --button;\n            break;\n    }\n\n    // Turn bit representing this button on or off.\n\n    if (on)\n    {\n        mouse_button_state |= (1 << button);\n    }\n    else\n    {\n        mouse_button_state &= ~(1 << button);\n    }\n\n    // Post an event with the new button state.\n\n    event.type = ev_mouse;\n    event.data1 = mouse_button_state;\n    event.data2 = event.data3 = 0;\n    D_PostEvent(&event);\n}\n\nstatic int AccelerateMouse(int val)\n{\n    if (val < 0)\n        return -AccelerateMouse(-val);\n\n    if (val > mouse_threshold)\n    {\n        return (int)((val - mouse_threshold) * mouse_acceleration + mouse_threshold);\n    }\n    else\n    {\n        return val;\n    }\n}\n\n// Get the equivalent ASCII (Unicode?) character for a keypress.\n\nstatic int GetTypedChar(SDL_Event *event)\n{\n    int key;\n\n    // If Vanilla keyboard mapping enabled, the keyboard\n    // scan code is used to give the character typed.\n    // This does not change depending on keyboard layout.\n    // If you have a German keyboard, pressing 'z' will\n    // give 'y', for example.  It is desirable to be able\n    // to fix this so that people with non-standard \n    // keyboard mappings can type properly.  If vanilla\n    // mode is disabled, use the properly translated \n    // version.\n\n    if (vanilla_keyboard_mapping)\n    {\n        key = TranslateKey(&event->key.keysym);\n\n        // Is shift held down?  If so, perform a translation.\n\n        if (shiftdown > 0)\n        {\n            if (key >= 0 && key < arrlen(shiftxform))\n            {\n                key = shiftxform[key];\n            }\n            else\n            {\n                key = 0;\n            }\n        }\n\n        return key;\n    }\n    else\n    {\n        // Unicode value, from key layout.\n\n        return tolower(event->key.keysym.unicode);\n    }\n}\n\nstatic void UpdateShiftStatus(SDL_Event *event)\n{\n    int change;\n\n    if (event->type == SDL_KEYDOWN)\n    {\n        change = 1;\n    }\n    else if (event->type == SDL_KEYUP)\n    {\n        change = -1;\n    }\n    else\n    {\n        return;\n    }\n\n    if (event->key.keysym.sym == SDLK_LSHIFT \n     || event->key.keysym.sym == SDLK_RSHIFT)\n    {\n        shiftdown += change;\n    }\n}\n\nvoid I_GetEvent(void)\n{\n    SDL_Event sdlevent;\n    event_t event;\n\n    // possibly not needed\n    \n    SDL_PumpEvents();\n\n    // put event-grabbing stuff in here\n    \n    while (SDL_PollEvent(&sdlevent))\n    {\n        // ignore mouse events when the window is not focused\n\n        if (!window_focused \n         && (sdlevent.type == SDL_MOUSEMOTION\n          || sdlevent.type == SDL_MOUSEBUTTONDOWN\n          || sdlevent.type == SDL_MOUSEBUTTONUP))\n        {\n            continue;\n        }\n\n        if (screensaver_mode && sdlevent.type == SDL_QUIT)\n        {\n            I_Quit();\n        }\n\n        UpdateShiftStatus(&sdlevent);\n\n        // process event\n        \n        switch (sdlevent.type)\n        {\n            case SDL_KEYDOWN:\n                // data1 has the key pressed, data2 has the character\n                // (shift-translated, etc)\n                event.type = ev_keydown;\n                event.data1 = TranslateKey(&sdlevent.key.keysym);\n                event.data2 = GetTypedChar(&sdlevent);\n\n                if (event.data1 != 0)\n                {\n                    D_PostEvent(&event);\n                }\n                break;\n\n            case SDL_KEYUP:\n                event.type = ev_keyup;\n                event.data1 = TranslateKey(&sdlevent.key.keysym);\n\n                // data2 is just initialized to zero for ev_keyup.\n                // For ev_keydown it's the shifted Unicode character\n                // that was typed, but if something wants to detect\n                // key releases it should do so based on data1\n                // (key ID), not the printable char.\n\n                event.data2 = 0;\n\n                if (event.data1 != 0)\n                {\n                    D_PostEvent(&event);\n                }\n                break;\n\n                /*\n            case SDL_MOUSEMOTION:\n                event.type = ev_mouse;\n                event.data1 = mouse_button_state;\n                event.data2 = AccelerateMouse(sdlevent.motion.xrel);\n                event.data3 = -AccelerateMouse(sdlevent.motion.yrel);\n                D_PostEvent(&event);\n                break;\n                */\n\n            case SDL_MOUSEBUTTONDOWN:\n\t\tif (usemouse && !nomouse)\n\t\t{\n                    UpdateMouseButtonState(sdlevent.button.button, true);\n\t\t}\n                break;\n\n            case SDL_MOUSEBUTTONUP:\n\t\tif (usemouse && !nomouse)\n\t\t{\n                    UpdateMouseButtonState(sdlevent.button.button, false);\n\t\t}\n                break;\n\n            case SDL_QUIT:\n                event.type = ev_quit;\n                D_PostEvent(&event);\n                break;\n\n            case SDL_ACTIVEEVENT:\n                // need to update our focus state\n                UpdateFocus();\n                break;\n\n            case SDL_VIDEOEXPOSE:\n                palette_to_set = true;\n                break;\n\n            case SDL_RESIZABLE:\n                need_resize = true;\n                resize_w = sdlevent.resize.w;\n                resize_h = sdlevent.resize.h;\n                last_resize_time = SDL_GetTicks();\n                break;\n\n            default:\n                break;\n        }\n    }\n}\n\n// Warp the mouse back to the middle of the screen\n\nstatic void CenterMouse(void)\n{\n    // Warp the the screen center\n\n    SDL_WarpMouse(screen->w / 2, screen->h / 2);\n\n    // Clear any relative movement caused by warping\n\n    SDL_PumpEvents();\n#if SDL_VERSION_ATLEAST(1, 3, 0)\n    SDL_GetRelativeMouseState(0, NULL, NULL);\n#else\n    SDL_GetRelativeMouseState(NULL, NULL);\n#endif\n}\n\n//\n// Read the change in mouse state to generate mouse motion events\n//\n// This is to combine all mouse movement for a tic into one mouse\n// motion event.\n\nstatic void I_ReadMouse(void)\n{\n    int x, y;\n    event_t ev;\n\n#if SDL_VERSION_ATLEAST(1, 3, 0)\n    SDL_GetRelativeMouseState(0, &x, &y);\n#else\n    SDL_GetRelativeMouseState(&x, &y);\n#endif\n\n    if (x != 0 || y != 0) \n    {\n        ev.type = ev_mouse;\n        ev.data1 = mouse_button_state;\n        ev.data2 = AccelerateMouse(x);\n\n        if (!novert)\n        {\n            ev.data3 = -AccelerateMouse(y);\n        }\n        else\n        {\n            ev.data3 = 0;\n        }\n        \n        D_PostEvent(&ev);\n    }\n\n    if (MouseShouldBeGrabbed())\n    {\n        CenterMouse();\n    }\n}\n\n//\n// I_StartTic\n//\nvoid I_StartTic (void)\n{\n    if (!initialized)\n    {\n        return;\n    }\n\n    I_GetEvent();\n\n    if (usemouse && !nomouse)\n    {\n        I_ReadMouse();\n    }\n\n    I_UpdateJoystick();\n}\n\n\n//\n// I_UpdateNoBlit\n//\nvoid I_UpdateNoBlit (void)\n{\n    // what is this?\n}\n\nstatic void UpdateGrab(void)\n{\n    static boolean currently_grabbed = false;\n    boolean grab;\n\n    grab = MouseShouldBeGrabbed();\n\n    if (screensaver_mode)\n    {\n        // Hide the cursor in screensaver mode\n\n        SetShowCursor(false);\n    }\n    else if (grab && !currently_grabbed)\n    {\n        SetShowCursor(false);\n        CenterMouse();\n    }\n    else if (!grab && currently_grabbed)\n    {\n        SetShowCursor(true);\n\n        // When releasing the mouse from grab, warp the mouse cursor to\n        // the bottom-right of the screen. This is a minimally distracting\n        // place for it to appear - we may only have released the grab\n        // because we're at an end of level intermission screen, for\n        // example.\n\n        SDL_WarpMouse(screen->w - 16, screen->h - 16);\n        SDL_GetRelativeMouseState(NULL, NULL);\n    }\n\n    currently_grabbed = grab;\n\n}\n\n// Update a small portion of the screen\n//\n// Does stretching and buffer blitting if neccessary\n//\n// Return true if blit was successful.\n\nstatic boolean BlitArea(int x1, int y1, int x2, int y2)\n{\n    int x_offset, y_offset;\n    boolean result;\n\n    // No blit needed on native surface\n\n    if (native_surface)\n    {\n\treturn true;\n    }\n\n    x_offset = (screenbuffer->w - screen_mode->width) / 2;\n    y_offset = (screenbuffer->h - screen_mode->height) / 2;\n\n    if (SDL_LockSurface(screenbuffer) >= 0)\n    {\n        I_InitScale(I_VideoBuffer,\n                    (byte *) screenbuffer->pixels\n                                + (y_offset * screenbuffer->pitch)\n                                + x_offset,\n                    screenbuffer->pitch);\n        result = screen_mode->DrawScreen(x1, y1, x2, y2);\n      \tSDL_UnlockSurface(screenbuffer);\n    }\n    else\n    {\n        result = false;\n    }\n\n    return result;\n}\n\nstatic void UpdateRect(int x1, int y1, int x2, int y2)\n{\n    int x1_scaled, x2_scaled, y1_scaled, y2_scaled;\n\n    // Do stretching and blitting\n\n    if (BlitArea(x1, y1, x2, y2))\n    {\n        // Update the area\n\n        x1_scaled = (x1 * screen_mode->width) / SCREENWIDTH;\n        y1_scaled = (y1 * screen_mode->height) / SCREENHEIGHT;\n        x2_scaled = (x2 * screen_mode->width) / SCREENWIDTH;\n        y2_scaled = (y2 * screen_mode->height) / SCREENHEIGHT;\n\n        SDL_UpdateRect(screen,\n                       x1_scaled, y1_scaled,\n                       x2_scaled - x1_scaled,\n                       y2_scaled - y1_scaled);\n    }\n}\n\nvoid I_BeginRead(void)\n{\n    byte *screenloc = I_VideoBuffer\n                    + (SCREENHEIGHT - LOADING_DISK_H) * SCREENWIDTH\n                    + (SCREENWIDTH - LOADING_DISK_W);\n    int y;\n\n    if (!initialized || disk_image == NULL)\n        return;\n\n    // save background and copy the disk image in\n\n    for (y=0; y<LOADING_DISK_H; ++y)\n    {\n        memcpy(saved_background + y * LOADING_DISK_W,\n               screenloc,\n               LOADING_DISK_W);\n        memcpy(screenloc,\n               disk_image + y * LOADING_DISK_W,\n               LOADING_DISK_W);\n\n        screenloc += SCREENWIDTH;\n    }\n\n    UpdateRect(SCREENWIDTH - LOADING_DISK_W, SCREENHEIGHT - LOADING_DISK_H,\n               SCREENWIDTH, SCREENHEIGHT);\n}\n\nvoid I_EndRead(void)\n{\n    byte *screenloc = I_VideoBuffer\n                    + (SCREENHEIGHT - LOADING_DISK_H) * SCREENWIDTH\n                    + (SCREENWIDTH - LOADING_DISK_W);\n    int y;\n\n    if (!initialized || disk_image == NULL)\n        return;\n\n    // save background and copy the disk image in\n\n    for (y=0; y<LOADING_DISK_H; ++y)\n    {\n        memcpy(screenloc,\n               saved_background + y * LOADING_DISK_W,\n               LOADING_DISK_W);\n\n        screenloc += SCREENWIDTH;\n    }\n\n    UpdateRect(SCREENWIDTH - LOADING_DISK_W, SCREENHEIGHT - LOADING_DISK_H,\n               SCREENWIDTH, SCREENHEIGHT);\n}\n\n//\n// I_FinishUpdate\n//\nvoid I_FinishUpdate (void)\n{\n    static int\tlasttic;\n    int\t\ttics;\n    int\t\ti;\n\n    if (!initialized)\n        return;\n\n    if (noblit)\n        return;\n\n    if (need_resize && SDL_GetTicks() > last_resize_time + 500)\n    {\n        ApplyWindowResize(resize_w, resize_h);\n        need_resize = false;\n        palette_to_set = true;\n    }\n\n    UpdateGrab();\n\n    // Don't update the screen if the window isn't visible.\n    // Not doing this breaks under Windows when we alt-tab away \n    // while fullscreen.\n\n    if (!(SDL_GetAppState() & SDL_APPACTIVE))\n        return;\n\n    // draws little dots on the bottom of the screen\n\n    if (display_fps_dots)\n    {\n\ti = I_GetTime();\n\ttics = i - lasttic;\n\tlasttic = i;\n\tif (tics > 20) tics = 20;\n\n\tfor (i=0 ; i<tics*4 ; i+=4)\n\t    I_VideoBuffer[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0xff;\n\tfor ( ; i<20*4 ; i+=4)\n\t    I_VideoBuffer[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0;\n    }\n\n    // draw to screen\n\n    BlitArea(0, 0, SCREENWIDTH, SCREENHEIGHT);\n\n    if (palette_to_set)\n    {\n        SDL_SetColors(screenbuffer, palette, 0, 256);\n        palette_to_set = false;\n\n        // In native 8-bit mode, if we have a palette to set, the act\n        // of setting the palette updates the screen\n\n        if (screenbuffer == screen)\n        {\n            return;\n        }\n    }\n\n    // In 8in32 mode, we must blit from the fake 8-bit screen buffer\n    // to the real screen before doing a screen flip.\n\n    if (screenbuffer != screen)\n    {\n        SDL_Rect dst_rect;\n\n        // Center the buffer within the full screen space.\n\n        dst_rect.x = (screen->w - screenbuffer->w) / 2;\n        dst_rect.y = (screen->h - screenbuffer->h) / 2;\n\n        SDL_BlitSurface(screenbuffer, NULL, screen, &dst_rect);\n    }\n\n    SDL_Flip(screen);\n}\n\n\n//\n// I_ReadScreen\n//\nvoid I_ReadScreen (byte* scr)\n{\n    memcpy(scr, I_VideoBuffer, SCREENWIDTH*SCREENHEIGHT);\n}\n\n\n//\n// I_SetPalette\n//\nvoid I_SetPalette (byte *doompalette)\n{\n    int i;\n\n    for (i=0; i<256; ++i)\n    {\n        // Zero out the bottom two bits of each channel - the PC VGA\n        // controller only supports 6 bits of accuracy.\n\n        palette[i].r = gammatable[usegamma][*doompalette++] & ~3;\n        palette[i].g = gammatable[usegamma][*doompalette++] & ~3;\n        palette[i].b = gammatable[usegamma][*doompalette++] & ~3;\n    }\n\n    palette_to_set = true;\n}\n\n// Given an RGB value, find the closest matching palette index.\n\nint I_GetPaletteIndex(int r, int g, int b)\n{\n    int best, best_diff, diff;\n    int i;\n\n    best = 0; best_diff = INT_MAX;\n\n    for (i = 0; i < 256; ++i)\n    {\n        diff = (r - palette[i].r) * (r - palette[i].r)\n             + (g - palette[i].g) * (g - palette[i].g)\n             + (b - palette[i].b) * (b - palette[i].b);\n\n        if (diff < best_diff)\n        {\n            best = i;\n            best_diff = diff;\n        }\n\n        if (diff == 0)\n        {\n            break;\n        }\n    }\n\n    return best;\n}\n\n// \n// Set the window title\n//\n\nvoid I_SetWindowTitle(char *title)\n{\n    window_title = title;\n}\n\n//\n// Call the SDL function to set the window title, based on \n// the title set with I_SetWindowTitle.\n//\n\nvoid I_InitWindowTitle(void)\n{\n    char *buf;\n\n    buf = M_StringJoin(window_title, \" - \", PACKAGE_STRING, NULL);\n    SDL_WM_SetCaption(buf, NULL);\n    free(buf);\n}\n\n// Set the application icon\n\nvoid I_InitWindowIcon(void)\n{\n    SDL_Surface *surface;\n    Uint8 *mask;\n    int i;\n\n    // Generate the mask\n\n    mask = malloc(icon_w * icon_h / 8);\n    memset(mask, 0, icon_w * icon_h / 8);\n\n    for (i=0; i<icon_w * icon_h; ++i)\n    {\n        if (icon_data[i * 3] != 0x00\n         || icon_data[i * 3 + 1] != 0x00\n         || icon_data[i * 3 + 2] != 0x00)\n        {\n            mask[i / 8] |= 1 << (7 - i % 8);\n        }\n    }\n\n    surface = SDL_CreateRGBSurfaceFrom(icon_data,\n                                       icon_w,\n                                       icon_h,\n                                       24,\n                                       icon_w * 3,\n                                       0xff << 0,\n                                       0xff << 8,\n                                       0xff << 16,\n                                       0);\n\n    SDL_WM_SetIcon(surface, mask);\n    SDL_FreeSurface(surface);\n    free(mask);\n}\n\n// Pick the modes list to use:\n\nstatic void GetScreenModes(screen_mode_t ***modes_list, int *num_modes)\n{\n    if (aspect_ratio_correct)\n    {\n        *modes_list = screen_modes_corrected;\n        *num_modes = arrlen(screen_modes_corrected);\n    }\n    else\n    {\n        *modes_list = screen_modes;\n        *num_modes = arrlen(screen_modes);\n    }\n}\n\n// Find which screen_mode_t to use for the given width and height.\n\nstatic screen_mode_t *I_FindScreenMode(int w, int h)\n{\n    screen_mode_t **modes_list;\n    screen_mode_t *best_mode;\n    int modes_list_length;\n    int num_pixels;\n    int best_num_pixels;\n    int i;\n\n    // Special case: 320x200 and 640x400 are available even if aspect \n    // ratio correction is turned on.  These modes have non-square\n    // pixels.\n\n    if (fullscreen)\n    {\n        if (w == SCREENWIDTH && h == SCREENHEIGHT)\n        {\n            return &mode_scale_1x;\n        }\n        else if (w == SCREENWIDTH*2 && h == SCREENHEIGHT*2)\n        {\n            return &mode_scale_2x;\n        }\n    }\n\n    GetScreenModes(&modes_list, &modes_list_length);\n\n    // Find the biggest screen_mode_t in the list that fits within these \n    // dimensions\n\n    best_mode = NULL;\n    best_num_pixels = 0;\n\n    for (i=0; i<modes_list_length; ++i) \n    {\n        // Will this fit within the dimensions? If not, ignore.\n\n        if (modes_list[i]->width > w || modes_list[i]->height > h)\n        {\n            continue;\n        }\n\n        num_pixels = modes_list[i]->width * modes_list[i]->height;\n\n        if (num_pixels > best_num_pixels)\n        {\n            // This is a better mode than the current one\n\n            best_mode = modes_list[i];\n            best_num_pixels = num_pixels;\n        }\n    }\n\n    return best_mode;\n}\n\n// Adjust to an appropriate fullscreen mode.\n// Returns true if successful.\n\nstatic boolean AutoAdjustFullscreen(void)\n{\n    SDL_Rect **modes;\n    SDL_Rect *best_mode;\n    screen_mode_t *screen_mode;\n    int diff, best_diff;\n    int i;\n\n    modes = SDL_ListModes(NULL, SDL_FULLSCREEN);\n\n    // No fullscreen modes available at all?\n\n    if (modes == NULL || modes == (SDL_Rect **) -1 || *modes == NULL)\n    {\n        return false;\n    }\n\n    // Find the best mode that matches the mode specified in the\n    // configuration file\n\n    best_mode = NULL;\n    best_diff = INT_MAX;\n\n    for (i=0; modes[i] != NULL; ++i)\n    {\n        //printf(\"%ix%i?\\n\", modes[i]->w, modes[i]->h);\n\n        // What screen_mode_t would be used for this video mode?\n\n        screen_mode = I_FindScreenMode(modes[i]->w, modes[i]->h);\n\n        // Never choose a screen mode that we cannot run in, or\n        // is poor quality for fullscreen\n\n        if (screen_mode == NULL || screen_mode->poor_quality)\n        {\n        //    printf(\"\\tUnsupported / poor quality\\n\");\n            continue;\n        }\n\n        // Do we have the exact mode?\n        // If so, no autoadjust needed\n\n        if (screen_width == modes[i]->w && screen_height == modes[i]->h)\n        {\n        //    printf(\"\\tExact mode!\\n\");\n            return true;\n        }\n\n        // Is this mode better than the current mode?\n\n        diff = (screen_width - modes[i]->w) * (screen_width - modes[i]->w)\n             + (screen_height - modes[i]->h) * (screen_height - modes[i]->h);\n\n        if (diff < best_diff)\n        {\n        //    printf(\"\\tA valid mode\\n\");\n            best_mode = modes[i];\n            best_diff = diff;\n        }\n    }\n\n    if (best_mode == NULL)\n    {\n        // Unable to find a valid mode!\n\n        return false;\n    }\n\n    printf(\"I_InitGraphics: %ix%i mode not supported on this machine.\\n\",\n           screen_width, screen_height);\n\n    screen_width = best_mode->w;\n    screen_height = best_mode->h;\n\n    return true;\n}\n\n// Auto-adjust to a valid windowed mode.\n\nstatic void AutoAdjustWindowed(void)\n{\n    screen_mode_t *best_mode;\n\n    // Find a screen_mode_t to fit within the current settings\n\n    best_mode = I_FindScreenMode(screen_width, screen_height);\n\n    if (best_mode == NULL)\n    {\n        // Nothing fits within the current settings.\n        // Pick the closest to 320x200 possible.\n\n        best_mode = I_FindScreenMode(SCREENWIDTH, SCREENHEIGHT_4_3);\n    }\n\n    // Switch to the best mode if necessary.\n\n    if (best_mode->width != screen_width || best_mode->height != screen_height)\n    {\n        printf(\"I_InitGraphics: Cannot run at specified mode: %ix%i\\n\",\n               screen_width, screen_height);\n\n        screen_width = best_mode->width;\n        screen_height = best_mode->height;\n    }\n}\n\n// Auto-adjust to a valid color depth.\n\nstatic void AutoAdjustColorDepth(void)\n{\n    SDL_Rect **modes;\n    SDL_PixelFormat format;\n    const SDL_VideoInfo *info;\n    int flags;\n\n    // If screen_bpp=0, we should use the current (default) pixel depth.\n    // Fetch it from SDL.\n\n    if (screen_bpp == 0)\n    {\n        info = SDL_GetVideoInfo();\n\n        if (info != NULL && info->vfmt != NULL)\n        {\n            screen_bpp = info->vfmt->BitsPerPixel;\n        }\n    }\n\n    if (fullscreen)\n    {\n        flags = SDL_FULLSCREEN;\n    }\n    else\n    {\n        flags = 0;\n    }\n\n    format.BitsPerPixel = screen_bpp;\n    format.BytesPerPixel = (screen_bpp + 7) / 8;\n\n    // Are any screen modes supported at the configured color depth?\n\n    modes = SDL_ListModes(&format, flags);\n\n    // If not, we must autoadjust to something sensible.\n\n    if (modes == NULL)\n    {\n        printf(\"I_InitGraphics: %ibpp color depth not supported.\\n\",\n               screen_bpp);\n\n        info = SDL_GetVideoInfo();\n\n        if (info != NULL && info->vfmt != NULL)\n        {\n            screen_bpp = info->vfmt->BitsPerPixel;\n        }\n    }\n}\n\n// If the video mode set in the configuration file is not available,\n// try to choose a different mode.\n\nstatic void I_AutoAdjustSettings(void)\n{\n    int old_screen_w, old_screen_h, old_screen_bpp;\n\n    old_screen_w = screen_width;\n    old_screen_h = screen_height;\n    old_screen_bpp = screen_bpp;\n\n    // Possibly adjust color depth.\n\n    AutoAdjustColorDepth();\n\n    // If we are running fullscreen, try to autoadjust to a valid fullscreen\n    // mode.  If this is impossible, switch to windowed.\n\n    if (fullscreen && !AutoAdjustFullscreen())\n    {\n        fullscreen = 0;\n    }\n\n    // If we are running windowed, pick a valid window size.\n\n    if (!fullscreen)\n    {\n        AutoAdjustWindowed();\n    }\n\n    // Have the settings changed?  Show a message.\n\n    if (screen_width != old_screen_w || screen_height != old_screen_h\n     || screen_bpp != old_screen_bpp)\n    {\n        printf(\"I_InitGraphics: Auto-adjusted to %ix%ix%ibpp.\\n\",\n               screen_width, screen_height, screen_bpp);\n\n        printf(\"NOTE: Your video settings have been adjusted.  \"\n               \"To disable this behavior,\\n\"\n               \"set autoadjust_video_settings to 0 in your \"\n               \"configuration file.\\n\");\n    }\n}\n\n// Set video size to a particular scale factor (1x, 2x, 3x, etc.)\n\nstatic void SetScaleFactor(int factor)\n{\n    int w, h;\n\n    // Pick 320x200 or 320x240, depending on aspect ratio correct\n\n    if (aspect_ratio_correct)\n    {\n        w = SCREENWIDTH;\n        h = SCREENHEIGHT_4_3;\n    }\n    else\n    {\n        w = SCREENWIDTH;\n        h = SCREENHEIGHT;\n    }\n\n    screen_width = w * factor;\n    screen_height = h * factor;\n}\n\nvoid I_GraphicsCheckCommandLine(void)\n{\n    int i;\n\n    //!\n    // @vanilla\n    //\n    // Disable blitting the screen.\n    //\n\n    noblit = M_CheckParm (\"-noblit\"); \n\n    //!\n    // @category video \n    //\n    // Grab the mouse when running in windowed mode.\n    //\n\n    if (M_CheckParm(\"-grabmouse\"))\n    {\n        grabmouse = true;\n    }\n\n    //!\n    // @category video \n    //\n    // Don't grab the mouse when running in windowed mode.\n    //\n\n    if (M_CheckParm(\"-nograbmouse\"))\n    {\n        grabmouse = false;\n    }\n\n    // default to fullscreen mode, allow override with command line\n    // nofullscreen because we love prboom\n\n    //!\n    // @category video \n    //\n    // Run in a window.\n    //\n\n    if (M_CheckParm(\"-window\") || M_CheckParm(\"-nofullscreen\"))\n    {\n        fullscreen = false;\n    }\n\n    //!\n    // @category video \n    //\n    // Run in fullscreen mode.\n    //\n\n    if (M_CheckParm(\"-fullscreen\"))\n    {\n        fullscreen = true;\n    }\n\n    //!\n    // @category video \n    //\n    // Disable the mouse.\n    //\n\n    nomouse = M_CheckParm(\"-nomouse\") > 0;\n\n    //!\n    // @category video\n    // @arg <x>\n    //\n    // Specify the screen width, in pixels.\n    //\n\n    i = M_CheckParmWithArgs(\"-width\", 1);\n\n    if (i > 0)\n    {\n        screen_width = atoi(myargv[i + 1]);\n    }\n\n    //!\n    // @category video\n    // @arg <y>\n    //\n    // Specify the screen height, in pixels.\n    //\n\n    i = M_CheckParmWithArgs(\"-height\", 1);\n\n    if (i > 0)\n    {\n        screen_height = atoi(myargv[i + 1]);\n    }\n\n    //!\n    // @category video\n    // @arg <bpp>\n    //\n    // Specify the color depth of the screen, in bits per pixel.\n    //\n\n    i = M_CheckParmWithArgs(\"-bpp\", 1);\n\n    if (i > 0)\n    {\n        screen_bpp = atoi(myargv[i + 1]);\n    }\n\n    // Because we love Eternity:\n\n    //!\n    // @category video\n    //\n    // Set the color depth of the screen to 32 bits per pixel.\n    //\n\n    if (M_CheckParm(\"-8in32\"))\n    {\n        screen_bpp = 32;\n    }\n\n    //!\n    // @category video\n    // @arg <WxY>[wf]\n    //\n    // Specify the dimensions of the window or fullscreen mode.  An\n    // optional letter of w or f appended to the dimensions selects\n    // windowed or fullscreen mode.\n\n    i = M_CheckParmWithArgs(\"-geometry\", 1);\n\n    if (i > 0)\n    {\n        int w, h, s;\n        char f;\n\n        s = sscanf(myargv[i + 1], \"%ix%i%1c\", &w, &h, &f);\n        if (s == 2 || s == 3)\n        {\n            screen_width = w;\n            screen_height = h;\n\n            if (s == 3 && f == 'f')\n            {\n                fullscreen = true;\n            }\n            else if (s == 3 && f == 'w')\n            {\n                fullscreen = false;\n            }\n        }\n    }\n\n    //!\n    // @category video\n    //\n    // Don't scale up the screen.\n    //\n\n    if (M_CheckParm(\"-1\")) \n    {\n        SetScaleFactor(1);\n    }\n\n    //!\n    // @category video\n    //\n    // Double up the screen to 2x its normal size.\n    //\n\n    if (M_CheckParm(\"-2\")) \n    {\n        SetScaleFactor(2);\n    }\n\n    //!\n    // @category video\n    //\n    // Double up the screen to 3x its normal size.\n    //\n\n    if (M_CheckParm(\"-3\")) \n    {\n        SetScaleFactor(3);\n    }\n\n    //!\n    // @category video\n    //\n    // Disable vertical mouse movement.\n    //\n\n    if (M_CheckParm(\"-novert\"))\n    {\n        novert = true;\n    }\n\n    //!\n    // @category video\n    //\n    // Enable vertical mouse movement.\n    //\n\n    if (M_CheckParm(\"-nonovert\"))\n    {\n        novert = false;\n    }\n}\n\n// Check if we have been invoked as a screensaver by xscreensaver.\n\nvoid I_CheckIsScreensaver(void)\n{\n    char *env;\n\n    env = getenv(\"XSCREENSAVER_WINDOW\");\n\n    if (env != NULL)\n    {\n        screensaver_mode = true;\n    }\n}\n\nstatic void CreateCursors(void)\n{\n    static Uint8 empty_cursor_data = 0;\n\n    // Save the default cursor so it can be recalled later\n\n    cursors[1] = SDL_GetCursor();\n\n    // Create an empty cursor\n\n    cursors[0] = SDL_CreateCursor(&empty_cursor_data,\n                                  &empty_cursor_data,\n                                  1, 1, 0, 0);\n}\n\nstatic void SetSDLVideoDriver(void)\n{\n    // Allow a default value for the SDL video driver to be specified\n    // in the configuration file.\n\n    if (strcmp(video_driver, \"\") != 0)\n    {\n        char *env_string;\n\n        env_string = M_StringJoin(\"SDL_VIDEODRIVER=\", video_driver, NULL);\n        putenv(env_string);\n        free(env_string);\n    }\n}\n\nstatic void SetWindowPositionVars(void)\n{\n    char buf[64];\n    int x, y;\n\n    if (window_position == NULL || !strcmp(window_position, \"\"))\n    {\n        return;\n    }\n\n    if (!strcmp(window_position, \"center\"))\n    {\n        putenv(\"SDL_VIDEO_CENTERED=1\");\n    }\n    else if (sscanf(window_position, \"%i,%i\", &x, &y) == 2)\n    {\n        M_snprintf(buf, sizeof(buf), \"SDL_VIDEO_WINDOW_POS=%i,%i\", x, y);\n        putenv(buf);\n    }\n}\n\nstatic char *WindowBoxType(screen_mode_t *mode, int w, int h)\n{\n    if (mode->width != w && mode->height != h) \n    {\n        return \"Windowboxed\";\n    }\n    else if (mode->width == w) \n    {\n        return \"Letterboxed\";\n    }\n    else if (mode->height == h)\n    {\n        return \"Pillarboxed\";\n    }\n    else\n    {\n        return \"...\";\n    }\n}\n\nstatic void SetVideoMode(screen_mode_t *mode, int w, int h)\n{\n    byte *doompal;\n    int flags = 0;\n\n    doompal = W_CacheLumpName(DEH_String(\"PLAYPAL\"), PU_CACHE);\n\n    // If we are already running and in a true color mode, we need\n    // to free the screenbuffer surface before setting the new mode.\n\n    if (screenbuffer != NULL && screen != screenbuffer)\n    {\n        SDL_FreeSurface(screenbuffer);\n    }\n\n    // Generate lookup tables before setting the video mode.\n\n    if (mode != NULL && mode->InitMode != NULL)\n    {\n        mode->InitMode(doompal);\n    }\n\n    // Set the video mode.\n\n    flags |= SDL_SWSURFACE | SDL_DOUBLEBUF;\n\n    if (screen_bpp == 8)\n    {\n        flags |= SDL_HWPALETTE;\n    }\n\n    if (fullscreen)\n    {\n        flags |= SDL_FULLSCREEN;\n    }\n    else\n    {\n        // In windowed mode, the window can be resized while the game is\n        // running.  This feature is disabled on OS X, as it adds an ugly\n        // scroll handle to the corner of the screen.\n\n#ifndef __MACOSX__\n        flags |= SDL_RESIZABLE;\n#endif\n    }\n\n    screen = SDL_SetVideoMode(w, h, screen_bpp, flags);\n\n    if (screen == NULL)\n    {\n        I_Error(\"Error setting video mode %ix%ix%ibpp: %s\\n\",\n                w, h, screen_bpp, SDL_GetError());\n    }\n\n    // Blank out the full screen area in case there is any junk in\n    // the borders that won't otherwise be overwritten.\n\n    SDL_FillRect(screen, NULL, 0);\n\n    // If mode was not set, it must be set now that we know the\n    // screen size.\n\n    if (mode == NULL)\n    {\n        mode = I_FindScreenMode(screen->w, screen->h);\n\n        if (mode == NULL)\n        {\n            I_Error(\"I_InitGraphics: Unable to find a screen mode small \"\n                    \"enough for %ix%i\", screen->w, screen->h);\n        }\n\n        // Generate lookup tables before setting the video mode.\n\n        if (mode->InitMode != NULL)\n        {\n            mode->InitMode(doompal);\n        }\n    }\n\n    // Create the screenbuffer surface; if we have a real 8-bit palettized\n    // screen, then we can use the screen as the screenbuffer.\n\n    if (screen->format->BitsPerPixel == 8)\n    {\n        screenbuffer = screen;\n    }\n    else\n    {\n        screenbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE,\n                                            mode->width, mode->height, 8,\n                                            0, 0, 0, 0);\n\n        SDL_FillRect(screenbuffer, NULL, 0);\n    }\n\n    // Save screen mode.\n\n    screen_mode = mode;\n}\n\nstatic void ApplyWindowResize(unsigned int w, unsigned int h)\n{\n    screen_mode_t *mode;\n\n    // Find the biggest screen mode that will fall within these\n    // dimensions, falling back to the smallest mode possible if\n    // none is found.\n\n    mode = I_FindScreenMode(w, h);\n\n    if (mode == NULL)\n    {\n        mode = I_FindScreenMode(SCREENWIDTH, SCREENHEIGHT);\n    }\n\n    // Reset mode to resize window.\n\n    printf(\"Resize to %ix%i\\n\", mode->width, mode->height);\n    SetVideoMode(mode, mode->width, mode->height);\n\n    // Save settings.\n\n    screen_width = mode->width;\n    screen_height = mode->height;\n}\n\nvoid I_InitGraphics(void)\n{\n    SDL_Event dummy;\n    byte *doompal;\n    char *env;\n\n    // Pass through the XSCREENSAVER_WINDOW environment variable to \n    // SDL_WINDOWID, to embed the SDL window into the Xscreensaver\n    // window.\n\n    env = getenv(\"XSCREENSAVER_WINDOW\");\n\n    if (env != NULL)\n    {\n        char winenv[30];\n        int winid;\n\n        sscanf(env, \"0x%x\", &winid);\n        M_snprintf(winenv, sizeof(winenv), \"SDL_WINDOWID=%i\", winid);\n\n        putenv(winenv);\n    }\n\n    SetSDLVideoDriver();\n    SetWindowPositionVars();\n\n    if (SDL_Init(SDL_INIT_VIDEO) < 0) \n    {\n        I_Error(\"Failed to initialize video: %s\", SDL_GetError());\n    }\n\n    // Set up title and icon.  Windows cares about the ordering; this\n    // has to be done before the call to SDL_SetVideoMode.\n\n    I_InitWindowTitle();\n#if !SDL_VERSION_ATLEAST(1, 3, 0)\n    I_InitWindowIcon();\n#endif\n\n    // Warning to OS X users... though they might never see it :(\n#ifdef __MACOSX__\n    if (fullscreen)\n    {\n        printf(\"Some old versions of OS X might crash in fullscreen mode.\\n\"\n               \"If this happens to you, switch back to windowed mode.\\n\");\n    }\n#endif\n\n    //\n    // Enter into graphics mode.\n    //\n    // When in screensaver mode, run full screen and auto detect\n    // screen dimensions (don't change video mode)\n    //\n\n    if (screensaver_mode)\n    {\n        SetVideoMode(NULL, 0, 0);\n    }\n    else\n    {\n        int w, h;\n\n        if (autoadjust_video_settings)\n        {\n            I_AutoAdjustSettings();\n        }\n\n        w = screen_width;\n        h = screen_height;\n\n        screen_mode = I_FindScreenMode(w, h);\n\n        if (screen_mode == NULL)\n        {\n            I_Error(\"I_InitGraphics: Unable to find a screen mode small \"\n                    \"enough for %ix%i\", w, h);\n        }\n\n        if (w != screen_mode->width || h != screen_mode->height)\n        {\n            printf(\"I_InitGraphics: %s (%ix%i within %ix%i)\\n\",\n                   WindowBoxType(screen_mode, w, h),\n                   screen_mode->width, screen_mode->height, w, h);\n        }\n\n        SetVideoMode(screen_mode, w, h);\n    }\n\n    // Start with a clear black screen\n    // (screen will be flipped after we set the palette)\n\n    SDL_FillRect(screenbuffer, NULL, 0);\n\n    // Set the palette\n\n    doompal = W_CacheLumpName(DEH_String(\"PLAYPAL\"), PU_CACHE);\n    I_SetPalette(doompal);\n    SDL_SetColors(screenbuffer, palette, 0, 256);\n\n    CreateCursors();\n\n    UpdateFocus();\n    UpdateGrab();\n\n    // On some systems, it takes a second or so for the screen to settle\n    // after changing modes.  We include the option to add a delay when\n    // setting the screen mode, so that the game doesn't start immediately\n    // with the player unable to see anything.\n\n    if (fullscreen && !screensaver_mode)\n    {\n        SDL_Delay(startup_delay);\n    }\n\n    // Check if we have a native surface we can use\n    // If we have to lock the screen, draw to a buffer and copy\n    // Likewise if the screen pitch is not the same as the width\n    // If we have to multiply, drawing is done to a separate 320x200 buf\n\n    native_surface = screen == screenbuffer\n                  && !SDL_MUSTLOCK(screen)\n                  && screen_mode == &mode_scale_1x\n                  && screen->pitch == SCREENWIDTH\n                  && aspect_ratio_correct;\n\n    // If not, allocate a buffer and copy from that buffer to the\n    // screen when we do an update\n\n    if (native_surface)\n    {\n\tI_VideoBuffer = (unsigned char *) screen->pixels;\n\n        I_VideoBuffer += (screen->h - SCREENHEIGHT) / 2;\n    }\n    else\n    {\n\tI_VideoBuffer = (unsigned char *) Z_Malloc (SCREENWIDTH * SCREENHEIGHT, \n                                                    PU_STATIC, NULL);\n    }\n\n    V_RestoreBuffer();\n\n    // Clear the screen to black.\n\n    memset(I_VideoBuffer, 0, SCREENWIDTH * SCREENHEIGHT);\n\n    // We need SDL to give us translated versions of keys as well\n\n    SDL_EnableUNICODE(1);\n\n    // Repeat key presses - this is what Vanilla Doom does\n    // Not sure about repeat rate - probably dependent on which DOS\n    // driver is used.  This is good enough though.\n\n    SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);\n\n    // clear out any events waiting at the start and center the mouse\n  \n    while (SDL_PollEvent(&dummy));\n\n    initialized = true;\n\n    // Call I_ShutdownGraphics on quit\n\n    I_AtExit(I_ShutdownGraphics, true);\n}\n\n// Bind all variables controlling video options into the configuration\n// file system.\n\nvoid I_BindVideoVariables(void)\n{\n    M_BindVariable(\"use_mouse\",                 &usemouse);\n    M_BindVariable(\"autoadjust_video_settings\", &autoadjust_video_settings);\n    M_BindVariable(\"fullscreen\",                &fullscreen);\n    M_BindVariable(\"aspect_ratio_correct\",      &aspect_ratio_correct);\n    M_BindVariable(\"startup_delay\",             &startup_delay);\n    M_BindVariable(\"screen_width\",              &screen_width);\n    M_BindVariable(\"screen_height\",             &screen_height);\n    M_BindVariable(\"screen_bpp\",                &screen_bpp);\n    M_BindVariable(\"grabmouse\",                 &grabmouse);\n    M_BindVariable(\"mouse_acceleration\",        &mouse_acceleration);\n    M_BindVariable(\"mouse_threshold\",           &mouse_threshold);\n    M_BindVariable(\"video_driver\",              &video_driver);\n    M_BindVariable(\"window_position\",           &window_position);\n    M_BindVariable(\"usegamma\",                  &usegamma);\n    M_BindVariable(\"vanilla_keyboard_mapping\",  &vanilla_keyboard_mapping);\n    M_BindVariable(\"novert\",                    &novert);\n    M_BindVariable(\"png_screenshots\",           &png_screenshots);\n\n    // Windows Vista or later?  Set screen color depth to\n    // 32 bits per pixel, as 8-bit palettized screen modes\n    // don't work properly in recent versions.\n\n#if defined(_WIN32) && !defined(_WIN32_WCE)\n    {\n        OSVERSIONINFOEX version_info;\n\n        ZeroMemory(&version_info, sizeof(OSVERSIONINFOEX));\n        version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\n\n        GetVersionEx((OSVERSIONINFO *) &version_info);\n\n        if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT\n         && version_info.dwMajorVersion >= 6)\n        {\n            screen_bpp = 32;\n        }\n    }\n#endif\n\n    // Disable fullscreen by default on OS X, as there is an SDL bug\n    // where some old versions of OS X (<= Snow Leopard) crash.\n\n#ifdef __MACOSX__\n    fullscreen = 0;\n    screen_width = 800;\n    screen_height = 600;\n#endif\n}\n"
  },
  {
    "path": "fbdoom/i_video.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tSystem specific interface stuff.\n//\n\n\n#ifndef __I_VIDEO__\n#define __I_VIDEO__\n\n#include \"doomtype.h\"\n\n// Screen width and height.\n\n#define SCREENWIDTH  320\n#define SCREENHEIGHT 200\n\n// Screen width used for \"squash\" scale functions\n\n#define SCREENWIDTH_4_3 256\n\n// Screen height used for \"stretch\" scale functions.\n\n#define SCREENHEIGHT_4_3 240\n\n#define MAX_MOUSE_BUTTONS 8\n\ntypedef struct\n{\n    // Screen width and height\n\n    int width;\n    int height;\n\n    // Initialisation function to call when using this mode.\n    // Called with a pointer to the Doom palette.\n    //\n    // If NULL, no init function is called.\n\n    void (*InitMode)(byte *palette);\n    \n    // Function to call to draw the screen from the source buffer.\n    // Return true if draw was successful.\n\n    boolean (*DrawScreen)(int x1, int y1, int x2, int y2);\n\n    // If true, this is a \"poor quality\" mode.  The autoadjust\n    // code should always attempt to use a different mode to this\n    // mode in fullscreen.\n    //\n    // Some notes about what \"poor quality\" means in this context:\n    //\n    // The aspect ratio correction works by scaling up to the larger\n    // screen size and then drawing pixels on the edges between the\n    // \"virtual\" pixels so that an authentic blocky look-and-feel is\n    // achieved.\n    //\n    // For a mode like 640x480, you can imagine the grid of the\n    // \"original\" pixels spaced out, with extra \"blurry\" pixels added\n    // in the space between them to fill it out. However, when you're\n    // running at a resolution like 320x240, this is not the case. In\n    // the small screen case, every single pixel has to be a blurry\n    // interpolation of two pixels from the original image.\n    //\n    // If you run in 320x240 and put your face up close to the screen\n    // you can see this: it's particularly visible in the small yellow\n    // status bar numbers for example. Overall it still looks \"okay\"\n    // but there's an obvious - albeit small - deterioration in\n    // quality.\n    //\n    // Once you get to 640x480, all the original pixels are there at\n    // least once and it's okay (the higher the resolution, the more\n    // accurate it is). When I first wrote the code I was expecting\n    // that even higher resolutions would be needed before it would\n    // look acceptable, but it turned out to be okay even at 640x480.\n\n    boolean poor_quality;\n} screen_mode_t;\n\ntypedef boolean (*grabmouse_callback_t)(void);\n\n// Called by D_DoomMain,\n// determines the hardware configuration\n// and sets up the video mode\nvoid I_InitGraphics (void);\n\nvoid I_GraphicsCheckCommandLine(void);\n\nvoid I_ShutdownGraphics(void);\n\n// Takes full 8 bit values.\nvoid I_SetPalette (byte* palette);\nint I_GetPaletteIndex(int r, int g, int b);\n\nvoid I_UpdateNoBlit (void);\nvoid I_FinishUpdate (void);\n\nvoid I_ReadScreen (byte* scr);\n\nvoid I_BeginRead (void);\nvoid I_EndRead(void);\n\nvoid I_SetWindowTitle(char *title);\n\nvoid I_CheckIsScreensaver(void);\nvoid I_SetGrabMouseCallback(grabmouse_callback_t func);\n\nvoid I_DisplayFPSDots(boolean dots_on);\nvoid I_BindVideoVariables(void);\n\nvoid I_InitWindowTitle(void);\nvoid I_InitWindowIcon(void);\n\n// Called before processing any tics in a frame (just after displaying a frame).\n// Time consuming syncronous operations are performed here (joystick reading).\n\nvoid I_StartFrame (void);\n\n// Called before processing each tic in a frame.\n// Quick syncronous operations are performed here.\n\nvoid I_StartTic (void);\n\n// Enable the loading disk image displayed when reading from disk.\n\nvoid I_EnableLoadingDisk(void);\n\nextern char *video_driver;\nextern boolean screenvisible;\n\nextern float mouse_acceleration;\nextern int mouse_threshold;\nextern int vanilla_keyboard_mapping;\nextern boolean screensaver_mode;\nextern int usegamma;\nextern byte *I_VideoBuffer;\n\nextern int screen_width;\nextern int screen_height;\nextern int screen_bpp;\nextern int fullscreen;\nextern int aspect_ratio_correct;\n\nextern int show_diskicon;\nextern int diskicon_readbytes;\n\n#endif\n"
  },
  {
    "path": "fbdoom/i_video_fbdev.c",
    "content": "// Emacs style mode select   -*- C++ -*- \n//-----------------------------------------------------------------------------\n//\n// $Id:$\n//\n// Copyright (C) 1993-1996 by id Software, Inc.\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// $Log:$\n//\n// DESCRIPTION:\n//\tDOOM graphics stuff for X11, UNIX.\n//\n//-----------------------------------------------------------------------------\n\nstatic const char\nrcsid[] = \"$Id: i_x.c,v 1.6 1997/02/03 22:45:10 b1 Exp $\";\n\n#include \"config.h\"\n#include \"v_video.h\"\n#include \"m_argv.h\"\n#include \"d_event.h\"\n#include \"d_main.h\"\n#include \"i_video.h\"\n#include \"z_zone.h\"\n\n#include \"tables.h\"\n#include \"doomkeys.h\"\n\n#include <stdbool.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <fcntl.h>\n\n#include <stdarg.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <linux/fb.h>\n#include <sys/ioctl.h>\n\n//#define CMAP256\n\nstruct fb_var_screeninfo fb = {};\nint fb_scaling = 1;\nint usemouse = 0;\n\nstruct color {\n    uint32_t b:8;\n    uint32_t g:8;\n    uint32_t r:8;\n    uint32_t a:8;\n};\n\nstatic struct color colors[256];\n\n// The screen buffer; this is modified to draw things to the screen\n\nbyte *I_VideoBuffer = NULL;\nbyte *I_VideoBuffer_FB = NULL;\n\n/* framebuffer file descriptor */\nint fd_fb = 0;\n\nint\tX_width;\nint X_height;\n\n// If true, game is running as a screensaver\n\nboolean screensaver_mode = false;\n\n// Flag indicating whether the screen is currently visible:\n// when the screen isnt visible, don't render the screen\n\nboolean screenvisible;\n\n// Mouse acceleration\n//\n// This emulates some of the behavior of DOS mouse drivers by increasing\n// the speed when the mouse is moved fast.\n//\n// The mouse input values are input directly to the game, but when\n// the values exceed the value of mouse_threshold, they are multiplied\n// by mouse_acceleration to increase the speed.\n\nfloat mouse_acceleration = 2.0;\nint mouse_threshold = 10;\n\n// Gamma correction level to use\n\nint usegamma = 0;\n\ntypedef struct\n{\n\tbyte r;\n\tbyte g;\n\tbyte b;\n} col_t;\n\n// Palette converted to RGB565\n\nstatic uint16_t rgb565_palette[256];\n\nvoid cmap_to_rgb565(uint16_t * out, uint8_t * in, int in_pixels)\n{\n    int i, j;\n    struct color c;\n    uint16_t r, g, b;\n\n    for (i = 0; i < in_pixels; i++)\n    {\n        c = colors[*in]; \n        r = ((uint16_t)(c.r >> 3)) << 11;\n        g = ((uint16_t)(c.g >> 2)) << 5;\n        b = ((uint16_t)(c.b >> 3)) << 0;\n        *out = (r | g | b);\n\n        in++;\n        for (j = 0; j < fb_scaling; j++) {\n            out++;\n        }\n    }\n}\n\nvoid cmap_to_fb(uint8_t * out, uint8_t * in, int in_pixels)\n{\n    int i, j, k;\n    struct color c;\n    uint32_t pix;\n    uint16_t r, g, b;\n\n    for (i = 0; i < in_pixels; i++)\n    {\n        c = colors[*in];  /* R:8 G:8 B:8 format! */\n        r = (uint16_t)(c.r >> (8 - fb.red.length));\n        g = (uint16_t)(c.g >> (8 - fb.green.length));\n        b = (uint16_t)(c.b >> (8 - fb.blue.length));\n        pix = r << fb.red.offset;\n        pix |= g << fb.green.offset;\n        pix |= b << fb.blue.offset;\n\n        for (k = 0; k < fb_scaling; k++) {\n            for (j = 0; j < fb.bits_per_pixel/8; j++) {\n                *out = (pix >> (j*8));\n                out++;\n            }\n        }\n        in++;\n    }\n}\n\nvoid I_InitGraphics (void)\n{\n    int i;\n\n    /* Open fbdev file descriptor */\n    fd_fb = open(\"/dev/fb0\", O_RDWR);\n    if (fd_fb < 0)\n    {\n        printf(\"Could not open /dev/fb0\");\n        exit(-1);\n    }\n\n    /* fetch framebuffer info */\n    ioctl(fd_fb, FBIOGET_VSCREENINFO, &fb);\n    /* change params if needed */\n    //ioctl(fd_fb, FBIOPUT_VSCREENINFO, &fb);\n    printf(\"I_InitGraphics: framebuffer: x_res: %d, y_res: %d, x_virtual: %d, y_virtual: %d, bpp: %d, grayscale: %d\\n\",\n            fb.xres, fb.yres, fb.xres_virtual, fb.yres_virtual, fb.bits_per_pixel, fb.grayscale);\n\n    printf(\"I_InitGraphics: framebuffer: RGBA: %d%d%d%d, red_off: %d, green_off: %d, blue_off: %d, transp_off: %d\\n\",\n            fb.red.length, fb.green.length, fb.blue.length, fb.transp.length, fb.red.offset, fb.green.offset, fb.blue.offset, fb.transp.offset);\n\n    printf(\"I_InitGraphics: DOOM screen size: w x h: %d x %d\\n\", SCREENWIDTH, SCREENHEIGHT);\n\n\n    i = M_CheckParmWithArgs(\"-scaling\", 1);\n    if (i > 0) {\n        i = atoi(myargv[i + 1]);\n        fb_scaling = i;\n        printf(\"I_InitGraphics: Scaling factor: %d\\n\", fb_scaling);\n    } else {\n        fb_scaling = fb.xres / SCREENWIDTH;\n        if (fb.yres / SCREENHEIGHT < fb_scaling)\n            fb_scaling = fb.yres / SCREENHEIGHT;\n        printf(\"I_InitGraphics: Auto-scaling factor: %d\\n\", fb_scaling);\n    }\n\n\n    /* Allocate screen to draw to */\n\tI_VideoBuffer = (byte*)Z_Malloc (SCREENWIDTH * SCREENHEIGHT, PU_STATIC, NULL);  // For DOOM to draw on\n\tI_VideoBuffer_FB = (byte*)malloc(fb.xres * fb.yres * (fb.bits_per_pixel/8));     // For a single write() syscall to fbdev\n\n\tscreenvisible = true;\n\n    extern int I_InitInput(void);\n    I_InitInput();\n}\n\nvoid I_ShutdownGraphics (void)\n{\n\tZ_Free (I_VideoBuffer);\n\tfree(I_VideoBuffer_FB);\n}\n\nvoid I_StartFrame (void)\n{\n\n}\n\n__attribute__ ((weak)) void I_GetEvent (void)\n{\n//\tevent_t event;\n//\tbool button_state;\n//\n//\tbutton_state = button_read ();\n//\n//\tif (last_button_state != button_state)\n//\t{\n//\t\tlast_button_state = button_state;\n//\n//\t\tevent.type = last_button_state ? ev_keydown : ev_keyup;\n//\t\tevent.data1 = KEY_FIRE;\n//\t\tevent.data2 = -1;\n//\t\tevent.data3 = -1;\n//\n//\t\tD_PostEvent (&event);\n//\t}\n//\n//\ttouch_main ();\n//\n//\tif ((touch_state.x != last_touch_state.x) || (touch_state.y != last_touch_state.y) || (touch_state.status != last_touch_state.status))\n//\t{\n//\t\tlast_touch_state = touch_state;\n//\n//\t\tevent.type = (touch_state.status == TOUCH_PRESSED) ? ev_keydown : ev_keyup;\n//\t\tevent.data1 = -1;\n//\t\tevent.data2 = -1;\n//\t\tevent.data3 = -1;\n//\n//\t\tif ((touch_state.x > 49)\n//\t\t && (touch_state.x < 72)\n//\t\t && (touch_state.y > 104)\n//\t\t && (touch_state.y < 143))\n//\t\t{\n//\t\t\t// select weapon\n//\t\t\tif (touch_state.x < 60)\n//\t\t\t{\n//\t\t\t\t// lower row (5-7)\n//\t\t\t\tif (touch_state.y < 119)\n//\t\t\t\t{\n//\t\t\t\t\tevent.data1 = '5';\n//\t\t\t\t}\n//\t\t\t\telse if (touch_state.y < 131)\n//\t\t\t\t{\n//\t\t\t\t\tevent.data1 = '6';\n//\t\t\t\t}\n//\t\t\t\telse\n//\t\t\t\t{\n//\t\t\t\t\tevent.data1 = '1';\n//\t\t\t\t}\n//\t\t\t}\n//\t\t\telse\n//\t\t\t{\n//\t\t\t\t// upper row (2-4)\n//\t\t\t\tif (touch_state.y < 119)\n//\t\t\t\t{\n//\t\t\t\t\tevent.data1 = '2';\n//\t\t\t\t}\n//\t\t\t\telse if (touch_state.y < 131)\n//\t\t\t\t{\n//\t\t\t\t\tevent.data1 = '3';\n//\t\t\t\t}\n//\t\t\t\telse\n//\t\t\t\t{\n//\t\t\t\t\tevent.data1 = '4';\n//\t\t\t\t}\n//\t\t\t}\n//\t\t}\n//\t\telse if (touch_state.x < 40)\n//\t\t{\n//\t\t\t// button bar at bottom screen\n//\t\t\tif (touch_state.y < 40)\n//\t\t\t{\n//\t\t\t\t// enter\n//\t\t\t\tevent.data1 = KEY_ENTER;\n//\t\t\t}\n//\t\t\telse if (touch_state.y < 80)\n//\t\t\t{\n//\t\t\t\t// escape\n//\t\t\t\tevent.data1 = KEY_ESCAPE;\n//\t\t\t}\n//\t\t\telse if (touch_state.y < 120)\n//\t\t\t{\n//\t\t\t\t// use\n//\t\t\t\tevent.data1 = KEY_USE;\n//\t\t\t}\n//\t\t\telse if (touch_state.y < 160)\n//\t\t\t{\n//\t\t\t\t// map\n//\t\t\t\tevent.data1 = KEY_TAB;\n//\t\t\t}\n//\t\t\telse if (touch_state.y < 200)\n//\t\t\t{\n//\t\t\t\t// pause\n//\t\t\t\tevent.data1 = KEY_PAUSE;\n//\t\t\t}\n//\t\t\telse if (touch_state.y < 240)\n//\t\t\t{\n//\t\t\t\t// toggle run\n//\t\t\t\tif (touch_state.status == TOUCH_PRESSED)\n//\t\t\t\t{\n//\t\t\t\t\trun = !run;\n//\n//\t\t\t\t\tevent.data1 = KEY_RSHIFT;\n//\n//\t\t\t\t\tif (run)\n//\t\t\t\t\t{\n//\t\t\t\t\t\tevent.type = ev_keydown;\n//\t\t\t\t\t}\n//\t\t\t\t\telse\n//\t\t\t\t\t{\n//\t\t\t\t\t\tevent.type = ev_keyup;\n//\t\t\t\t\t}\n//\t\t\t\t}\n//\t\t\t\telse\n//\t\t\t\t{\n//\t\t\t\t\treturn;\n//\t\t\t\t}\n//\t\t\t}\n//\t\t\telse if (touch_state.y < 280)\n//\t\t\t{\n//\t\t\t\t// save\n//\t\t\t\tevent.data1 = KEY_F2;\n//\t\t\t}\n//\t\t\telse if (touch_state.y < 320)\n//\t\t\t{\n//\t\t\t\t// load\n//\t\t\t\tevent.data1 = KEY_F3;\n//\t\t\t}\n//\t\t}\n//\t\telse\n//\t\t{\n//\t\t\t// movement/menu navigation\n//\t\t\tif (touch_state.x < 100)\n//\t\t\t{\n//\t\t\t\tif (touch_state.y < 100)\n//\t\t\t\t{\n//\t\t\t\t\tevent.data1 = KEY_STRAFE_L;\n//\t\t\t\t}\n//\t\t\t\telse if (touch_state.y < 220)\n//\t\t\t\t{\n//\t\t\t\t\tevent.data1 = KEY_DOWNARROW;\n//\t\t\t\t}\n//\t\t\t\telse\n//\t\t\t\t{\n//\t\t\t\t\tevent.data1 = KEY_STRAFE_R;\n//\t\t\t\t}\n//\t\t\t}\n//\t\t\telse if (touch_state.x < 180)\n//\t\t\t{\n//\t\t\t\tif (touch_state.y < 160)\n//\t\t\t\t{\n//\t\t\t\t\tevent.data1 = KEY_LEFTARROW;\n//\t\t\t\t}\n//\t\t\t\telse\n//\t\t\t\t{\n//\t\t\t\t\tevent.data1 = KEY_RIGHTARROW;\n//\t\t\t\t}\n//\t\t\t}\n//\t\t\telse\n//\t\t\t{\n//\t\t\t\tevent.data1 = KEY_UPARROW;\n//\t\t\t}\n//\t\t}\n//\n//\t\tD_PostEvent (&event);\n//\t}\n}\n\n__attribute__ ((weak)) void I_StartTic (void)\n{\n\tI_GetEvent();\n}\n\nvoid I_UpdateNoBlit (void)\n{\n}\n\n//\n// I_FinishUpdate\n//\n\nvoid I_FinishUpdate (void)\n{\n    int y;\n    int x_offset, y_offset, x_offset_end;\n    unsigned char *line_in, *line_out;\n\n    /* Offsets in case FB is bigger than DOOM */\n    /* 600 = fb heigt, 200 screenheight */\n    /* 600 = fb heigt, 200 screenheight */\n    /* 2048 =fb width, 320 screenwidth */\n    y_offset     = (((fb.yres - (SCREENHEIGHT * fb_scaling)) * fb.bits_per_pixel/8)) / 2;\n    x_offset     = (((fb.xres - (SCREENWIDTH  * fb_scaling)) * fb.bits_per_pixel/8)) / 2; // XXX: siglent FB hack: /4 instead of /2, since it seems to handle the resolution in a funny way\n    //x_offset     = 0;\n    x_offset_end = ((fb.xres - (SCREENWIDTH  * fb_scaling)) * fb.bits_per_pixel/8) - x_offset;\n\n    /* DRAW SCREEN */\n    line_in  = (unsigned char *) I_VideoBuffer;\n    line_out = (unsigned char *) I_VideoBuffer_FB;\n\n    y = SCREENHEIGHT;\n\n    while (y--)\n    {\n        int i;\n        for (i = 0; i < fb_scaling; i++) {\n            line_out += x_offset;\n#ifdef CMAP256\n            for (fb_scaling == 1) {\n                memcpy(line_out, line_in, SCREENWIDTH); /* fb_width is bigger than Doom SCREENWIDTH... */\n            } else {\n                //XXX FIXME fb_scaling support!\n            }\n#else\n            //cmap_to_rgb565((void*)line_out, (void*)line_in, SCREENWIDTH);\n            cmap_to_fb((void*)line_out, (void*)line_in, SCREENWIDTH);\n#endif\n            line_out += (SCREENWIDTH * fb_scaling * (fb.bits_per_pixel/8)) + x_offset_end;\n        }\n        line_in += SCREENWIDTH;\n    }\n\n    /* Start drawing from y-offset */\n    lseek(fd_fb, y_offset * fb.xres, SEEK_SET);\n    write(fd_fb, I_VideoBuffer_FB, (SCREENHEIGHT * fb_scaling * (fb.bits_per_pixel/8)) * fb.xres); /* draw only portion used by doom + x-offsets */\n}\n\n//\n// I_ReadScreen\n//\nvoid I_ReadScreen (byte* scr)\n{\n    memcpy (scr, I_VideoBuffer, SCREENWIDTH * SCREENHEIGHT);\n}\n\n//\n// I_SetPalette\n//\n#define GFX_RGB565(r, g, b)\t\t\t((((r & 0xF8) >> 3) << 11) | (((g & 0xFC) >> 2) << 5) | ((b & 0xF8) >> 3))\n#define GFX_RGB565_R(color)\t\t\t((0xF800 & color) >> 11)\n#define GFX_RGB565_G(color)\t\t\t((0x07E0 & color) >> 5)\n#define GFX_RGB565_B(color)\t\t\t(0x001F & color)\n\nvoid I_SetPalette (byte* palette)\n{\n\tint i;\n\t//col_t* c;\n\n\t//for (i = 0; i < 256; i++)\n\t//{\n\t//\tc = (col_t*)palette;\n\n\t//\trgb565_palette[i] = GFX_RGB565(gammatable[usegamma][c->r],\n\t//\t\t\t\t\t\t\t\t   gammatable[usegamma][c->g],\n\t//\t\t\t\t\t\t\t\t   gammatable[usegamma][c->b]);\n\n\t//\tpalette += 3;\n\t//}\n    \n\n    /* performance boost:\n     * map to the right pixel format over here! */\n\n    for (i=0; i<256; ++i ) {\n        colors[i].a = 0;\n        colors[i].r = gammatable[usegamma][*palette++];\n        colors[i].g = gammatable[usegamma][*palette++];\n        colors[i].b = gammatable[usegamma][*palette++];\n    }\n\n    /* Set new color map in kernel framebuffer driver */\n    //XXX FIXME ioctl(fd_fb, IOCTL_FB_PUTCMAP, colors);\n}\n\n// Given an RGB value, find the closest matching palette index.\n\nint I_GetPaletteIndex (int r, int g, int b)\n{\n    int best, best_diff, diff;\n    int i;\n    col_t color;\n\n    printf(\"I_GetPaletteIndex\\n\");\n\n    best = 0;\n    best_diff = INT_MAX;\n\n    for (i = 0; i < 256; ++i)\n    {\n    \tcolor.r = GFX_RGB565_R(rgb565_palette[i]);\n    \tcolor.g = GFX_RGB565_G(rgb565_palette[i]);\n    \tcolor.b = GFX_RGB565_B(rgb565_palette[i]);\n\n        diff = (r - color.r) * (r - color.r)\n             + (g - color.g) * (g - color.g)\n             + (b - color.b) * (b - color.b);\n\n        if (diff < best_diff)\n        {\n            best = i;\n            best_diff = diff;\n        }\n\n        if (diff == 0)\n        {\n            break;\n        }\n    }\n\n    return best;\n}\n\nvoid I_BeginRead (void)\n{\n}\n\nvoid I_EndRead (void)\n{\n}\n\nvoid I_SetWindowTitle (char *title)\n{\n}\n\nvoid I_GraphicsCheckCommandLine (void)\n{\n}\n\nvoid I_SetGrabMouseCallback (grabmouse_callback_t func)\n{\n}\n\nvoid I_EnableLoadingDisk(void)\n{\n}\n\nvoid I_BindVideoVariables (void)\n{\n}\n\nvoid I_DisplayFPSDots (boolean dots_on)\n{\n}\n\nvoid I_CheckIsScreensaver (void)\n{\n}\n"
  },
  {
    "path": "fbdoom/icon.c",
    "content": "static int icon_w = 32;\nstatic int icon_h = 32;\n\nstatic unsigned char icon_data[] = {\n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0xa2,0x86,0x73,  \n    0xa9,0x8d,0x7a,  0xbd,0xa0,0x8c,  0xda,0xba,0xa0,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0xbd,0x8d,0x67,  0xd7,0xb9,0xa5,  0xeb,0xd8,0xcd,  0xd3,0xbf,0xae,  \n    0xbd,0xa0,0x8c,  0xeb,0xd8,0xcd,  0xc2,0x9d,0x86,  0x95,0x5d,0x38,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x9b,0x7e,0x66,  \n    0xc5,0x9e,0x81,  0xd3,0xb3,0x99,  0xd4,0xac,0x8e,  0xee,0xdc,0xd1,  \n    0xb9,0x93,0x76,  0xad,0x71,0x45,  0xd4,0xac,0x8e,  0xb9,0x93,0x76,  \n    0xa3,0x77,0x58,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x39,0x1d,0x2d,  0x55,0x20,0x22,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0xda,0xb4,0x9c,  0xd3,0xa3,0x83,  \n    0xaf,0x91,0x78,  0xa7,0x83,0x6d,  0xc4,0xa7,0x93,  0xee,0xe2,0xd5,  \n    0xeb,0xd8,0xcd,  0x8c,0x60,0x3d,  0x9b,0x7e,0x66,  0xce,0x9f,0x7e,  \n    0x84,0x54,0x33,  0xba,0x83,0x5b,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x24,0x1c,0x35,  0x00,0x0f,0x32,  0x29,0x18,0x2e,  0x55,0x20,0x22,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0xd3,0xb3,0x99,  0xca,0x93,0x6f,  0xc4,0x94,0x6e,  \n    0x98,0x66,0x45,  0x78,0x50,0x2d,  0xd7,0xb9,0xa5,  0xee,0xdc,0xd1,  \n    0xc4,0x9b,0x79,  0xa1,0x6d,0x45,  0x66,0x40,0x24,  0xb8,0x7a,0x4f,  \n    0xcf,0xa6,0x83,  0x98,0x6d,0x4e,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x30,0x1c,0x2f,  0x08,0x13,0x30,  0x00,0x0f,0x32,  0x00,0x0f,0x32,  \n    0x39,0x1d,0x2d,  0x52,0x1c,0x1a,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x9e,0x7b,0x65,  0xb9,0x89,0x64,  0xaa,0x7d,0x5e,  0x9e,0x72,0x53,  \n    0x88,0x5e,0x40,  0xc4,0xa7,0x93,  0xb9,0x89,0x64,  0x90,0x6c,0x51,  \n    0x7f,0x50,0x2f,  0x90,0x5e,0x37,  0x75,0x4d,0x30,  0x7f,0x50,0x2f,  \n    0xd3,0xa3,0x83,  0xd4,0xac,0x8e,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x29,0x18,0x2e,  0x08,0x13,0x30,  0x08,0x13,0x30,  0x08,0x13,0x30,  \n    0x00,0x0f,0x32,  0x08,0x13,0x30,  0x49,0x1e,0x2b,  0x49,0x1a,0x16,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0xda,0xba,0xa0,  \n    0xd4,0xac,0x8e,  0xc4,0x9b,0x79,  0xaa,0x7d,0x5e,  0xaa,0x7d,0x5e,  \n    0xbd,0xa0,0x8c,  0x8c,0x60,0x3d,  0x70,0x49,0x2c,  0x89,0x60,0x42,  \n    0x57,0x38,0x20,  0x6c,0x45,0x29,  0x66,0x40,0x24,  0x51,0x35,0x21,  \n    0x7e,0x55,0x38,  0xce,0x9f,0x7e,  0xc2,0x8a,0x61,  0x00,0x00,0x00,  \n    0x30,0x1c,0x2f,  0x00,0x0f,0x32,  0x00,0x0f,0x32,  0x08,0x13,0x30,  \n    0x00,0x0f,0x32,  0x00,0x0f,0x32,  0x08,0x13,0x30,  0x08,0x13,0x30,  \n    0x59,0x25,0x2b,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0xcb,0x9a,0x74,  \n    0xb7,0x81,0x58,  0x8c,0x60,0x3d,  0x79,0x4b,0x2b,  0x89,0x58,0x31,  \n    0x89,0x58,0x31,  0x7f,0x50,0x2f,  0x9e,0x64,0x39,  0x75,0x4c,0x2a,  \n    0x51,0x35,0x21,  0x84,0x54,0x33,  0x54,0x36,0x1d,  0x98,0x6d,0x4e,  \n    0xb4,0x7f,0x5c,  0xba,0x83,0x5b,  0xb8,0x7a,0x4f,  0x00,0x00,0x00,  \n    0x3e,0x28,0x36,  0x08,0x13,0x30,  0x00,0x0f,0x32,  0x08,0x13,0x30,  \n    0x00,0x0f,0x32,  0x00,0x0f,0x32,  0x00,0x0f,0x32,  0x08,0x13,0x30,  \n    0x20,0x1f,0x36,  0x35,0x19,0x12,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0xc2,0x8a,0x61,  \n    0x89,0x60,0x42,  0x84,0x54,0x33,  0x7f,0x50,0x2f,  0x86,0x56,0x35,  \n    0x8d,0x5b,0x35,  0x75,0x4c,0x2a,  0x8d,0x5b,0x35,  0x5c,0x38,0x22,  \n    0x5e,0x3f,0x27,  0x75,0x4d,0x30,  0x9d,0x64,0x3f,  0x75,0x4c,0x2a,  \n    0x78,0x50,0x2d,  0x7f,0x50,0x2f,  0xb7,0x81,0x58,  0x00,0x00,0x00,  \n    0x46,0x35,0x42,  0x04,0x18,0x3a,  0x08,0x13,0x30,  0x5d,0x30,0x28,  \n    0x20,0x1f,0x36,  0x08,0x13,0x30,  0x08,0x13,0x30,  0x04,0x18,0x3a,  \n    0x19,0x1c,0x37,  0x3a,0x1d,0x16,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x91,0x5f,0x3e,  \n    0x84,0x54,0x33,  0x89,0x58,0x31,  0x7e,0x6e,0x64,  0xc4,0x94,0x6e,  \n    0x78,0x50,0x2d,  0x92,0x6f,0x59,  0xa1,0x7c,0x60,  0x9c,0x6f,0x4b,  \n    0x8d,0x5b,0x35,  0xbc,0x7f,0x53,  0xad,0x71,0x45,  0x75,0x4d,0x30,  \n    0x51,0x35,0x21,  0x4b,0x2f,0x1c,  0x70,0x49,0x2c,  0x00,0x00,0x00,  \n    0x59,0x44,0x4d,  0x1e,0x28,0x42,  0x1e,0x28,0x42,  0x48,0x19,0x10,  \n    0x42,0x19,0x12,  0x53,0x2b,0x30,  0x0c,0x26,0x48,  0x1e,0x28,0x42,  \n    0x24,0x2d,0x48,  0x5f,0x2c,0x1d,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x89,0x58,0x31,  \n    0xa1,0x84,0x6c,  0xc4,0x94,0x6e,  0x88,0x64,0x44,  0xb5,0x8f,0x73,  \n    0x9e,0x72,0x53,  0xa1,0x6d,0x45,  0x93,0x60,0x3a,  0xad,0x71,0x45,  \n    0xb4,0x7f,0x5c,  0xbd,0x8d,0x67,  0xc2,0x8a,0x61,  0xb3,0x76,0x4b,  \n    0xb8,0x7a,0x4f,  0x88,0x64,0x44,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x61,0x50,0x52,  0x1c,0x34,0x52,  0x1c,0x34,0x52,  0x54,0x27,0x16,  \n    0x29,0x17,0x09,  0x5d,0x30,0x28,  0x1c,0x34,0x52,  0x1c,0x34,0x52,  \n    0x24,0x35,0x4f,  0x69,0x34,0x24,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0xa7,0x83,0x6d,  \n    0xac,0x86,0x6a,  0x74,0x47,0x2d,  0x84,0x54,0x33,  0x5c,0x38,0x22,  \n    0x54,0x36,0x1d,  0x6c,0x45,0x29,  0x96,0x63,0x3c,  0xa3,0x6e,0x41,  \n    0xb3,0x76,0x4b,  0xb3,0x76,0x4b,  0xa2,0x68,0x3d,  0x7c,0x4e,0x2d,  \n    0x63,0x3e,0x27,  0x96,0x63,0x3c,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x6b,0x5d,0x59,  0x22,0x42,0x5f,  0x22,0x42,0x5f,  0x5d,0x34,0x1a,  \n    0x38,0x23,0x0f,  0x5c,0x38,0x22,  0x22,0x42,0x5f,  0x22,0x42,0x5f,  \n    0x2c,0x45,0x5e,  0x6f,0x3e,0x2b,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0xb7,0x81,0x58,  \n    0x98,0x74,0x59,  0x6c,0x45,0x29,  0x4b,0x35,0x25,  0x78,0x50,0x2d,  \n    0x78,0x50,0x2d,  0x78,0x50,0x2d,  0x7f,0x50,0x2f,  0x84,0x54,0x33,  \n    0x8d,0x5b,0x35,  0x96,0x63,0x3c,  0x74,0x47,0x2d,  0x65,0x45,0x26,  \n    0x65,0x45,0x26,  0x7c,0x4e,0x2d,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x77,0x69,0x64,  0x30,0x4e,0x6d,  0x32,0x52,0x6b,  0x69,0x42,0x26,  \n    0x49,0x31,0x11,  0x6c,0x47,0x2f,  0x27,0x4f,0x6d,  0x27,0x4f,0x6d,  \n    0x32,0x52,0x6b,  0x70,0x49,0x2c,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0xb7,0x81,0x58,  \n    0x8a,0x5a,0x39,  0x8a,0x5a,0x39,  0x91,0x5f,0x3e,  0x5e,0x3f,0x27,  \n    0x5c,0x38,0x22,  0x89,0x58,0x31,  0x89,0x58,0x31,  0x95,0x5d,0x38,  \n    0x9d,0x64,0x3f,  0x65,0x45,0x26,  0x4b,0x2f,0x1c,  0x7f,0x50,0x2f,  \n    0x78,0x50,0x2d,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x77,0x69,0x64,  0x36,0x5c,0x7a,  0x3e,0x5e,0x78,  0x76,0x52,0x2e,  \n    0x5d,0x42,0x22,  0x75,0x4d,0x30,  0x36,0x5c,0x7a,  0x36,0x5c,0x7a,  \n    0x3e,0x5e,0x78,  0x74,0x47,0x2d,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x89,0x58,0x31,  0x63,0x3e,0x27,  0xa8,0x6d,0x42,  0x4b,0x2f,0x1c,  \n    0x65,0x45,0x26,  0x70,0x49,0x2c,  0x51,0x35,0x21,  0x78,0x50,0x2d,  \n    0x42,0x30,0x14,  0x49,0x31,0x11,  0x59,0x44,0x22,  0x7c,0x5c,0x2a,  \n    0x8a,0x71,0x27,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x71,0x67,0x5c,  0x37,0x52,0x66,  0x3f,0x55,0x64,  0x80,0x55,0x27,  \n    0x64,0x4c,0x1f,  0x7e,0x59,0x2e,  0x37,0x52,0x66,  0x37,0x52,0x66,  \n    0x3f,0x55,0x64,  0x6c,0x47,0x2f,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x70,0x49,0x2c,  0x65,0x45,0x26,  0x65,0x45,0x26,  0x63,0x3e,0x27,  \n    0x76,0x4d,0x25,  0x5d,0x42,0x22,  0x5e,0x3f,0x27,  0x4e,0x43,0x18,  \n    0x4e,0x43,0x18,  0x6a,0x5b,0x1c,  0x4e,0x43,0x18,  0x5f,0x51,0x19,  \n    0x8a,0x76,0x2a,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x63,0x38,0x19,  0x34,0x11,0x04,  0x32,0x0f,0x00,  0x86,0x58,0x1e,  \n    0x74,0x59,0x25,  0x86,0x58,0x1e,  0x34,0x15,0x00,  0x32,0x0f,0x00,  \n    0x34,0x15,0x00,  0x4e,0x31,0x18,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x7e,0x6c,0x27,  0x5a,0x4d,0x1c,  \n    0x4d,0x3e,0x15,  0x67,0x58,0x21,  0x5a,0x4d,0x1c,  0x57,0x4b,0x1a,  \n    0x5f,0x51,0x19,  0x64,0x55,0x1e,  0x5a,0x4d,0x1c,  0x8a,0x71,0x27,  \n    0x8e,0x79,0x26,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x63,0x38,0x19,  0x34,0x11,0x04,  0x32,0x0f,0x00,  0x8d,0x63,0x1f,  \n    0x83,0x66,0x2c,  0x8d,0x63,0x1f,  0x32,0x0f,0x00,  0x35,0x19,0x12,  \n    0x34,0x11,0x04,  0x53,0x3a,0x20,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0xac,0x93,0x39,  0x76,0x65,0x20,  \n    0x6a,0x5b,0x1c,  0x6a,0x5b,0x1c,  0x67,0x58,0x21,  0x4e,0x43,0x18,  \n    0x4e,0x43,0x18,  0x9b,0x85,0x32,  0xb8,0x9e,0x3c,  0xb1,0x8d,0x36,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x63,0x38,0x19,  0x34,0x15,0x00,  0x32,0x0f,0x00,  0x8d,0x63,0x1f,  \n    0x83,0x66,0x2c,  0x8d,0x63,0x1f,  0x32,0x0f,0x00,  0x32,0x0f,0x00,  \n    0x34,0x15,0x00,  0x53,0x3a,0x20,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0xb0,0x96,0x34,  0x76,0x65,0x20,  \n    0x7e,0x66,0x23,  0x8e,0x79,0x26,  0x8a,0x71,0x27,  0x7e,0x6c,0x27,  \n    0x8a,0x71,0x27,  0x8a,0x71,0x27,  0xb0,0x96,0x34,  0x98,0x82,0x2f,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x75,0x4c,0x2a,  0x38,0x19,0x05,  0x38,0x19,0x05,  0x99,0x6d,0x22,  \n    0x96,0x70,0x2a,  0x99,0x6d,0x22,  0x38,0x19,0x05,  0x38,0x19,0x05,  \n    0x38,0x19,0x05,  0x59,0x3f,0x25,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0xac,0x93,0x39,  0x8a,0x76,0x2a,  \n    0x7e,0x66,0x23,  0x76,0x65,0x20,  0x93,0x7d,0x2a,  0x82,0x6f,0x23,  \n    0x9f,0x88,0x35,  0xb8,0xa0,0x4c,  0xb8,0xa0,0x4c,  0xc4,0xa8,0x3f,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x7c,0x5c,0x2a,  0x52,0x2e,0x0d,  0x52,0x2e,0x0d,  0xa4,0x7b,0x27,  \n    0xa1,0x80,0x37,  0x9f,0x77,0x1a,  0x52,0x2e,0x0d,  0x52,0x2e,0x0d,  \n    0x52,0x2e,0x0d,  0x5f,0x4e,0x2a,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x86,0x72,0x26,  \n    0xac,0x93,0x39,  0x97,0x82,0x36,  0xb1,0x8d,0x36,  0xac,0x93,0x39,  \n    0x97,0x82,0x36,  0xa4,0x8c,0x32,  0xbd,0xa2,0x41,  0x8a,0x71,0x27,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x8b,0x6d,0x32,  0x66,0x44,0x14,  0x6d,0x4a,0x20,  0xab,0x86,0x29,  \n    0xb1,0x8d,0x36,  0xa4,0x7b,0x27,  0x66,0x44,0x14,  0x66,0x44,0x14,  \n    0x66,0x44,0x14,  0x69,0x56,0x2c,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0xac,0x93,0x31,  \n    0x7e,0x6c,0x27,  0x9f,0x88,0x35,  0x97,0x82,0x36,  0x7e,0x66,0x23,  \n    0x7e,0x66,0x23,  0xb2,0x99,0x3f,  0xbd,0xa2,0x41,  0x8a,0x76,0x2a,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x99,0x7a,0x38,  0x86,0x58,0x1e,  0x7f,0x59,0x22,  0xb2,0x8b,0x1c,  \n    0x94,0x6e,0x21,  0x7f,0x59,0x22,  0x7f,0x59,0x22,  0x7f,0x59,0x22,  \n    0x7f,0x59,0x22,  0x69,0x56,0x2c,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0xac,0x93,0x39,  \n    0x8a,0x71,0x27,  0xb4,0x9c,0x48,  0x7e,0x66,0x23,  0xac,0x93,0x39,  \n    0x9c,0x87,0x3a,  0x9c,0x87,0x3a,  0xbd,0xa2,0x41,  0x8e,0x79,0x26,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0xa8,0x86,0x3d,  0x96,0x70,0x2a,  0x96,0x70,0x2a,  0x96,0x70,0x2a,  \n    0x96,0x70,0x2a,  0x96,0x70,0x2a,  0x96,0x70,0x2a,  0x96,0x70,0x2a,  \n    0xa1,0x80,0x37,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0xb4,0x9c,0x48,  \n    0xac,0x93,0x31,  0x93,0x7d,0x2a,  0xbd,0xa3,0x48,  0x93,0x7d,0x2a,  \n    0xb8,0xa0,0x4c,  0xb4,0x9c,0x48,  0xcc,0xa5,0x4e,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0xa8,0x86,0x3d,  0xaf,0x85,0x31,  0xaf,0x85,0x31,  0xaf,0x85,0x31,  \n    0xaf,0x85,0x31,  0xaf,0x85,0x31,  0xaf,0x85,0x31,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x97,0x82,0x36,  0xb4,0x9c,0x48,  0xb2,0x99,0x3f,  0xb4,0x9c,0x48,  \n    0xb0,0x96,0x34,  0xc1,0xa7,0x4c,  0x9b,0x84,0x2a,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0xbd,0x9e,0x4c,  0xc7,0x9a,0x3f,  0xc7,0x9a,0x3f,  0xc7,0x9a,0x3f,  \n    0xc7,0x9a,0x3f,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x9b,0x85,0x32,  0xa7,0x8e,0x2c,  0xac,0x93,0x39,  0xb5,0x91,0x41,  \n    0x76,0x65,0x20,  0xa7,0x8e,0x2c,  0xb4,0x9a,0x38,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0xcc,0xa5,0x4e,  0xe0,0xaf,0x45,  0xe0,0xaf,0x45,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0xa4,0x8c,0x32,  0xb8,0x9e,0x44,  0x86,0x72,0x26,  0x9f,0x88,0x35,  \n    0xbd,0xa3,0x48,  0x9b,0x85,0x32,  0xa3,0x81,0x32,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0xd1,0xae,0x4e,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0xb0,0x97,0x3c,  0xb4,0x9a,0x38,  0xac,0x94,0x41,  0xb2,0x99,0x3f,  \n    0xb4,0x9a,0x38,  0xb8,0x9e,0x3c,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0xcc,0xa5,0x4e,  0xa6,0x8f,0x3c,  0xb2,0x99,0x3f,  0xb4,0x9c,0x48,  \n    0xa8,0x90,0x36,  0x9f,0x88,0x35,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  0x00,0x00,0x00,  \n    \n};\n"
  },
  {
    "path": "fbdoom/info.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tThing frame/state LUT,\n//\tgenerated by multigen utilitiy.\n//\tThis one is the original DOOM version, preserved.\n//\n\n#include <stdio.h>\n#include <stdlib.h>\n\n// Data.\n#include \"sounds.h\"\n#include \"m_fixed.h\"\n\n#include \"info.h\"\n\n#include \"p_mobj.h\"\n\nchar *sprnames[] = {\n    \"TROO\",\"SHTG\",\"PUNG\",\"PISG\",\"PISF\",\"SHTF\",\"SHT2\",\"CHGG\",\"CHGF\",\"MISG\",\n    \"MISF\",\"SAWG\",\"PLSG\",\"PLSF\",\"BFGG\",\"BFGF\",\"BLUD\",\"PUFF\",\"BAL1\",\"BAL2\",\n    \"PLSS\",\"PLSE\",\"MISL\",\"BFS1\",\"BFE1\",\"BFE2\",\"TFOG\",\"IFOG\",\"PLAY\",\"POSS\",\n    \"SPOS\",\"VILE\",\"FIRE\",\"FATB\",\"FBXP\",\"SKEL\",\"MANF\",\"FATT\",\"CPOS\",\"SARG\",\n    \"HEAD\",\"BAL7\",\"BOSS\",\"BOS2\",\"SKUL\",\"SPID\",\"BSPI\",\"APLS\",\"APBX\",\"CYBR\",\n    \"PAIN\",\"SSWV\",\"KEEN\",\"BBRN\",\"BOSF\",\"ARM1\",\"ARM2\",\"BAR1\",\"BEXP\",\"FCAN\",\n    \"BON1\",\"BON2\",\"BKEY\",\"RKEY\",\"YKEY\",\"BSKU\",\"RSKU\",\"YSKU\",\"STIM\",\"MEDI\",\n    \"SOUL\",\"PINV\",\"PSTR\",\"PINS\",\"MEGA\",\"SUIT\",\"PMAP\",\"PVIS\",\"CLIP\",\"AMMO\",\n    \"ROCK\",\"BROK\",\"CELL\",\"CELP\",\"SHEL\",\"SBOX\",\"BPAK\",\"BFUG\",\"MGUN\",\"CSAW\",\n    \"LAUN\",\"PLAS\",\"SHOT\",\"SGN2\",\"COLU\",\"SMT2\",\"GOR1\",\"POL2\",\"POL5\",\"POL4\",\n    \"POL3\",\"POL1\",\"POL6\",\"GOR2\",\"GOR3\",\"GOR4\",\"GOR5\",\"SMIT\",\"COL1\",\"COL2\",\n    \"COL3\",\"COL4\",\"CAND\",\"CBRA\",\"COL6\",\"TRE1\",\"TRE2\",\"ELEC\",\"CEYE\",\"FSKU\",\n    \"COL5\",\"TBLU\",\"TGRN\",\"TRED\",\"SMBT\",\"SMGT\",\"SMRT\",\"HDB1\",\"HDB2\",\"HDB3\",\n    \"HDB4\",\"HDB5\",\"HDB6\",\"POB1\",\"POB2\",\"BRS1\",\"TLMP\",\"TLP2\", NULL\n};\n\n\n// Doesn't work with g++, needs actionf_p1\nvoid  A_Light0();\nvoid A_WeaponReady();\nvoid A_Lower();\nvoid A_Raise();\nvoid A_Punch();\nvoid A_ReFire();\nvoid A_FirePistol();\nvoid A_Light1();\nvoid A_FireShotgun();\nvoid A_Light2();\nvoid A_FireShotgun2();\nvoid A_CheckReload();\nvoid A_OpenShotgun2();\nvoid A_LoadShotgun2();\nvoid A_CloseShotgun2();\nvoid A_FireCGun();\nvoid A_GunFlash();\nvoid A_FireMissile();\nvoid A_Saw();\nvoid A_FirePlasma();\nvoid A_BFGsound();\nvoid A_FireBFG();\nvoid A_BFGSpray();\nvoid A_Explode();\nvoid A_Pain();\nvoid A_PlayerScream();\nvoid A_Fall();\nvoid A_XScream();\nvoid A_Look();\nvoid A_Chase();\nvoid A_FaceTarget();\nvoid A_PosAttack();\nvoid A_Scream();\nvoid A_SPosAttack();\nvoid A_VileChase();\nvoid A_VileStart();\nvoid A_VileTarget();\nvoid A_VileAttack();\nvoid A_StartFire();\nvoid A_Fire();\nvoid A_FireCrackle();\nvoid A_Tracer();\nvoid A_SkelWhoosh();\nvoid A_SkelFist();\nvoid A_SkelMissile();\nvoid A_FatRaise();\nvoid A_FatAttack1();\nvoid A_FatAttack2();\nvoid A_FatAttack3();\nvoid A_BossDeath();\nvoid A_CPosAttack();\nvoid A_CPosRefire();\nvoid A_TroopAttack();\nvoid A_SargAttack();\nvoid A_HeadAttack();\nvoid A_BruisAttack();\nvoid A_SkullAttack();\nvoid A_Metal();\nvoid A_SpidRefire();\nvoid A_BabyMetal();\nvoid A_BspiAttack();\nvoid A_Hoof();\nvoid A_CyberAttack();\nvoid A_PainAttack();\nvoid A_PainDie();\nvoid A_KeenDie();\nvoid A_BrainPain();\nvoid A_BrainScream();\nvoid A_BrainDie();\nvoid A_BrainAwake();\nvoid A_BrainSpit();\nvoid A_SpawnSound();\nvoid A_SpawnFly();\nvoid A_BrainExplode();\n\n\nstate_t\tstates[NUMSTATES] = {\n    {SPR_TROO,0,-1,{NULL},S_NULL,0,0},\t// S_NULL\n    {SPR_SHTG,4,0,{A_Light0},S_NULL,0,0},\t// S_LIGHTDONE\n    {SPR_PUNG,0,1,{A_WeaponReady},S_PUNCH,0,0},\t// S_PUNCH\n    {SPR_PUNG,0,1,{A_Lower},S_PUNCHDOWN,0,0},\t// S_PUNCHDOWN\n    {SPR_PUNG,0,1,{A_Raise},S_PUNCHUP,0,0},\t// S_PUNCHUP\n    {SPR_PUNG,1,4,{NULL},S_PUNCH2,0,0},\t\t// S_PUNCH1\n    {SPR_PUNG,2,4,{A_Punch},S_PUNCH3,0,0},\t// S_PUNCH2\n    {SPR_PUNG,3,5,{NULL},S_PUNCH4,0,0},\t\t// S_PUNCH3\n    {SPR_PUNG,2,4,{NULL},S_PUNCH5,0,0},\t\t// S_PUNCH4\n    {SPR_PUNG,1,5,{A_ReFire},S_PUNCH,0,0},\t// S_PUNCH5\n    {SPR_PISG,0,1,{A_WeaponReady},S_PISTOL,0,0},// S_PISTOL\n    {SPR_PISG,0,1,{A_Lower},S_PISTOLDOWN,0,0},\t// S_PISTOLDOWN\n    {SPR_PISG,0,1,{A_Raise},S_PISTOLUP,0,0},\t// S_PISTOLUP\n    {SPR_PISG,0,4,{NULL},S_PISTOL2,0,0},\t// S_PISTOL1\n    {SPR_PISG,1,6,{A_FirePistol},S_PISTOL3,0,0},// S_PISTOL2\n    {SPR_PISG,2,4,{NULL},S_PISTOL4,0,0},\t// S_PISTOL3\n    {SPR_PISG,1,5,{A_ReFire},S_PISTOL,0,0},\t// S_PISTOL4\n    {SPR_PISF,32768,7,{A_Light1},S_LIGHTDONE,0,0},\t// S_PISTOLFLASH\n    {SPR_SHTG,0,1,{A_WeaponReady},S_SGUN,0,0},\t// S_SGUN\n    {SPR_SHTG,0,1,{A_Lower},S_SGUNDOWN,0,0},\t// S_SGUNDOWN\n    {SPR_SHTG,0,1,{A_Raise},S_SGUNUP,0,0},\t// S_SGUNUP\n    {SPR_SHTG,0,3,{NULL},S_SGUN2,0,0},\t// S_SGUN1\n    {SPR_SHTG,0,7,{A_FireShotgun},S_SGUN3,0,0},\t// S_SGUN2\n    {SPR_SHTG,1,5,{NULL},S_SGUN4,0,0},\t// S_SGUN3\n    {SPR_SHTG,2,5,{NULL},S_SGUN5,0,0},\t// S_SGUN4\n    {SPR_SHTG,3,4,{NULL},S_SGUN6,0,0},\t// S_SGUN5\n    {SPR_SHTG,2,5,{NULL},S_SGUN7,0,0},\t// S_SGUN6\n    {SPR_SHTG,1,5,{NULL},S_SGUN8,0,0},\t// S_SGUN7\n    {SPR_SHTG,0,3,{NULL},S_SGUN9,0,0},\t// S_SGUN8\n    {SPR_SHTG,0,7,{A_ReFire},S_SGUN,0,0},\t// S_SGUN9\n    {SPR_SHTF,32768,4,{A_Light1},S_SGUNFLASH2,0,0},\t// S_SGUNFLASH1\n    {SPR_SHTF,32769,3,{A_Light2},S_LIGHTDONE,0,0},\t// S_SGUNFLASH2\n    {SPR_SHT2,0,1,{A_WeaponReady},S_DSGUN,0,0},\t// S_DSGUN\n    {SPR_SHT2,0,1,{A_Lower},S_DSGUNDOWN,0,0},\t// S_DSGUNDOWN\n    {SPR_SHT2,0,1,{A_Raise},S_DSGUNUP,0,0},\t// S_DSGUNUP\n    {SPR_SHT2,0,3,{NULL},S_DSGUN2,0,0},\t// S_DSGUN1\n    {SPR_SHT2,0,7,{A_FireShotgun2},S_DSGUN3,0,0},\t// S_DSGUN2\n    {SPR_SHT2,1,7,{NULL},S_DSGUN4,0,0},\t// S_DSGUN3\n    {SPR_SHT2,2,7,{A_CheckReload},S_DSGUN5,0,0},\t// S_DSGUN4\n    {SPR_SHT2,3,7,{A_OpenShotgun2},S_DSGUN6,0,0},\t// S_DSGUN5\n    {SPR_SHT2,4,7,{NULL},S_DSGUN7,0,0},\t// S_DSGUN6\n    {SPR_SHT2,5,7,{A_LoadShotgun2},S_DSGUN8,0,0},\t// S_DSGUN7\n    {SPR_SHT2,6,6,{NULL},S_DSGUN9,0,0},\t// S_DSGUN8\n    {SPR_SHT2,7,6,{A_CloseShotgun2},S_DSGUN10,0,0},\t// S_DSGUN9\n    {SPR_SHT2,0,5,{A_ReFire},S_DSGUN,0,0},\t// S_DSGUN10\n    {SPR_SHT2,1,7,{NULL},S_DSNR2,0,0},\t// S_DSNR1\n    {SPR_SHT2,0,3,{NULL},S_DSGUNDOWN,0,0},\t// S_DSNR2\n    {SPR_SHT2,32776,5,{A_Light1},S_DSGUNFLASH2,0,0},\t// S_DSGUNFLASH1\n    {SPR_SHT2,32777,4,{A_Light2},S_LIGHTDONE,0,0},\t// S_DSGUNFLASH2\n    {SPR_CHGG,0,1,{A_WeaponReady},S_CHAIN,0,0},\t// S_CHAIN\n    {SPR_CHGG,0,1,{A_Lower},S_CHAINDOWN,0,0},\t// S_CHAINDOWN\n    {SPR_CHGG,0,1,{A_Raise},S_CHAINUP,0,0},\t// S_CHAINUP\n    {SPR_CHGG,0,4,{A_FireCGun},S_CHAIN2,0,0},\t// S_CHAIN1\n    {SPR_CHGG,1,4,{A_FireCGun},S_CHAIN3,0,0},\t// S_CHAIN2\n    {SPR_CHGG,1,0,{A_ReFire},S_CHAIN,0,0},\t// S_CHAIN3\n    {SPR_CHGF,32768,5,{A_Light1},S_LIGHTDONE,0,0},\t// S_CHAINFLASH1\n    {SPR_CHGF,32769,5,{A_Light2},S_LIGHTDONE,0,0},\t// S_CHAINFLASH2\n    {SPR_MISG,0,1,{A_WeaponReady},S_MISSILE,0,0},\t// S_MISSILE\n    {SPR_MISG,0,1,{A_Lower},S_MISSILEDOWN,0,0},\t// S_MISSILEDOWN\n    {SPR_MISG,0,1,{A_Raise},S_MISSILEUP,0,0},\t// S_MISSILEUP\n    {SPR_MISG,1,8,{A_GunFlash},S_MISSILE2,0,0},\t// S_MISSILE1\n    {SPR_MISG,1,12,{A_FireMissile},S_MISSILE3,0,0},\t// S_MISSILE2\n    {SPR_MISG,1,0,{A_ReFire},S_MISSILE,0,0},\t// S_MISSILE3\n    {SPR_MISF,32768,3,{A_Light1},S_MISSILEFLASH2,0,0},\t// S_MISSILEFLASH1\n    {SPR_MISF,32769,4,{NULL},S_MISSILEFLASH3,0,0},\t// S_MISSILEFLASH2\n    {SPR_MISF,32770,4,{A_Light2},S_MISSILEFLASH4,0,0},\t// S_MISSILEFLASH3\n    {SPR_MISF,32771,4,{A_Light2},S_LIGHTDONE,0,0},\t// S_MISSILEFLASH4\n    {SPR_SAWG,2,4,{A_WeaponReady},S_SAWB,0,0},\t// S_SAW\n    {SPR_SAWG,3,4,{A_WeaponReady},S_SAW,0,0},\t// S_SAWB\n    {SPR_SAWG,2,1,{A_Lower},S_SAWDOWN,0,0},\t// S_SAWDOWN\n    {SPR_SAWG,2,1,{A_Raise},S_SAWUP,0,0},\t// S_SAWUP\n    {SPR_SAWG,0,4,{A_Saw},S_SAW2,0,0},\t// S_SAW1\n    {SPR_SAWG,1,4,{A_Saw},S_SAW3,0,0},\t// S_SAW2\n    {SPR_SAWG,1,0,{A_ReFire},S_SAW,0,0},\t// S_SAW3\n    {SPR_PLSG,0,1,{A_WeaponReady},S_PLASMA,0,0},\t// S_PLASMA\n    {SPR_PLSG,0,1,{A_Lower},S_PLASMADOWN,0,0},\t// S_PLASMADOWN\n    {SPR_PLSG,0,1,{A_Raise},S_PLASMAUP,0,0},\t// S_PLASMAUP\n    {SPR_PLSG,0,3,{A_FirePlasma},S_PLASMA2,0,0},\t// S_PLASMA1\n    {SPR_PLSG,1,20,{A_ReFire},S_PLASMA,0,0},\t// S_PLASMA2\n    {SPR_PLSF,32768,4,{A_Light1},S_LIGHTDONE,0,0},\t// S_PLASMAFLASH1\n    {SPR_PLSF,32769,4,{A_Light1},S_LIGHTDONE,0,0},\t// S_PLASMAFLASH2\n    {SPR_BFGG,0,1,{A_WeaponReady},S_BFG,0,0},\t// S_BFG\n    {SPR_BFGG,0,1,{A_Lower},S_BFGDOWN,0,0},\t// S_BFGDOWN\n    {SPR_BFGG,0,1,{A_Raise},S_BFGUP,0,0},\t// S_BFGUP\n    {SPR_BFGG,0,20,{A_BFGsound},S_BFG2,0,0},\t// S_BFG1\n    {SPR_BFGG,1,10,{A_GunFlash},S_BFG3,0,0},\t// S_BFG2\n    {SPR_BFGG,1,10,{A_FireBFG},S_BFG4,0,0},\t// S_BFG3\n    {SPR_BFGG,1,20,{A_ReFire},S_BFG,0,0},\t// S_BFG4\n    {SPR_BFGF,32768,11,{A_Light1},S_BFGFLASH2,0,0},\t// S_BFGFLASH1\n    {SPR_BFGF,32769,6,{A_Light2},S_LIGHTDONE,0,0},\t// S_BFGFLASH2\n    {SPR_BLUD,2,8,{NULL},S_BLOOD2,0,0},\t// S_BLOOD1\n    {SPR_BLUD,1,8,{NULL},S_BLOOD3,0,0},\t// S_BLOOD2\n    {SPR_BLUD,0,8,{NULL},S_NULL,0,0},\t// S_BLOOD3\n    {SPR_PUFF,32768,4,{NULL},S_PUFF2,0,0},\t// S_PUFF1\n    {SPR_PUFF,1,4,{NULL},S_PUFF3,0,0},\t// S_PUFF2\n    {SPR_PUFF,2,4,{NULL},S_PUFF4,0,0},\t// S_PUFF3\n    {SPR_PUFF,3,4,{NULL},S_NULL,0,0},\t// S_PUFF4\n    {SPR_BAL1,32768,4,{NULL},S_TBALL2,0,0},\t// S_TBALL1\n    {SPR_BAL1,32769,4,{NULL},S_TBALL1,0,0},\t// S_TBALL2\n    {SPR_BAL1,32770,6,{NULL},S_TBALLX2,0,0},\t// S_TBALLX1\n    {SPR_BAL1,32771,6,{NULL},S_TBALLX3,0,0},\t// S_TBALLX2\n    {SPR_BAL1,32772,6,{NULL},S_NULL,0,0},\t// S_TBALLX3\n    {SPR_BAL2,32768,4,{NULL},S_RBALL2,0,0},\t// S_RBALL1\n    {SPR_BAL2,32769,4,{NULL},S_RBALL1,0,0},\t// S_RBALL2\n    {SPR_BAL2,32770,6,{NULL},S_RBALLX2,0,0},\t// S_RBALLX1\n    {SPR_BAL2,32771,6,{NULL},S_RBALLX3,0,0},\t// S_RBALLX2\n    {SPR_BAL2,32772,6,{NULL},S_NULL,0,0},\t// S_RBALLX3\n    {SPR_PLSS,32768,6,{NULL},S_PLASBALL2,0,0},\t// S_PLASBALL\n    {SPR_PLSS,32769,6,{NULL},S_PLASBALL,0,0},\t// S_PLASBALL2\n    {SPR_PLSE,32768,4,{NULL},S_PLASEXP2,0,0},\t// S_PLASEXP\n    {SPR_PLSE,32769,4,{NULL},S_PLASEXP3,0,0},\t// S_PLASEXP2\n    {SPR_PLSE,32770,4,{NULL},S_PLASEXP4,0,0},\t// S_PLASEXP3\n    {SPR_PLSE,32771,4,{NULL},S_PLASEXP5,0,0},\t// S_PLASEXP4\n    {SPR_PLSE,32772,4,{NULL},S_NULL,0,0},\t// S_PLASEXP5\n    {SPR_MISL,32768,1,{NULL},S_ROCKET,0,0},\t// S_ROCKET\n    {SPR_BFS1,32768,4,{NULL},S_BFGSHOT2,0,0},\t// S_BFGSHOT\n    {SPR_BFS1,32769,4,{NULL},S_BFGSHOT,0,0},\t// S_BFGSHOT2\n    {SPR_BFE1,32768,8,{NULL},S_BFGLAND2,0,0},\t// S_BFGLAND\n    {SPR_BFE1,32769,8,{NULL},S_BFGLAND3,0,0},\t// S_BFGLAND2\n    {SPR_BFE1,32770,8,{A_BFGSpray},S_BFGLAND4,0,0},\t// S_BFGLAND3\n    {SPR_BFE1,32771,8,{NULL},S_BFGLAND5,0,0},\t// S_BFGLAND4\n    {SPR_BFE1,32772,8,{NULL},S_BFGLAND6,0,0},\t// S_BFGLAND5\n    {SPR_BFE1,32773,8,{NULL},S_NULL,0,0},\t// S_BFGLAND6\n    {SPR_BFE2,32768,8,{NULL},S_BFGEXP2,0,0},\t// S_BFGEXP\n    {SPR_BFE2,32769,8,{NULL},S_BFGEXP3,0,0},\t// S_BFGEXP2\n    {SPR_BFE2,32770,8,{NULL},S_BFGEXP4,0,0},\t// S_BFGEXP3\n    {SPR_BFE2,32771,8,{NULL},S_NULL,0,0},\t// S_BFGEXP4\n    {SPR_MISL,32769,8,{A_Explode},S_EXPLODE2,0,0},\t// S_EXPLODE1\n    {SPR_MISL,32770,6,{NULL},S_EXPLODE3,0,0},\t// S_EXPLODE2\n    {SPR_MISL,32771,4,{NULL},S_NULL,0,0},\t// S_EXPLODE3\n    {SPR_TFOG,32768,6,{NULL},S_TFOG01,0,0},\t// S_TFOG\n    {SPR_TFOG,32769,6,{NULL},S_TFOG02,0,0},\t// S_TFOG01\n    {SPR_TFOG,32768,6,{NULL},S_TFOG2,0,0},\t// S_TFOG02\n    {SPR_TFOG,32769,6,{NULL},S_TFOG3,0,0},\t// S_TFOG2\n    {SPR_TFOG,32770,6,{NULL},S_TFOG4,0,0},\t// S_TFOG3\n    {SPR_TFOG,32771,6,{NULL},S_TFOG5,0,0},\t// S_TFOG4\n    {SPR_TFOG,32772,6,{NULL},S_TFOG6,0,0},\t// S_TFOG5\n    {SPR_TFOG,32773,6,{NULL},S_TFOG7,0,0},\t// S_TFOG6\n    {SPR_TFOG,32774,6,{NULL},S_TFOG8,0,0},\t// S_TFOG7\n    {SPR_TFOG,32775,6,{NULL},S_TFOG9,0,0},\t// S_TFOG8\n    {SPR_TFOG,32776,6,{NULL},S_TFOG10,0,0},\t// S_TFOG9\n    {SPR_TFOG,32777,6,{NULL},S_NULL,0,0},\t// S_TFOG10\n    {SPR_IFOG,32768,6,{NULL},S_IFOG01,0,0},\t// S_IFOG\n    {SPR_IFOG,32769,6,{NULL},S_IFOG02,0,0},\t// S_IFOG01\n    {SPR_IFOG,32768,6,{NULL},S_IFOG2,0,0},\t// S_IFOG02\n    {SPR_IFOG,32769,6,{NULL},S_IFOG3,0,0},\t// S_IFOG2\n    {SPR_IFOG,32770,6,{NULL},S_IFOG4,0,0},\t// S_IFOG3\n    {SPR_IFOG,32771,6,{NULL},S_IFOG5,0,0},\t// S_IFOG4\n    {SPR_IFOG,32772,6,{NULL},S_NULL,0,0},\t// S_IFOG5\n    {SPR_PLAY,0,-1,{NULL},S_NULL,0,0},\t// S_PLAY\n    {SPR_PLAY,0,4,{NULL},S_PLAY_RUN2,0,0},\t// S_PLAY_RUN1\n    {SPR_PLAY,1,4,{NULL},S_PLAY_RUN3,0,0},\t// S_PLAY_RUN2\n    {SPR_PLAY,2,4,{NULL},S_PLAY_RUN4,0,0},\t// S_PLAY_RUN3\n    {SPR_PLAY,3,4,{NULL},S_PLAY_RUN1,0,0},\t// S_PLAY_RUN4\n    {SPR_PLAY,4,12,{NULL},S_PLAY,0,0},\t// S_PLAY_ATK1\n    {SPR_PLAY,32773,6,{NULL},S_PLAY_ATK1,0,0},\t// S_PLAY_ATK2\n    {SPR_PLAY,6,4,{NULL},S_PLAY_PAIN2,0,0},\t// S_PLAY_PAIN\n    {SPR_PLAY,6,4,{A_Pain},S_PLAY,0,0},\t// S_PLAY_PAIN2\n    {SPR_PLAY,7,10,{NULL},S_PLAY_DIE2,0,0},\t// S_PLAY_DIE1\n    {SPR_PLAY,8,10,{A_PlayerScream},S_PLAY_DIE3,0,0},\t// S_PLAY_DIE2\n    {SPR_PLAY,9,10,{A_Fall},S_PLAY_DIE4,0,0},\t// S_PLAY_DIE3\n    {SPR_PLAY,10,10,{NULL},S_PLAY_DIE5,0,0},\t// S_PLAY_DIE4\n    {SPR_PLAY,11,10,{NULL},S_PLAY_DIE6,0,0},\t// S_PLAY_DIE5\n    {SPR_PLAY,12,10,{NULL},S_PLAY_DIE7,0,0},\t// S_PLAY_DIE6\n    {SPR_PLAY,13,-1,{NULL},S_NULL,0,0},\t// S_PLAY_DIE7\n    {SPR_PLAY,14,5,{NULL},S_PLAY_XDIE2,0,0},\t// S_PLAY_XDIE1\n    {SPR_PLAY,15,5,{A_XScream},S_PLAY_XDIE3,0,0},\t// S_PLAY_XDIE2\n    {SPR_PLAY,16,5,{A_Fall},S_PLAY_XDIE4,0,0},\t// S_PLAY_XDIE3\n    {SPR_PLAY,17,5,{NULL},S_PLAY_XDIE5,0,0},\t// S_PLAY_XDIE4\n    {SPR_PLAY,18,5,{NULL},S_PLAY_XDIE6,0,0},\t// S_PLAY_XDIE5\n    {SPR_PLAY,19,5,{NULL},S_PLAY_XDIE7,0,0},\t// S_PLAY_XDIE6\n    {SPR_PLAY,20,5,{NULL},S_PLAY_XDIE8,0,0},\t// S_PLAY_XDIE7\n    {SPR_PLAY,21,5,{NULL},S_PLAY_XDIE9,0,0},\t// S_PLAY_XDIE8\n    {SPR_PLAY,22,-1,{NULL},S_NULL,0,0},\t// S_PLAY_XDIE9\n    {SPR_POSS,0,10,{A_Look},S_POSS_STND2,0,0},\t// S_POSS_STND\n    {SPR_POSS,1,10,{A_Look},S_POSS_STND,0,0},\t// S_POSS_STND2\n    {SPR_POSS,0,4,{A_Chase},S_POSS_RUN2,0,0},\t// S_POSS_RUN1\n    {SPR_POSS,0,4,{A_Chase},S_POSS_RUN3,0,0},\t// S_POSS_RUN2\n    {SPR_POSS,1,4,{A_Chase},S_POSS_RUN4,0,0},\t// S_POSS_RUN3\n    {SPR_POSS,1,4,{A_Chase},S_POSS_RUN5,0,0},\t// S_POSS_RUN4\n    {SPR_POSS,2,4,{A_Chase},S_POSS_RUN6,0,0},\t// S_POSS_RUN5\n    {SPR_POSS,2,4,{A_Chase},S_POSS_RUN7,0,0},\t// S_POSS_RUN6\n    {SPR_POSS,3,4,{A_Chase},S_POSS_RUN8,0,0},\t// S_POSS_RUN7\n    {SPR_POSS,3,4,{A_Chase},S_POSS_RUN1,0,0},\t// S_POSS_RUN8\n    {SPR_POSS,4,10,{A_FaceTarget},S_POSS_ATK2,0,0},\t// S_POSS_ATK1\n    {SPR_POSS,5,8,{A_PosAttack},S_POSS_ATK3,0,0},\t// S_POSS_ATK2\n    {SPR_POSS,4,8,{NULL},S_POSS_RUN1,0,0},\t// S_POSS_ATK3\n    {SPR_POSS,6,3,{NULL},S_POSS_PAIN2,0,0},\t// S_POSS_PAIN\n    {SPR_POSS,6,3,{A_Pain},S_POSS_RUN1,0,0},\t// S_POSS_PAIN2\n    {SPR_POSS,7,5,{NULL},S_POSS_DIE2,0,0},\t// S_POSS_DIE1\n    {SPR_POSS,8,5,{A_Scream},S_POSS_DIE3,0,0},\t// S_POSS_DIE2\n    {SPR_POSS,9,5,{A_Fall},S_POSS_DIE4,0,0},\t// S_POSS_DIE3\n    {SPR_POSS,10,5,{NULL},S_POSS_DIE5,0,0},\t// S_POSS_DIE4\n    {SPR_POSS,11,-1,{NULL},S_NULL,0,0},\t// S_POSS_DIE5\n    {SPR_POSS,12,5,{NULL},S_POSS_XDIE2,0,0},\t// S_POSS_XDIE1\n    {SPR_POSS,13,5,{A_XScream},S_POSS_XDIE3,0,0},\t// S_POSS_XDIE2\n    {SPR_POSS,14,5,{A_Fall},S_POSS_XDIE4,0,0},\t// S_POSS_XDIE3\n    {SPR_POSS,15,5,{NULL},S_POSS_XDIE5,0,0},\t// S_POSS_XDIE4\n    {SPR_POSS,16,5,{NULL},S_POSS_XDIE6,0,0},\t// S_POSS_XDIE5\n    {SPR_POSS,17,5,{NULL},S_POSS_XDIE7,0,0},\t// S_POSS_XDIE6\n    {SPR_POSS,18,5,{NULL},S_POSS_XDIE8,0,0},\t// S_POSS_XDIE7\n    {SPR_POSS,19,5,{NULL},S_POSS_XDIE9,0,0},\t// S_POSS_XDIE8\n    {SPR_POSS,20,-1,{NULL},S_NULL,0,0},\t// S_POSS_XDIE9\n    {SPR_POSS,10,5,{NULL},S_POSS_RAISE2,0,0},\t// S_POSS_RAISE1\n    {SPR_POSS,9,5,{NULL},S_POSS_RAISE3,0,0},\t// S_POSS_RAISE2\n    {SPR_POSS,8,5,{NULL},S_POSS_RAISE4,0,0},\t// S_POSS_RAISE3\n    {SPR_POSS,7,5,{NULL},S_POSS_RUN1,0,0},\t// S_POSS_RAISE4\n    {SPR_SPOS,0,10,{A_Look},S_SPOS_STND2,0,0},\t// S_SPOS_STND\n    {SPR_SPOS,1,10,{A_Look},S_SPOS_STND,0,0},\t// S_SPOS_STND2\n    {SPR_SPOS,0,3,{A_Chase},S_SPOS_RUN2,0,0},\t// S_SPOS_RUN1\n    {SPR_SPOS,0,3,{A_Chase},S_SPOS_RUN3,0,0},\t// S_SPOS_RUN2\n    {SPR_SPOS,1,3,{A_Chase},S_SPOS_RUN4,0,0},\t// S_SPOS_RUN3\n    {SPR_SPOS,1,3,{A_Chase},S_SPOS_RUN5,0,0},\t// S_SPOS_RUN4\n    {SPR_SPOS,2,3,{A_Chase},S_SPOS_RUN6,0,0},\t// S_SPOS_RUN5\n    {SPR_SPOS,2,3,{A_Chase},S_SPOS_RUN7,0,0},\t// S_SPOS_RUN6\n    {SPR_SPOS,3,3,{A_Chase},S_SPOS_RUN8,0,0},\t// S_SPOS_RUN7\n    {SPR_SPOS,3,3,{A_Chase},S_SPOS_RUN1,0,0},\t// S_SPOS_RUN8\n    {SPR_SPOS,4,10,{A_FaceTarget},S_SPOS_ATK2,0,0},\t// S_SPOS_ATK1\n    {SPR_SPOS,32773,10,{A_SPosAttack},S_SPOS_ATK3,0,0},\t// S_SPOS_ATK2\n    {SPR_SPOS,4,10,{NULL},S_SPOS_RUN1,0,0},\t// S_SPOS_ATK3\n    {SPR_SPOS,6,3,{NULL},S_SPOS_PAIN2,0,0},\t// S_SPOS_PAIN\n    {SPR_SPOS,6,3,{A_Pain},S_SPOS_RUN1,0,0},\t// S_SPOS_PAIN2\n    {SPR_SPOS,7,5,{NULL},S_SPOS_DIE2,0,0},\t// S_SPOS_DIE1\n    {SPR_SPOS,8,5,{A_Scream},S_SPOS_DIE3,0,0},\t// S_SPOS_DIE2\n    {SPR_SPOS,9,5,{A_Fall},S_SPOS_DIE4,0,0},\t// S_SPOS_DIE3\n    {SPR_SPOS,10,5,{NULL},S_SPOS_DIE5,0,0},\t// S_SPOS_DIE4\n    {SPR_SPOS,11,-1,{NULL},S_NULL,0,0},\t// S_SPOS_DIE5\n    {SPR_SPOS,12,5,{NULL},S_SPOS_XDIE2,0,0},\t// S_SPOS_XDIE1\n    {SPR_SPOS,13,5,{A_XScream},S_SPOS_XDIE3,0,0},\t// S_SPOS_XDIE2\n    {SPR_SPOS,14,5,{A_Fall},S_SPOS_XDIE4,0,0},\t// S_SPOS_XDIE3\n    {SPR_SPOS,15,5,{NULL},S_SPOS_XDIE5,0,0},\t// S_SPOS_XDIE4\n    {SPR_SPOS,16,5,{NULL},S_SPOS_XDIE6,0,0},\t// S_SPOS_XDIE5\n    {SPR_SPOS,17,5,{NULL},S_SPOS_XDIE7,0,0},\t// S_SPOS_XDIE6\n    {SPR_SPOS,18,5,{NULL},S_SPOS_XDIE8,0,0},\t// S_SPOS_XDIE7\n    {SPR_SPOS,19,5,{NULL},S_SPOS_XDIE9,0,0},\t// S_SPOS_XDIE8\n    {SPR_SPOS,20,-1,{NULL},S_NULL,0,0},\t// S_SPOS_XDIE9\n    {SPR_SPOS,11,5,{NULL},S_SPOS_RAISE2,0,0},\t// S_SPOS_RAISE1\n    {SPR_SPOS,10,5,{NULL},S_SPOS_RAISE3,0,0},\t// S_SPOS_RAISE2\n    {SPR_SPOS,9,5,{NULL},S_SPOS_RAISE4,0,0},\t// S_SPOS_RAISE3\n    {SPR_SPOS,8,5,{NULL},S_SPOS_RAISE5,0,0},\t// S_SPOS_RAISE4\n    {SPR_SPOS,7,5,{NULL},S_SPOS_RUN1,0,0},\t// S_SPOS_RAISE5\n    {SPR_VILE,0,10,{A_Look},S_VILE_STND2,0,0},\t// S_VILE_STND\n    {SPR_VILE,1,10,{A_Look},S_VILE_STND,0,0},\t// S_VILE_STND2\n    {SPR_VILE,0,2,{A_VileChase},S_VILE_RUN2,0,0},\t// S_VILE_RUN1\n    {SPR_VILE,0,2,{A_VileChase},S_VILE_RUN3,0,0},\t// S_VILE_RUN2\n    {SPR_VILE,1,2,{A_VileChase},S_VILE_RUN4,0,0},\t// S_VILE_RUN3\n    {SPR_VILE,1,2,{A_VileChase},S_VILE_RUN5,0,0},\t// S_VILE_RUN4\n    {SPR_VILE,2,2,{A_VileChase},S_VILE_RUN6,0,0},\t// S_VILE_RUN5\n    {SPR_VILE,2,2,{A_VileChase},S_VILE_RUN7,0,0},\t// S_VILE_RUN6\n    {SPR_VILE,3,2,{A_VileChase},S_VILE_RUN8,0,0},\t// S_VILE_RUN7\n    {SPR_VILE,3,2,{A_VileChase},S_VILE_RUN9,0,0},\t// S_VILE_RUN8\n    {SPR_VILE,4,2,{A_VileChase},S_VILE_RUN10,0,0},\t// S_VILE_RUN9\n    {SPR_VILE,4,2,{A_VileChase},S_VILE_RUN11,0,0},\t// S_VILE_RUN10\n    {SPR_VILE,5,2,{A_VileChase},S_VILE_RUN12,0,0},\t// S_VILE_RUN11\n    {SPR_VILE,5,2,{A_VileChase},S_VILE_RUN1,0,0},\t// S_VILE_RUN12\n    {SPR_VILE,32774,0,{A_VileStart},S_VILE_ATK2,0,0},\t// S_VILE_ATK1\n    {SPR_VILE,32774,10,{A_FaceTarget},S_VILE_ATK3,0,0},\t// S_VILE_ATK2\n    {SPR_VILE,32775,8,{A_VileTarget},S_VILE_ATK4,0,0},\t// S_VILE_ATK3\n    {SPR_VILE,32776,8,{A_FaceTarget},S_VILE_ATK5,0,0},\t// S_VILE_ATK4\n    {SPR_VILE,32777,8,{A_FaceTarget},S_VILE_ATK6,0,0},\t// S_VILE_ATK5\n    {SPR_VILE,32778,8,{A_FaceTarget},S_VILE_ATK7,0,0},\t// S_VILE_ATK6\n    {SPR_VILE,32779,8,{A_FaceTarget},S_VILE_ATK8,0,0},\t// S_VILE_ATK7\n    {SPR_VILE,32780,8,{A_FaceTarget},S_VILE_ATK9,0,0},\t// S_VILE_ATK8\n    {SPR_VILE,32781,8,{A_FaceTarget},S_VILE_ATK10,0,0},\t// S_VILE_ATK9\n    {SPR_VILE,32782,8,{A_VileAttack},S_VILE_ATK11,0,0},\t// S_VILE_ATK10\n    {SPR_VILE,32783,20,{NULL},S_VILE_RUN1,0,0},\t// S_VILE_ATK11\n    {SPR_VILE,32794,10,{NULL},S_VILE_HEAL2,0,0},\t// S_VILE_HEAL1\n    {SPR_VILE,32795,10,{NULL},S_VILE_HEAL3,0,0},\t// S_VILE_HEAL2\n    {SPR_VILE,32796,10,{NULL},S_VILE_RUN1,0,0},\t// S_VILE_HEAL3\n    {SPR_VILE,16,5,{NULL},S_VILE_PAIN2,0,0},\t// S_VILE_PAIN\n    {SPR_VILE,16,5,{A_Pain},S_VILE_RUN1,0,0},\t// S_VILE_PAIN2\n    {SPR_VILE,16,7,{NULL},S_VILE_DIE2,0,0},\t// S_VILE_DIE1\n    {SPR_VILE,17,7,{A_Scream},S_VILE_DIE3,0,0},\t// S_VILE_DIE2\n    {SPR_VILE,18,7,{A_Fall},S_VILE_DIE4,0,0},\t// S_VILE_DIE3\n    {SPR_VILE,19,7,{NULL},S_VILE_DIE5,0,0},\t// S_VILE_DIE4\n    {SPR_VILE,20,7,{NULL},S_VILE_DIE6,0,0},\t// S_VILE_DIE5\n    {SPR_VILE,21,7,{NULL},S_VILE_DIE7,0,0},\t// S_VILE_DIE6\n    {SPR_VILE,22,7,{NULL},S_VILE_DIE8,0,0},\t// S_VILE_DIE7\n    {SPR_VILE,23,5,{NULL},S_VILE_DIE9,0,0},\t// S_VILE_DIE8\n    {SPR_VILE,24,5,{NULL},S_VILE_DIE10,0,0},\t// S_VILE_DIE9\n    {SPR_VILE,25,-1,{NULL},S_NULL,0,0},\t// S_VILE_DIE10\n    {SPR_FIRE,32768,2,{A_StartFire},S_FIRE2,0,0},\t// S_FIRE1\n    {SPR_FIRE,32769,2,{A_Fire},S_FIRE3,0,0},\t// S_FIRE2\n    {SPR_FIRE,32768,2,{A_Fire},S_FIRE4,0,0},\t// S_FIRE3\n    {SPR_FIRE,32769,2,{A_Fire},S_FIRE5,0,0},\t// S_FIRE4\n    {SPR_FIRE,32770,2,{A_FireCrackle},S_FIRE6,0,0},\t// S_FIRE5\n    {SPR_FIRE,32769,2,{A_Fire},S_FIRE7,0,0},\t// S_FIRE6\n    {SPR_FIRE,32770,2,{A_Fire},S_FIRE8,0,0},\t// S_FIRE7\n    {SPR_FIRE,32769,2,{A_Fire},S_FIRE9,0,0},\t// S_FIRE8\n    {SPR_FIRE,32770,2,{A_Fire},S_FIRE10,0,0},\t// S_FIRE9\n    {SPR_FIRE,32771,2,{A_Fire},S_FIRE11,0,0},\t// S_FIRE10\n    {SPR_FIRE,32770,2,{A_Fire},S_FIRE12,0,0},\t// S_FIRE11\n    {SPR_FIRE,32771,2,{A_Fire},S_FIRE13,0,0},\t// S_FIRE12\n    {SPR_FIRE,32770,2,{A_Fire},S_FIRE14,0,0},\t// S_FIRE13\n    {SPR_FIRE,32771,2,{A_Fire},S_FIRE15,0,0},\t// S_FIRE14\n    {SPR_FIRE,32772,2,{A_Fire},S_FIRE16,0,0},\t// S_FIRE15\n    {SPR_FIRE,32771,2,{A_Fire},S_FIRE17,0,0},\t// S_FIRE16\n    {SPR_FIRE,32772,2,{A_Fire},S_FIRE18,0,0},\t// S_FIRE17\n    {SPR_FIRE,32771,2,{A_Fire},S_FIRE19,0,0},\t// S_FIRE18\n    {SPR_FIRE,32772,2,{A_FireCrackle},S_FIRE20,0,0},\t// S_FIRE19\n    {SPR_FIRE,32773,2,{A_Fire},S_FIRE21,0,0},\t// S_FIRE20\n    {SPR_FIRE,32772,2,{A_Fire},S_FIRE22,0,0},\t// S_FIRE21\n    {SPR_FIRE,32773,2,{A_Fire},S_FIRE23,0,0},\t// S_FIRE22\n    {SPR_FIRE,32772,2,{A_Fire},S_FIRE24,0,0},\t// S_FIRE23\n    {SPR_FIRE,32773,2,{A_Fire},S_FIRE25,0,0},\t// S_FIRE24\n    {SPR_FIRE,32774,2,{A_Fire},S_FIRE26,0,0},\t// S_FIRE25\n    {SPR_FIRE,32775,2,{A_Fire},S_FIRE27,0,0},\t// S_FIRE26\n    {SPR_FIRE,32774,2,{A_Fire},S_FIRE28,0,0},\t// S_FIRE27\n    {SPR_FIRE,32775,2,{A_Fire},S_FIRE29,0,0},\t// S_FIRE28\n    {SPR_FIRE,32774,2,{A_Fire},S_FIRE30,0,0},\t// S_FIRE29\n    {SPR_FIRE,32775,2,{A_Fire},S_NULL,0,0},\t// S_FIRE30\n    {SPR_PUFF,1,4,{NULL},S_SMOKE2,0,0},\t// S_SMOKE1\n    {SPR_PUFF,2,4,{NULL},S_SMOKE3,0,0},\t// S_SMOKE2\n    {SPR_PUFF,1,4,{NULL},S_SMOKE4,0,0},\t// S_SMOKE3\n    {SPR_PUFF,2,4,{NULL},S_SMOKE5,0,0},\t// S_SMOKE4\n    {SPR_PUFF,3,4,{NULL},S_NULL,0,0},\t// S_SMOKE5\n    {SPR_FATB,32768,2,{A_Tracer},S_TRACER2,0,0},\t// S_TRACER\n    {SPR_FATB,32769,2,{A_Tracer},S_TRACER,0,0},\t// S_TRACER2\n    {SPR_FBXP,32768,8,{NULL},S_TRACEEXP2,0,0},\t// S_TRACEEXP1\n    {SPR_FBXP,32769,6,{NULL},S_TRACEEXP3,0,0},\t// S_TRACEEXP2\n    {SPR_FBXP,32770,4,{NULL},S_NULL,0,0},\t// S_TRACEEXP3\n    {SPR_SKEL,0,10,{A_Look},S_SKEL_STND2,0,0},\t// S_SKEL_STND\n    {SPR_SKEL,1,10,{A_Look},S_SKEL_STND,0,0},\t// S_SKEL_STND2\n    {SPR_SKEL,0,2,{A_Chase},S_SKEL_RUN2,0,0},\t// S_SKEL_RUN1\n    {SPR_SKEL,0,2,{A_Chase},S_SKEL_RUN3,0,0},\t// S_SKEL_RUN2\n    {SPR_SKEL,1,2,{A_Chase},S_SKEL_RUN4,0,0},\t// S_SKEL_RUN3\n    {SPR_SKEL,1,2,{A_Chase},S_SKEL_RUN5,0,0},\t// S_SKEL_RUN4\n    {SPR_SKEL,2,2,{A_Chase},S_SKEL_RUN6,0,0},\t// S_SKEL_RUN5\n    {SPR_SKEL,2,2,{A_Chase},S_SKEL_RUN7,0,0},\t// S_SKEL_RUN6\n    {SPR_SKEL,3,2,{A_Chase},S_SKEL_RUN8,0,0},\t// S_SKEL_RUN7\n    {SPR_SKEL,3,2,{A_Chase},S_SKEL_RUN9,0,0},\t// S_SKEL_RUN8\n    {SPR_SKEL,4,2,{A_Chase},S_SKEL_RUN10,0,0},\t// S_SKEL_RUN9\n    {SPR_SKEL,4,2,{A_Chase},S_SKEL_RUN11,0,0},\t// S_SKEL_RUN10\n    {SPR_SKEL,5,2,{A_Chase},S_SKEL_RUN12,0,0},\t// S_SKEL_RUN11\n    {SPR_SKEL,5,2,{A_Chase},S_SKEL_RUN1,0,0},\t// S_SKEL_RUN12\n    {SPR_SKEL,6,0,{A_FaceTarget},S_SKEL_FIST2,0,0},\t// S_SKEL_FIST1\n    {SPR_SKEL,6,6,{A_SkelWhoosh},S_SKEL_FIST3,0,0},\t// S_SKEL_FIST2\n    {SPR_SKEL,7,6,{A_FaceTarget},S_SKEL_FIST4,0,0},\t// S_SKEL_FIST3\n    {SPR_SKEL,8,6,{A_SkelFist},S_SKEL_RUN1,0,0},\t// S_SKEL_FIST4\n    {SPR_SKEL,32777,0,{A_FaceTarget},S_SKEL_MISS2,0,0},\t// S_SKEL_MISS1\n    {SPR_SKEL,32777,10,{A_FaceTarget},S_SKEL_MISS3,0,0},\t// S_SKEL_MISS2\n    {SPR_SKEL,10,10,{A_SkelMissile},S_SKEL_MISS4,0,0},\t// S_SKEL_MISS3\n    {SPR_SKEL,10,10,{A_FaceTarget},S_SKEL_RUN1,0,0},\t// S_SKEL_MISS4\n    {SPR_SKEL,11,5,{NULL},S_SKEL_PAIN2,0,0},\t// S_SKEL_PAIN\n    {SPR_SKEL,11,5,{A_Pain},S_SKEL_RUN1,0,0},\t// S_SKEL_PAIN2\n    {SPR_SKEL,11,7,{NULL},S_SKEL_DIE2,0,0},\t// S_SKEL_DIE1\n    {SPR_SKEL,12,7,{NULL},S_SKEL_DIE3,0,0},\t// S_SKEL_DIE2\n    {SPR_SKEL,13,7,{A_Scream},S_SKEL_DIE4,0,0},\t// S_SKEL_DIE3\n    {SPR_SKEL,14,7,{A_Fall},S_SKEL_DIE5,0,0},\t// S_SKEL_DIE4\n    {SPR_SKEL,15,7,{NULL},S_SKEL_DIE6,0,0},\t// S_SKEL_DIE5\n    {SPR_SKEL,16,-1,{NULL},S_NULL,0,0},\t// S_SKEL_DIE6\n    {SPR_SKEL,16,5,{NULL},S_SKEL_RAISE2,0,0},\t// S_SKEL_RAISE1\n    {SPR_SKEL,15,5,{NULL},S_SKEL_RAISE3,0,0},\t// S_SKEL_RAISE2\n    {SPR_SKEL,14,5,{NULL},S_SKEL_RAISE4,0,0},\t// S_SKEL_RAISE3\n    {SPR_SKEL,13,5,{NULL},S_SKEL_RAISE5,0,0},\t// S_SKEL_RAISE4\n    {SPR_SKEL,12,5,{NULL},S_SKEL_RAISE6,0,0},\t// S_SKEL_RAISE5\n    {SPR_SKEL,11,5,{NULL},S_SKEL_RUN1,0,0},\t// S_SKEL_RAISE6\n    {SPR_MANF,32768,4,{NULL},S_FATSHOT2,0,0},\t// S_FATSHOT1\n    {SPR_MANF,32769,4,{NULL},S_FATSHOT1,0,0},\t// S_FATSHOT2\n    {SPR_MISL,32769,8,{NULL},S_FATSHOTX2,0,0},\t// S_FATSHOTX1\n    {SPR_MISL,32770,6,{NULL},S_FATSHOTX3,0,0},\t// S_FATSHOTX2\n    {SPR_MISL,32771,4,{NULL},S_NULL,0,0},\t// S_FATSHOTX3\n    {SPR_FATT,0,15,{A_Look},S_FATT_STND2,0,0},\t// S_FATT_STND\n    {SPR_FATT,1,15,{A_Look},S_FATT_STND,0,0},\t// S_FATT_STND2\n    {SPR_FATT,0,4,{A_Chase},S_FATT_RUN2,0,0},\t// S_FATT_RUN1\n    {SPR_FATT,0,4,{A_Chase},S_FATT_RUN3,0,0},\t// S_FATT_RUN2\n    {SPR_FATT,1,4,{A_Chase},S_FATT_RUN4,0,0},\t// S_FATT_RUN3\n    {SPR_FATT,1,4,{A_Chase},S_FATT_RUN5,0,0},\t// S_FATT_RUN4\n    {SPR_FATT,2,4,{A_Chase},S_FATT_RUN6,0,0},\t// S_FATT_RUN5\n    {SPR_FATT,2,4,{A_Chase},S_FATT_RUN7,0,0},\t// S_FATT_RUN6\n    {SPR_FATT,3,4,{A_Chase},S_FATT_RUN8,0,0},\t// S_FATT_RUN7\n    {SPR_FATT,3,4,{A_Chase},S_FATT_RUN9,0,0},\t// S_FATT_RUN8\n    {SPR_FATT,4,4,{A_Chase},S_FATT_RUN10,0,0},\t// S_FATT_RUN9\n    {SPR_FATT,4,4,{A_Chase},S_FATT_RUN11,0,0},\t// S_FATT_RUN10\n    {SPR_FATT,5,4,{A_Chase},S_FATT_RUN12,0,0},\t// S_FATT_RUN11\n    {SPR_FATT,5,4,{A_Chase},S_FATT_RUN1,0,0},\t// S_FATT_RUN12\n    {SPR_FATT,6,20,{A_FatRaise},S_FATT_ATK2,0,0},\t// S_FATT_ATK1\n    {SPR_FATT,32775,10,{A_FatAttack1},S_FATT_ATK3,0,0},\t// S_FATT_ATK2\n    {SPR_FATT,8,5,{A_FaceTarget},S_FATT_ATK4,0,0},\t// S_FATT_ATK3\n    {SPR_FATT,6,5,{A_FaceTarget},S_FATT_ATK5,0,0},\t// S_FATT_ATK4\n    {SPR_FATT,32775,10,{A_FatAttack2},S_FATT_ATK6,0,0},\t// S_FATT_ATK5\n    {SPR_FATT,8,5,{A_FaceTarget},S_FATT_ATK7,0,0},\t// S_FATT_ATK6\n    {SPR_FATT,6,5,{A_FaceTarget},S_FATT_ATK8,0,0},\t// S_FATT_ATK7\n    {SPR_FATT,32775,10,{A_FatAttack3},S_FATT_ATK9,0,0},\t// S_FATT_ATK8\n    {SPR_FATT,8,5,{A_FaceTarget},S_FATT_ATK10,0,0},\t// S_FATT_ATK9\n    {SPR_FATT,6,5,{A_FaceTarget},S_FATT_RUN1,0,0},\t// S_FATT_ATK10\n    {SPR_FATT,9,3,{NULL},S_FATT_PAIN2,0,0},\t// S_FATT_PAIN\n    {SPR_FATT,9,3,{A_Pain},S_FATT_RUN1,0,0},\t// S_FATT_PAIN2\n    {SPR_FATT,10,6,{NULL},S_FATT_DIE2,0,0},\t// S_FATT_DIE1\n    {SPR_FATT,11,6,{A_Scream},S_FATT_DIE3,0,0},\t// S_FATT_DIE2\n    {SPR_FATT,12,6,{A_Fall},S_FATT_DIE4,0,0},\t// S_FATT_DIE3\n    {SPR_FATT,13,6,{NULL},S_FATT_DIE5,0,0},\t// S_FATT_DIE4\n    {SPR_FATT,14,6,{NULL},S_FATT_DIE6,0,0},\t// S_FATT_DIE5\n    {SPR_FATT,15,6,{NULL},S_FATT_DIE7,0,0},\t// S_FATT_DIE6\n    {SPR_FATT,16,6,{NULL},S_FATT_DIE8,0,0},\t// S_FATT_DIE7\n    {SPR_FATT,17,6,{NULL},S_FATT_DIE9,0,0},\t// S_FATT_DIE8\n    {SPR_FATT,18,6,{NULL},S_FATT_DIE10,0,0},\t// S_FATT_DIE9\n    {SPR_FATT,19,-1,{A_BossDeath},S_NULL,0,0},\t// S_FATT_DIE10\n    {SPR_FATT,17,5,{NULL},S_FATT_RAISE2,0,0},\t// S_FATT_RAISE1\n    {SPR_FATT,16,5,{NULL},S_FATT_RAISE3,0,0},\t// S_FATT_RAISE2\n    {SPR_FATT,15,5,{NULL},S_FATT_RAISE4,0,0},\t// S_FATT_RAISE3\n    {SPR_FATT,14,5,{NULL},S_FATT_RAISE5,0,0},\t// S_FATT_RAISE4\n    {SPR_FATT,13,5,{NULL},S_FATT_RAISE6,0,0},\t// S_FATT_RAISE5\n    {SPR_FATT,12,5,{NULL},S_FATT_RAISE7,0,0},\t// S_FATT_RAISE6\n    {SPR_FATT,11,5,{NULL},S_FATT_RAISE8,0,0},\t// S_FATT_RAISE7\n    {SPR_FATT,10,5,{NULL},S_FATT_RUN1,0,0},\t// S_FATT_RAISE8\n    {SPR_CPOS,0,10,{A_Look},S_CPOS_STND2,0,0},\t// S_CPOS_STND\n    {SPR_CPOS,1,10,{A_Look},S_CPOS_STND,0,0},\t// S_CPOS_STND2\n    {SPR_CPOS,0,3,{A_Chase},S_CPOS_RUN2,0,0},\t// S_CPOS_RUN1\n    {SPR_CPOS,0,3,{A_Chase},S_CPOS_RUN3,0,0},\t// S_CPOS_RUN2\n    {SPR_CPOS,1,3,{A_Chase},S_CPOS_RUN4,0,0},\t// S_CPOS_RUN3\n    {SPR_CPOS,1,3,{A_Chase},S_CPOS_RUN5,0,0},\t// S_CPOS_RUN4\n    {SPR_CPOS,2,3,{A_Chase},S_CPOS_RUN6,0,0},\t// S_CPOS_RUN5\n    {SPR_CPOS,2,3,{A_Chase},S_CPOS_RUN7,0,0},\t// S_CPOS_RUN6\n    {SPR_CPOS,3,3,{A_Chase},S_CPOS_RUN8,0,0},\t// S_CPOS_RUN7\n    {SPR_CPOS,3,3,{A_Chase},S_CPOS_RUN1,0,0},\t// S_CPOS_RUN8\n    {SPR_CPOS,4,10,{A_FaceTarget},S_CPOS_ATK2,0,0},\t// S_CPOS_ATK1\n    {SPR_CPOS,32773,4,{A_CPosAttack},S_CPOS_ATK3,0,0},\t// S_CPOS_ATK2\n    {SPR_CPOS,32772,4,{A_CPosAttack},S_CPOS_ATK4,0,0},\t// S_CPOS_ATK3\n    {SPR_CPOS,5,1,{A_CPosRefire},S_CPOS_ATK2,0,0},\t// S_CPOS_ATK4\n    {SPR_CPOS,6,3,{NULL},S_CPOS_PAIN2,0,0},\t// S_CPOS_PAIN\n    {SPR_CPOS,6,3,{A_Pain},S_CPOS_RUN1,0,0},\t// S_CPOS_PAIN2\n    {SPR_CPOS,7,5,{NULL},S_CPOS_DIE2,0,0},\t// S_CPOS_DIE1\n    {SPR_CPOS,8,5,{A_Scream},S_CPOS_DIE3,0,0},\t// S_CPOS_DIE2\n    {SPR_CPOS,9,5,{A_Fall},S_CPOS_DIE4,0,0},\t// S_CPOS_DIE3\n    {SPR_CPOS,10,5,{NULL},S_CPOS_DIE5,0,0},\t// S_CPOS_DIE4\n    {SPR_CPOS,11,5,{NULL},S_CPOS_DIE6,0,0},\t// S_CPOS_DIE5\n    {SPR_CPOS,12,5,{NULL},S_CPOS_DIE7,0,0},\t// S_CPOS_DIE6\n    {SPR_CPOS,13,-1,{NULL},S_NULL,0,0},\t// S_CPOS_DIE7\n    {SPR_CPOS,14,5,{NULL},S_CPOS_XDIE2,0,0},\t// S_CPOS_XDIE1\n    {SPR_CPOS,15,5,{A_XScream},S_CPOS_XDIE3,0,0},\t// S_CPOS_XDIE2\n    {SPR_CPOS,16,5,{A_Fall},S_CPOS_XDIE4,0,0},\t// S_CPOS_XDIE3\n    {SPR_CPOS,17,5,{NULL},S_CPOS_XDIE5,0,0},\t// S_CPOS_XDIE4\n    {SPR_CPOS,18,5,{NULL},S_CPOS_XDIE6,0,0},\t// S_CPOS_XDIE5\n    {SPR_CPOS,19,-1,{NULL},S_NULL,0,0},\t// S_CPOS_XDIE6\n    {SPR_CPOS,13,5,{NULL},S_CPOS_RAISE2,0,0},\t// S_CPOS_RAISE1\n    {SPR_CPOS,12,5,{NULL},S_CPOS_RAISE3,0,0},\t// S_CPOS_RAISE2\n    {SPR_CPOS,11,5,{NULL},S_CPOS_RAISE4,0,0},\t// S_CPOS_RAISE3\n    {SPR_CPOS,10,5,{NULL},S_CPOS_RAISE5,0,0},\t// S_CPOS_RAISE4\n    {SPR_CPOS,9,5,{NULL},S_CPOS_RAISE6,0,0},\t// S_CPOS_RAISE5\n    {SPR_CPOS,8,5,{NULL},S_CPOS_RAISE7,0,0},\t// S_CPOS_RAISE6\n    {SPR_CPOS,7,5,{NULL},S_CPOS_RUN1,0,0},\t// S_CPOS_RAISE7\n    {SPR_TROO,0,10,{A_Look},S_TROO_STND2,0,0},\t// S_TROO_STND\n    {SPR_TROO,1,10,{A_Look},S_TROO_STND,0,0},\t// S_TROO_STND2\n    {SPR_TROO,0,3,{A_Chase},S_TROO_RUN2,0,0},\t// S_TROO_RUN1\n    {SPR_TROO,0,3,{A_Chase},S_TROO_RUN3,0,0},\t// S_TROO_RUN2\n    {SPR_TROO,1,3,{A_Chase},S_TROO_RUN4,0,0},\t// S_TROO_RUN3\n    {SPR_TROO,1,3,{A_Chase},S_TROO_RUN5,0,0},\t// S_TROO_RUN4\n    {SPR_TROO,2,3,{A_Chase},S_TROO_RUN6,0,0},\t// S_TROO_RUN5\n    {SPR_TROO,2,3,{A_Chase},S_TROO_RUN7,0,0},\t// S_TROO_RUN6\n    {SPR_TROO,3,3,{A_Chase},S_TROO_RUN8,0,0},\t// S_TROO_RUN7\n    {SPR_TROO,3,3,{A_Chase},S_TROO_RUN1,0,0},\t// S_TROO_RUN8\n    {SPR_TROO,4,8,{A_FaceTarget},S_TROO_ATK2,0,0},\t// S_TROO_ATK1\n    {SPR_TROO,5,8,{A_FaceTarget},S_TROO_ATK3,0,0},\t// S_TROO_ATK2\n    {SPR_TROO,6,6,{A_TroopAttack},S_TROO_RUN1,0,0},\t// S_TROO_ATK3\n    {SPR_TROO,7,2,{NULL},S_TROO_PAIN2,0,0},\t// S_TROO_PAIN\n    {SPR_TROO,7,2,{A_Pain},S_TROO_RUN1,0,0},\t// S_TROO_PAIN2\n    {SPR_TROO,8,8,{NULL},S_TROO_DIE2,0,0},\t// S_TROO_DIE1\n    {SPR_TROO,9,8,{A_Scream},S_TROO_DIE3,0,0},\t// S_TROO_DIE2\n    {SPR_TROO,10,6,{NULL},S_TROO_DIE4,0,0},\t// S_TROO_DIE3\n    {SPR_TROO,11,6,{A_Fall},S_TROO_DIE5,0,0},\t// S_TROO_DIE4\n    {SPR_TROO,12,-1,{NULL},S_NULL,0,0},\t// S_TROO_DIE5\n    {SPR_TROO,13,5,{NULL},S_TROO_XDIE2,0,0},\t// S_TROO_XDIE1\n    {SPR_TROO,14,5,{A_XScream},S_TROO_XDIE3,0,0},\t// S_TROO_XDIE2\n    {SPR_TROO,15,5,{NULL},S_TROO_XDIE4,0,0},\t// S_TROO_XDIE3\n    {SPR_TROO,16,5,{A_Fall},S_TROO_XDIE5,0,0},\t// S_TROO_XDIE4\n    {SPR_TROO,17,5,{NULL},S_TROO_XDIE6,0,0},\t// S_TROO_XDIE5\n    {SPR_TROO,18,5,{NULL},S_TROO_XDIE7,0,0},\t// S_TROO_XDIE6\n    {SPR_TROO,19,5,{NULL},S_TROO_XDIE8,0,0},\t// S_TROO_XDIE7\n    {SPR_TROO,20,-1,{NULL},S_NULL,0,0},\t// S_TROO_XDIE8\n    {SPR_TROO,12,8,{NULL},S_TROO_RAISE2,0,0},\t// S_TROO_RAISE1\n    {SPR_TROO,11,8,{NULL},S_TROO_RAISE3,0,0},\t// S_TROO_RAISE2\n    {SPR_TROO,10,6,{NULL},S_TROO_RAISE4,0,0},\t// S_TROO_RAISE3\n    {SPR_TROO,9,6,{NULL},S_TROO_RAISE5,0,0},\t// S_TROO_RAISE4\n    {SPR_TROO,8,6,{NULL},S_TROO_RUN1,0,0},\t// S_TROO_RAISE5\n    {SPR_SARG,0,10,{A_Look},S_SARG_STND2,0,0},\t// S_SARG_STND\n    {SPR_SARG,1,10,{A_Look},S_SARG_STND,0,0},\t// S_SARG_STND2\n    {SPR_SARG,0,2,{A_Chase},S_SARG_RUN2,0,0},\t// S_SARG_RUN1\n    {SPR_SARG,0,2,{A_Chase},S_SARG_RUN3,0,0},\t// S_SARG_RUN2\n    {SPR_SARG,1,2,{A_Chase},S_SARG_RUN4,0,0},\t// S_SARG_RUN3\n    {SPR_SARG,1,2,{A_Chase},S_SARG_RUN5,0,0},\t// S_SARG_RUN4\n    {SPR_SARG,2,2,{A_Chase},S_SARG_RUN6,0,0},\t// S_SARG_RUN5\n    {SPR_SARG,2,2,{A_Chase},S_SARG_RUN7,0,0},\t// S_SARG_RUN6\n    {SPR_SARG,3,2,{A_Chase},S_SARG_RUN8,0,0},\t// S_SARG_RUN7\n    {SPR_SARG,3,2,{A_Chase},S_SARG_RUN1,0,0},\t// S_SARG_RUN8\n    {SPR_SARG,4,8,{A_FaceTarget},S_SARG_ATK2,0,0},\t// S_SARG_ATK1\n    {SPR_SARG,5,8,{A_FaceTarget},S_SARG_ATK3,0,0},\t// S_SARG_ATK2\n    {SPR_SARG,6,8,{A_SargAttack},S_SARG_RUN1,0,0},\t// S_SARG_ATK3\n    {SPR_SARG,7,2,{NULL},S_SARG_PAIN2,0,0},\t// S_SARG_PAIN\n    {SPR_SARG,7,2,{A_Pain},S_SARG_RUN1,0,0},\t// S_SARG_PAIN2\n    {SPR_SARG,8,8,{NULL},S_SARG_DIE2,0,0},\t// S_SARG_DIE1\n    {SPR_SARG,9,8,{A_Scream},S_SARG_DIE3,0,0},\t// S_SARG_DIE2\n    {SPR_SARG,10,4,{NULL},S_SARG_DIE4,0,0},\t// S_SARG_DIE3\n    {SPR_SARG,11,4,{A_Fall},S_SARG_DIE5,0,0},\t// S_SARG_DIE4\n    {SPR_SARG,12,4,{NULL},S_SARG_DIE6,0,0},\t// S_SARG_DIE5\n    {SPR_SARG,13,-1,{NULL},S_NULL,0,0},\t// S_SARG_DIE6\n    {SPR_SARG,13,5,{NULL},S_SARG_RAISE2,0,0},\t// S_SARG_RAISE1\n    {SPR_SARG,12,5,{NULL},S_SARG_RAISE3,0,0},\t// S_SARG_RAISE2\n    {SPR_SARG,11,5,{NULL},S_SARG_RAISE4,0,0},\t// S_SARG_RAISE3\n    {SPR_SARG,10,5,{NULL},S_SARG_RAISE5,0,0},\t// S_SARG_RAISE4\n    {SPR_SARG,9,5,{NULL},S_SARG_RAISE6,0,0},\t// S_SARG_RAISE5\n    {SPR_SARG,8,5,{NULL},S_SARG_RUN1,0,0},\t// S_SARG_RAISE6\n    {SPR_HEAD,0,10,{A_Look},S_HEAD_STND,0,0},\t// S_HEAD_STND\n    {SPR_HEAD,0,3,{A_Chase},S_HEAD_RUN1,0,0},\t// S_HEAD_RUN1\n    {SPR_HEAD,1,5,{A_FaceTarget},S_HEAD_ATK2,0,0},\t// S_HEAD_ATK1\n    {SPR_HEAD,2,5,{A_FaceTarget},S_HEAD_ATK3,0,0},\t// S_HEAD_ATK2\n    {SPR_HEAD,32771,5,{A_HeadAttack},S_HEAD_RUN1,0,0},\t// S_HEAD_ATK3\n    {SPR_HEAD,4,3,{NULL},S_HEAD_PAIN2,0,0},\t// S_HEAD_PAIN\n    {SPR_HEAD,4,3,{A_Pain},S_HEAD_PAIN3,0,0},\t// S_HEAD_PAIN2\n    {SPR_HEAD,5,6,{NULL},S_HEAD_RUN1,0,0},\t// S_HEAD_PAIN3\n    {SPR_HEAD,6,8,{NULL},S_HEAD_DIE2,0,0},\t// S_HEAD_DIE1\n    {SPR_HEAD,7,8,{A_Scream},S_HEAD_DIE3,0,0},\t// S_HEAD_DIE2\n    {SPR_HEAD,8,8,{NULL},S_HEAD_DIE4,0,0},\t// S_HEAD_DIE3\n    {SPR_HEAD,9,8,{NULL},S_HEAD_DIE5,0,0},\t// S_HEAD_DIE4\n    {SPR_HEAD,10,8,{A_Fall},S_HEAD_DIE6,0,0},\t// S_HEAD_DIE5\n    {SPR_HEAD,11,-1,{NULL},S_NULL,0,0},\t// S_HEAD_DIE6\n    {SPR_HEAD,11,8,{NULL},S_HEAD_RAISE2,0,0},\t// S_HEAD_RAISE1\n    {SPR_HEAD,10,8,{NULL},S_HEAD_RAISE3,0,0},\t// S_HEAD_RAISE2\n    {SPR_HEAD,9,8,{NULL},S_HEAD_RAISE4,0,0},\t// S_HEAD_RAISE3\n    {SPR_HEAD,8,8,{NULL},S_HEAD_RAISE5,0,0},\t// S_HEAD_RAISE4\n    {SPR_HEAD,7,8,{NULL},S_HEAD_RAISE6,0,0},\t// S_HEAD_RAISE5\n    {SPR_HEAD,6,8,{NULL},S_HEAD_RUN1,0,0},\t// S_HEAD_RAISE6\n    {SPR_BAL7,32768,4,{NULL},S_BRBALL2,0,0},\t// S_BRBALL1\n    {SPR_BAL7,32769,4,{NULL},S_BRBALL1,0,0},\t// S_BRBALL2\n    {SPR_BAL7,32770,6,{NULL},S_BRBALLX2,0,0},\t// S_BRBALLX1\n    {SPR_BAL7,32771,6,{NULL},S_BRBALLX3,0,0},\t// S_BRBALLX2\n    {SPR_BAL7,32772,6,{NULL},S_NULL,0,0},\t// S_BRBALLX3\n    {SPR_BOSS,0,10,{A_Look},S_BOSS_STND2,0,0},\t// S_BOSS_STND\n    {SPR_BOSS,1,10,{A_Look},S_BOSS_STND,0,0},\t// S_BOSS_STND2\n    {SPR_BOSS,0,3,{A_Chase},S_BOSS_RUN2,0,0},\t// S_BOSS_RUN1\n    {SPR_BOSS,0,3,{A_Chase},S_BOSS_RUN3,0,0},\t// S_BOSS_RUN2\n    {SPR_BOSS,1,3,{A_Chase},S_BOSS_RUN4,0,0},\t// S_BOSS_RUN3\n    {SPR_BOSS,1,3,{A_Chase},S_BOSS_RUN5,0,0},\t// S_BOSS_RUN4\n    {SPR_BOSS,2,3,{A_Chase},S_BOSS_RUN6,0,0},\t// S_BOSS_RUN5\n    {SPR_BOSS,2,3,{A_Chase},S_BOSS_RUN7,0,0},\t// S_BOSS_RUN6\n    {SPR_BOSS,3,3,{A_Chase},S_BOSS_RUN8,0,0},\t// S_BOSS_RUN7\n    {SPR_BOSS,3,3,{A_Chase},S_BOSS_RUN1,0,0},\t// S_BOSS_RUN8\n    {SPR_BOSS,4,8,{A_FaceTarget},S_BOSS_ATK2,0,0},\t// S_BOSS_ATK1\n    {SPR_BOSS,5,8,{A_FaceTarget},S_BOSS_ATK3,0,0},\t// S_BOSS_ATK2\n    {SPR_BOSS,6,8,{A_BruisAttack},S_BOSS_RUN1,0,0},\t// S_BOSS_ATK3\n    {SPR_BOSS,7,2,{NULL},S_BOSS_PAIN2,0,0},\t// S_BOSS_PAIN\n    {SPR_BOSS,7,2,{A_Pain},S_BOSS_RUN1,0,0},\t// S_BOSS_PAIN2\n    {SPR_BOSS,8,8,{NULL},S_BOSS_DIE2,0,0},\t// S_BOSS_DIE1\n    {SPR_BOSS,9,8,{A_Scream},S_BOSS_DIE3,0,0},\t// S_BOSS_DIE2\n    {SPR_BOSS,10,8,{NULL},S_BOSS_DIE4,0,0},\t// S_BOSS_DIE3\n    {SPR_BOSS,11,8,{A_Fall},S_BOSS_DIE5,0,0},\t// S_BOSS_DIE4\n    {SPR_BOSS,12,8,{NULL},S_BOSS_DIE6,0,0},\t// S_BOSS_DIE5\n    {SPR_BOSS,13,8,{NULL},S_BOSS_DIE7,0,0},\t// S_BOSS_DIE6\n    {SPR_BOSS,14,-1,{A_BossDeath},S_NULL,0,0},\t// S_BOSS_DIE7\n    {SPR_BOSS,14,8,{NULL},S_BOSS_RAISE2,0,0},\t// S_BOSS_RAISE1\n    {SPR_BOSS,13,8,{NULL},S_BOSS_RAISE3,0,0},\t// S_BOSS_RAISE2\n    {SPR_BOSS,12,8,{NULL},S_BOSS_RAISE4,0,0},\t// S_BOSS_RAISE3\n    {SPR_BOSS,11,8,{NULL},S_BOSS_RAISE5,0,0},\t// S_BOSS_RAISE4\n    {SPR_BOSS,10,8,{NULL},S_BOSS_RAISE6,0,0},\t// S_BOSS_RAISE5\n    {SPR_BOSS,9,8,{NULL},S_BOSS_RAISE7,0,0},\t// S_BOSS_RAISE6\n    {SPR_BOSS,8,8,{NULL},S_BOSS_RUN1,0,0},\t// S_BOSS_RAISE7\n    {SPR_BOS2,0,10,{A_Look},S_BOS2_STND2,0,0},\t// S_BOS2_STND\n    {SPR_BOS2,1,10,{A_Look},S_BOS2_STND,0,0},\t// S_BOS2_STND2\n    {SPR_BOS2,0,3,{A_Chase},S_BOS2_RUN2,0,0},\t// S_BOS2_RUN1\n    {SPR_BOS2,0,3,{A_Chase},S_BOS2_RUN3,0,0},\t// S_BOS2_RUN2\n    {SPR_BOS2,1,3,{A_Chase},S_BOS2_RUN4,0,0},\t// S_BOS2_RUN3\n    {SPR_BOS2,1,3,{A_Chase},S_BOS2_RUN5,0,0},\t// S_BOS2_RUN4\n    {SPR_BOS2,2,3,{A_Chase},S_BOS2_RUN6,0,0},\t// S_BOS2_RUN5\n    {SPR_BOS2,2,3,{A_Chase},S_BOS2_RUN7,0,0},\t// S_BOS2_RUN6\n    {SPR_BOS2,3,3,{A_Chase},S_BOS2_RUN8,0,0},\t// S_BOS2_RUN7\n    {SPR_BOS2,3,3,{A_Chase},S_BOS2_RUN1,0,0},\t// S_BOS2_RUN8\n    {SPR_BOS2,4,8,{A_FaceTarget},S_BOS2_ATK2,0,0},\t// S_BOS2_ATK1\n    {SPR_BOS2,5,8,{A_FaceTarget},S_BOS2_ATK3,0,0},\t// S_BOS2_ATK2\n    {SPR_BOS2,6,8,{A_BruisAttack},S_BOS2_RUN1,0,0},\t// S_BOS2_ATK3\n    {SPR_BOS2,7,2,{NULL},S_BOS2_PAIN2,0,0},\t// S_BOS2_PAIN\n    {SPR_BOS2,7,2,{A_Pain},S_BOS2_RUN1,0,0},\t// S_BOS2_PAIN2\n    {SPR_BOS2,8,8,{NULL},S_BOS2_DIE2,0,0},\t// S_BOS2_DIE1\n    {SPR_BOS2,9,8,{A_Scream},S_BOS2_DIE3,0,0},\t// S_BOS2_DIE2\n    {SPR_BOS2,10,8,{NULL},S_BOS2_DIE4,0,0},\t// S_BOS2_DIE3\n    {SPR_BOS2,11,8,{A_Fall},S_BOS2_DIE5,0,0},\t// S_BOS2_DIE4\n    {SPR_BOS2,12,8,{NULL},S_BOS2_DIE6,0,0},\t// S_BOS2_DIE5\n    {SPR_BOS2,13,8,{NULL},S_BOS2_DIE7,0,0},\t// S_BOS2_DIE6\n    {SPR_BOS2,14,-1,{NULL},S_NULL,0,0},\t// S_BOS2_DIE7\n    {SPR_BOS2,14,8,{NULL},S_BOS2_RAISE2,0,0},\t// S_BOS2_RAISE1\n    {SPR_BOS2,13,8,{NULL},S_BOS2_RAISE3,0,0},\t// S_BOS2_RAISE2\n    {SPR_BOS2,12,8,{NULL},S_BOS2_RAISE4,0,0},\t// S_BOS2_RAISE3\n    {SPR_BOS2,11,8,{NULL},S_BOS2_RAISE5,0,0},\t// S_BOS2_RAISE4\n    {SPR_BOS2,10,8,{NULL},S_BOS2_RAISE6,0,0},\t// S_BOS2_RAISE5\n    {SPR_BOS2,9,8,{NULL},S_BOS2_RAISE7,0,0},\t// S_BOS2_RAISE6\n    {SPR_BOS2,8,8,{NULL},S_BOS2_RUN1,0,0},\t// S_BOS2_RAISE7\n    {SPR_SKUL,32768,10,{A_Look},S_SKULL_STND2,0,0},\t// S_SKULL_STND\n    {SPR_SKUL,32769,10,{A_Look},S_SKULL_STND,0,0},\t// S_SKULL_STND2\n    {SPR_SKUL,32768,6,{A_Chase},S_SKULL_RUN2,0,0},\t// S_SKULL_RUN1\n    {SPR_SKUL,32769,6,{A_Chase},S_SKULL_RUN1,0,0},\t// S_SKULL_RUN2\n    {SPR_SKUL,32770,10,{A_FaceTarget},S_SKULL_ATK2,0,0},\t// S_SKULL_ATK1\n    {SPR_SKUL,32771,4,{A_SkullAttack},S_SKULL_ATK3,0,0},\t// S_SKULL_ATK2\n    {SPR_SKUL,32770,4,{NULL},S_SKULL_ATK4,0,0},\t// S_SKULL_ATK3\n    {SPR_SKUL,32771,4,{NULL},S_SKULL_ATK3,0,0},\t// S_SKULL_ATK4\n    {SPR_SKUL,32772,3,{NULL},S_SKULL_PAIN2,0,0},\t// S_SKULL_PAIN\n    {SPR_SKUL,32772,3,{A_Pain},S_SKULL_RUN1,0,0},\t// S_SKULL_PAIN2\n    {SPR_SKUL,32773,6,{NULL},S_SKULL_DIE2,0,0},\t// S_SKULL_DIE1\n    {SPR_SKUL,32774,6,{A_Scream},S_SKULL_DIE3,0,0},\t// S_SKULL_DIE2\n    {SPR_SKUL,32775,6,{NULL},S_SKULL_DIE4,0,0},\t// S_SKULL_DIE3\n    {SPR_SKUL,32776,6,{A_Fall},S_SKULL_DIE5,0,0},\t// S_SKULL_DIE4\n    {SPR_SKUL,9,6,{NULL},S_SKULL_DIE6,0,0},\t// S_SKULL_DIE5\n    {SPR_SKUL,10,6,{NULL},S_NULL,0,0},\t// S_SKULL_DIE6\n    {SPR_SPID,0,10,{A_Look},S_SPID_STND2,0,0},\t// S_SPID_STND\n    {SPR_SPID,1,10,{A_Look},S_SPID_STND,0,0},\t// S_SPID_STND2\n    {SPR_SPID,0,3,{A_Metal},S_SPID_RUN2,0,0},\t// S_SPID_RUN1\n    {SPR_SPID,0,3,{A_Chase},S_SPID_RUN3,0,0},\t// S_SPID_RUN2\n    {SPR_SPID,1,3,{A_Chase},S_SPID_RUN4,0,0},\t// S_SPID_RUN3\n    {SPR_SPID,1,3,{A_Chase},S_SPID_RUN5,0,0},\t// S_SPID_RUN4\n    {SPR_SPID,2,3,{A_Metal},S_SPID_RUN6,0,0},\t// S_SPID_RUN5\n    {SPR_SPID,2,3,{A_Chase},S_SPID_RUN7,0,0},\t// S_SPID_RUN6\n    {SPR_SPID,3,3,{A_Chase},S_SPID_RUN8,0,0},\t// S_SPID_RUN7\n    {SPR_SPID,3,3,{A_Chase},S_SPID_RUN9,0,0},\t// S_SPID_RUN8\n    {SPR_SPID,4,3,{A_Metal},S_SPID_RUN10,0,0},\t// S_SPID_RUN9\n    {SPR_SPID,4,3,{A_Chase},S_SPID_RUN11,0,0},\t// S_SPID_RUN10\n    {SPR_SPID,5,3,{A_Chase},S_SPID_RUN12,0,0},\t// S_SPID_RUN11\n    {SPR_SPID,5,3,{A_Chase},S_SPID_RUN1,0,0},\t// S_SPID_RUN12\n    {SPR_SPID,32768,20,{A_FaceTarget},S_SPID_ATK2,0,0},\t// S_SPID_ATK1\n    {SPR_SPID,32774,4,{A_SPosAttack},S_SPID_ATK3,0,0},\t// S_SPID_ATK2\n    {SPR_SPID,32775,4,{A_SPosAttack},S_SPID_ATK4,0,0},\t// S_SPID_ATK3\n    {SPR_SPID,32775,1,{A_SpidRefire},S_SPID_ATK2,0,0},\t// S_SPID_ATK4\n    {SPR_SPID,8,3,{NULL},S_SPID_PAIN2,0,0},\t// S_SPID_PAIN\n    {SPR_SPID,8,3,{A_Pain},S_SPID_RUN1,0,0},\t// S_SPID_PAIN2\n    {SPR_SPID,9,20,{A_Scream},S_SPID_DIE2,0,0},\t// S_SPID_DIE1\n    {SPR_SPID,10,10,{A_Fall},S_SPID_DIE3,0,0},\t// S_SPID_DIE2\n    {SPR_SPID,11,10,{NULL},S_SPID_DIE4,0,0},\t// S_SPID_DIE3\n    {SPR_SPID,12,10,{NULL},S_SPID_DIE5,0,0},\t// S_SPID_DIE4\n    {SPR_SPID,13,10,{NULL},S_SPID_DIE6,0,0},\t// S_SPID_DIE5\n    {SPR_SPID,14,10,{NULL},S_SPID_DIE7,0,0},\t// S_SPID_DIE6\n    {SPR_SPID,15,10,{NULL},S_SPID_DIE8,0,0},\t// S_SPID_DIE7\n    {SPR_SPID,16,10,{NULL},S_SPID_DIE9,0,0},\t// S_SPID_DIE8\n    {SPR_SPID,17,10,{NULL},S_SPID_DIE10,0,0},\t// S_SPID_DIE9\n    {SPR_SPID,18,30,{NULL},S_SPID_DIE11,0,0},\t// S_SPID_DIE10\n    {SPR_SPID,18,-1,{A_BossDeath},S_NULL,0,0},\t// S_SPID_DIE11\n    {SPR_BSPI,0,10,{A_Look},S_BSPI_STND2,0,0},\t// S_BSPI_STND\n    {SPR_BSPI,1,10,{A_Look},S_BSPI_STND,0,0},\t// S_BSPI_STND2\n    {SPR_BSPI,0,20,{NULL},S_BSPI_RUN1,0,0},\t// S_BSPI_SIGHT\n    {SPR_BSPI,0,3,{A_BabyMetal},S_BSPI_RUN2,0,0},\t// S_BSPI_RUN1\n    {SPR_BSPI,0,3,{A_Chase},S_BSPI_RUN3,0,0},\t// S_BSPI_RUN2\n    {SPR_BSPI,1,3,{A_Chase},S_BSPI_RUN4,0,0},\t// S_BSPI_RUN3\n    {SPR_BSPI,1,3,{A_Chase},S_BSPI_RUN5,0,0},\t// S_BSPI_RUN4\n    {SPR_BSPI,2,3,{A_Chase},S_BSPI_RUN6,0,0},\t// S_BSPI_RUN5\n    {SPR_BSPI,2,3,{A_Chase},S_BSPI_RUN7,0,0},\t// S_BSPI_RUN6\n    {SPR_BSPI,3,3,{A_BabyMetal},S_BSPI_RUN8,0,0},\t// S_BSPI_RUN7\n    {SPR_BSPI,3,3,{A_Chase},S_BSPI_RUN9,0,0},\t// S_BSPI_RUN8\n    {SPR_BSPI,4,3,{A_Chase},S_BSPI_RUN10,0,0},\t// S_BSPI_RUN9\n    {SPR_BSPI,4,3,{A_Chase},S_BSPI_RUN11,0,0},\t// S_BSPI_RUN10\n    {SPR_BSPI,5,3,{A_Chase},S_BSPI_RUN12,0,0},\t// S_BSPI_RUN11\n    {SPR_BSPI,5,3,{A_Chase},S_BSPI_RUN1,0,0},\t// S_BSPI_RUN12\n    {SPR_BSPI,32768,20,{A_FaceTarget},S_BSPI_ATK2,0,0},\t// S_BSPI_ATK1\n    {SPR_BSPI,32774,4,{A_BspiAttack},S_BSPI_ATK3,0,0},\t// S_BSPI_ATK2\n    {SPR_BSPI,32775,4,{NULL},S_BSPI_ATK4,0,0},\t// S_BSPI_ATK3\n    {SPR_BSPI,32775,1,{A_SpidRefire},S_BSPI_ATK2,0,0},\t// S_BSPI_ATK4\n    {SPR_BSPI,8,3,{NULL},S_BSPI_PAIN2,0,0},\t// S_BSPI_PAIN\n    {SPR_BSPI,8,3,{A_Pain},S_BSPI_RUN1,0,0},\t// S_BSPI_PAIN2\n    {SPR_BSPI,9,20,{A_Scream},S_BSPI_DIE2,0,0},\t// S_BSPI_DIE1\n    {SPR_BSPI,10,7,{A_Fall},S_BSPI_DIE3,0,0},\t// S_BSPI_DIE2\n    {SPR_BSPI,11,7,{NULL},S_BSPI_DIE4,0,0},\t// S_BSPI_DIE3\n    {SPR_BSPI,12,7,{NULL},S_BSPI_DIE5,0,0},\t// S_BSPI_DIE4\n    {SPR_BSPI,13,7,{NULL},S_BSPI_DIE6,0,0},\t// S_BSPI_DIE5\n    {SPR_BSPI,14,7,{NULL},S_BSPI_DIE7,0,0},\t// S_BSPI_DIE6\n    {SPR_BSPI,15,-1,{A_BossDeath},S_NULL,0,0},\t// S_BSPI_DIE7\n    {SPR_BSPI,15,5,{NULL},S_BSPI_RAISE2,0,0},\t// S_BSPI_RAISE1\n    {SPR_BSPI,14,5,{NULL},S_BSPI_RAISE3,0,0},\t// S_BSPI_RAISE2\n    {SPR_BSPI,13,5,{NULL},S_BSPI_RAISE4,0,0},\t// S_BSPI_RAISE3\n    {SPR_BSPI,12,5,{NULL},S_BSPI_RAISE5,0,0},\t// S_BSPI_RAISE4\n    {SPR_BSPI,11,5,{NULL},S_BSPI_RAISE6,0,0},\t// S_BSPI_RAISE5\n    {SPR_BSPI,10,5,{NULL},S_BSPI_RAISE7,0,0},\t// S_BSPI_RAISE6\n    {SPR_BSPI,9,5,{NULL},S_BSPI_RUN1,0,0},\t// S_BSPI_RAISE7\n    {SPR_APLS,32768,5,{NULL},S_ARACH_PLAZ2,0,0},\t// S_ARACH_PLAZ\n    {SPR_APLS,32769,5,{NULL},S_ARACH_PLAZ,0,0},\t// S_ARACH_PLAZ2\n    {SPR_APBX,32768,5,{NULL},S_ARACH_PLEX2,0,0},\t// S_ARACH_PLEX\n    {SPR_APBX,32769,5,{NULL},S_ARACH_PLEX3,0,0},\t// S_ARACH_PLEX2\n    {SPR_APBX,32770,5,{NULL},S_ARACH_PLEX4,0,0},\t// S_ARACH_PLEX3\n    {SPR_APBX,32771,5,{NULL},S_ARACH_PLEX5,0,0},\t// S_ARACH_PLEX4\n    {SPR_APBX,32772,5,{NULL},S_NULL,0,0},\t// S_ARACH_PLEX5\n    {SPR_CYBR,0,10,{A_Look},S_CYBER_STND2,0,0},\t// S_CYBER_STND\n    {SPR_CYBR,1,10,{A_Look},S_CYBER_STND,0,0},\t// S_CYBER_STND2\n    {SPR_CYBR,0,3,{A_Hoof},S_CYBER_RUN2,0,0},\t// S_CYBER_RUN1\n    {SPR_CYBR,0,3,{A_Chase},S_CYBER_RUN3,0,0},\t// S_CYBER_RUN2\n    {SPR_CYBR,1,3,{A_Chase},S_CYBER_RUN4,0,0},\t// S_CYBER_RUN3\n    {SPR_CYBR,1,3,{A_Chase},S_CYBER_RUN5,0,0},\t// S_CYBER_RUN4\n    {SPR_CYBR,2,3,{A_Chase},S_CYBER_RUN6,0,0},\t// S_CYBER_RUN5\n    {SPR_CYBR,2,3,{A_Chase},S_CYBER_RUN7,0,0},\t// S_CYBER_RUN6\n    {SPR_CYBR,3,3,{A_Metal},S_CYBER_RUN8,0,0},\t// S_CYBER_RUN7\n    {SPR_CYBR,3,3,{A_Chase},S_CYBER_RUN1,0,0},\t// S_CYBER_RUN8\n    {SPR_CYBR,4,6,{A_FaceTarget},S_CYBER_ATK2,0,0},\t// S_CYBER_ATK1\n    {SPR_CYBR,5,12,{A_CyberAttack},S_CYBER_ATK3,0,0},\t// S_CYBER_ATK2\n    {SPR_CYBR,4,12,{A_FaceTarget},S_CYBER_ATK4,0,0},\t// S_CYBER_ATK3\n    {SPR_CYBR,5,12,{A_CyberAttack},S_CYBER_ATK5,0,0},\t// S_CYBER_ATK4\n    {SPR_CYBR,4,12,{A_FaceTarget},S_CYBER_ATK6,0,0},\t// S_CYBER_ATK5\n    {SPR_CYBR,5,12,{A_CyberAttack},S_CYBER_RUN1,0,0},\t// S_CYBER_ATK6\n    {SPR_CYBR,6,10,{A_Pain},S_CYBER_RUN1,0,0},\t// S_CYBER_PAIN\n    {SPR_CYBR,7,10,{NULL},S_CYBER_DIE2,0,0},\t// S_CYBER_DIE1\n    {SPR_CYBR,8,10,{A_Scream},S_CYBER_DIE3,0,0},\t// S_CYBER_DIE2\n    {SPR_CYBR,9,10,{NULL},S_CYBER_DIE4,0,0},\t// S_CYBER_DIE3\n    {SPR_CYBR,10,10,{NULL},S_CYBER_DIE5,0,0},\t// S_CYBER_DIE4\n    {SPR_CYBR,11,10,{NULL},S_CYBER_DIE6,0,0},\t// S_CYBER_DIE5\n    {SPR_CYBR,12,10,{A_Fall},S_CYBER_DIE7,0,0},\t// S_CYBER_DIE6\n    {SPR_CYBR,13,10,{NULL},S_CYBER_DIE8,0,0},\t// S_CYBER_DIE7\n    {SPR_CYBR,14,10,{NULL},S_CYBER_DIE9,0,0},\t// S_CYBER_DIE8\n    {SPR_CYBR,15,30,{NULL},S_CYBER_DIE10,0,0},\t// S_CYBER_DIE9\n    {SPR_CYBR,15,-1,{A_BossDeath},S_NULL,0,0},\t// S_CYBER_DIE10\n    {SPR_PAIN,0,10,{A_Look},S_PAIN_STND,0,0},\t// S_PAIN_STND\n    {SPR_PAIN,0,3,{A_Chase},S_PAIN_RUN2,0,0},\t// S_PAIN_RUN1\n    {SPR_PAIN,0,3,{A_Chase},S_PAIN_RUN3,0,0},\t// S_PAIN_RUN2\n    {SPR_PAIN,1,3,{A_Chase},S_PAIN_RUN4,0,0},\t// S_PAIN_RUN3\n    {SPR_PAIN,1,3,{A_Chase},S_PAIN_RUN5,0,0},\t// S_PAIN_RUN4\n    {SPR_PAIN,2,3,{A_Chase},S_PAIN_RUN6,0,0},\t// S_PAIN_RUN5\n    {SPR_PAIN,2,3,{A_Chase},S_PAIN_RUN1,0,0},\t// S_PAIN_RUN6\n    {SPR_PAIN,3,5,{A_FaceTarget},S_PAIN_ATK2,0,0},\t// S_PAIN_ATK1\n    {SPR_PAIN,4,5,{A_FaceTarget},S_PAIN_ATK3,0,0},\t// S_PAIN_ATK2\n    {SPR_PAIN,32773,5,{A_FaceTarget},S_PAIN_ATK4,0,0},\t// S_PAIN_ATK3\n    {SPR_PAIN,32773,0,{A_PainAttack},S_PAIN_RUN1,0,0},\t// S_PAIN_ATK4\n    {SPR_PAIN,6,6,{NULL},S_PAIN_PAIN2,0,0},\t// S_PAIN_PAIN\n    {SPR_PAIN,6,6,{A_Pain},S_PAIN_RUN1,0,0},\t// S_PAIN_PAIN2\n    {SPR_PAIN,32775,8,{NULL},S_PAIN_DIE2,0,0},\t// S_PAIN_DIE1\n    {SPR_PAIN,32776,8,{A_Scream},S_PAIN_DIE3,0,0},\t// S_PAIN_DIE2\n    {SPR_PAIN,32777,8,{NULL},S_PAIN_DIE4,0,0},\t// S_PAIN_DIE3\n    {SPR_PAIN,32778,8,{NULL},S_PAIN_DIE5,0,0},\t// S_PAIN_DIE4\n    {SPR_PAIN,32779,8,{A_PainDie},S_PAIN_DIE6,0,0},\t// S_PAIN_DIE5\n    {SPR_PAIN,32780,8,{NULL},S_NULL,0,0},\t// S_PAIN_DIE6\n    {SPR_PAIN,12,8,{NULL},S_PAIN_RAISE2,0,0},\t// S_PAIN_RAISE1\n    {SPR_PAIN,11,8,{NULL},S_PAIN_RAISE3,0,0},\t// S_PAIN_RAISE2\n    {SPR_PAIN,10,8,{NULL},S_PAIN_RAISE4,0,0},\t// S_PAIN_RAISE3\n    {SPR_PAIN,9,8,{NULL},S_PAIN_RAISE5,0,0},\t// S_PAIN_RAISE4\n    {SPR_PAIN,8,8,{NULL},S_PAIN_RAISE6,0,0},\t// S_PAIN_RAISE5\n    {SPR_PAIN,7,8,{NULL},S_PAIN_RUN1,0,0},\t// S_PAIN_RAISE6\n    {SPR_SSWV,0,10,{A_Look},S_SSWV_STND2,0,0},\t// S_SSWV_STND\n    {SPR_SSWV,1,10,{A_Look},S_SSWV_STND,0,0},\t// S_SSWV_STND2\n    {SPR_SSWV,0,3,{A_Chase},S_SSWV_RUN2,0,0},\t// S_SSWV_RUN1\n    {SPR_SSWV,0,3,{A_Chase},S_SSWV_RUN3,0,0},\t// S_SSWV_RUN2\n    {SPR_SSWV,1,3,{A_Chase},S_SSWV_RUN4,0,0},\t// S_SSWV_RUN3\n    {SPR_SSWV,1,3,{A_Chase},S_SSWV_RUN5,0,0},\t// S_SSWV_RUN4\n    {SPR_SSWV,2,3,{A_Chase},S_SSWV_RUN6,0,0},\t// S_SSWV_RUN5\n    {SPR_SSWV,2,3,{A_Chase},S_SSWV_RUN7,0,0},\t// S_SSWV_RUN6\n    {SPR_SSWV,3,3,{A_Chase},S_SSWV_RUN8,0,0},\t// S_SSWV_RUN7\n    {SPR_SSWV,3,3,{A_Chase},S_SSWV_RUN1,0,0},\t// S_SSWV_RUN8\n    {SPR_SSWV,4,10,{A_FaceTarget},S_SSWV_ATK2,0,0},\t// S_SSWV_ATK1\n    {SPR_SSWV,5,10,{A_FaceTarget},S_SSWV_ATK3,0,0},\t// S_SSWV_ATK2\n    {SPR_SSWV,32774,4,{A_CPosAttack},S_SSWV_ATK4,0,0},\t// S_SSWV_ATK3\n    {SPR_SSWV,5,6,{A_FaceTarget},S_SSWV_ATK5,0,0},\t// S_SSWV_ATK4\n    {SPR_SSWV,32774,4,{A_CPosAttack},S_SSWV_ATK6,0,0},\t// S_SSWV_ATK5\n    {SPR_SSWV,5,1,{A_CPosRefire},S_SSWV_ATK2,0,0},\t// S_SSWV_ATK6\n    {SPR_SSWV,7,3,{NULL},S_SSWV_PAIN2,0,0},\t// S_SSWV_PAIN\n    {SPR_SSWV,7,3,{A_Pain},S_SSWV_RUN1,0,0},\t// S_SSWV_PAIN2\n    {SPR_SSWV,8,5,{NULL},S_SSWV_DIE2,0,0},\t// S_SSWV_DIE1\n    {SPR_SSWV,9,5,{A_Scream},S_SSWV_DIE3,0,0},\t// S_SSWV_DIE2\n    {SPR_SSWV,10,5,{A_Fall},S_SSWV_DIE4,0,0},\t// S_SSWV_DIE3\n    {SPR_SSWV,11,5,{NULL},S_SSWV_DIE5,0,0},\t// S_SSWV_DIE4\n    {SPR_SSWV,12,-1,{NULL},S_NULL,0,0},\t// S_SSWV_DIE5\n    {SPR_SSWV,13,5,{NULL},S_SSWV_XDIE2,0,0},\t// S_SSWV_XDIE1\n    {SPR_SSWV,14,5,{A_XScream},S_SSWV_XDIE3,0,0},\t// S_SSWV_XDIE2\n    {SPR_SSWV,15,5,{A_Fall},S_SSWV_XDIE4,0,0},\t// S_SSWV_XDIE3\n    {SPR_SSWV,16,5,{NULL},S_SSWV_XDIE5,0,0},\t// S_SSWV_XDIE4\n    {SPR_SSWV,17,5,{NULL},S_SSWV_XDIE6,0,0},\t// S_SSWV_XDIE5\n    {SPR_SSWV,18,5,{NULL},S_SSWV_XDIE7,0,0},\t// S_SSWV_XDIE6\n    {SPR_SSWV,19,5,{NULL},S_SSWV_XDIE8,0,0},\t// S_SSWV_XDIE7\n    {SPR_SSWV,20,5,{NULL},S_SSWV_XDIE9,0,0},\t// S_SSWV_XDIE8\n    {SPR_SSWV,21,-1,{NULL},S_NULL,0,0},\t// S_SSWV_XDIE9\n    {SPR_SSWV,12,5,{NULL},S_SSWV_RAISE2,0,0},\t// S_SSWV_RAISE1\n    {SPR_SSWV,11,5,{NULL},S_SSWV_RAISE3,0,0},\t// S_SSWV_RAISE2\n    {SPR_SSWV,10,5,{NULL},S_SSWV_RAISE4,0,0},\t// S_SSWV_RAISE3\n    {SPR_SSWV,9,5,{NULL},S_SSWV_RAISE5,0,0},\t// S_SSWV_RAISE4\n    {SPR_SSWV,8,5,{NULL},S_SSWV_RUN1,0,0},\t// S_SSWV_RAISE5\n    {SPR_KEEN,0,-1,{NULL},S_KEENSTND,0,0},\t// S_KEENSTND\n    {SPR_KEEN,0,6,{NULL},S_COMMKEEN2,0,0},\t// S_COMMKEEN\n    {SPR_KEEN,1,6,{NULL},S_COMMKEEN3,0,0},\t// S_COMMKEEN2\n    {SPR_KEEN,2,6,{A_Scream},S_COMMKEEN4,0,0},\t// S_COMMKEEN3\n    {SPR_KEEN,3,6,{NULL},S_COMMKEEN5,0,0},\t// S_COMMKEEN4\n    {SPR_KEEN,4,6,{NULL},S_COMMKEEN6,0,0},\t// S_COMMKEEN5\n    {SPR_KEEN,5,6,{NULL},S_COMMKEEN7,0,0},\t// S_COMMKEEN6\n    {SPR_KEEN,6,6,{NULL},S_COMMKEEN8,0,0},\t// S_COMMKEEN7\n    {SPR_KEEN,7,6,{NULL},S_COMMKEEN9,0,0},\t// S_COMMKEEN8\n    {SPR_KEEN,8,6,{NULL},S_COMMKEEN10,0,0},\t// S_COMMKEEN9\n    {SPR_KEEN,9,6,{NULL},S_COMMKEEN11,0,0},\t// S_COMMKEEN10\n    {SPR_KEEN,10,6,{A_KeenDie},S_COMMKEEN12,0,0},// S_COMMKEEN11\n    {SPR_KEEN,11,-1,{NULL},S_NULL,0,0},\t\t// S_COMMKEEN12\n    {SPR_KEEN,12,4,{NULL},S_KEENPAIN2,0,0},\t// S_KEENPAIN\n    {SPR_KEEN,12,8,{A_Pain},S_KEENSTND,0,0},\t// S_KEENPAIN2\n    {SPR_BBRN,0,-1,{NULL},S_NULL,0,0},\t\t// S_BRAIN\n    {SPR_BBRN,1,36,{A_BrainPain},S_BRAIN,0,0},\t// S_BRAIN_PAIN\n    {SPR_BBRN,0,100,{A_BrainScream},S_BRAIN_DIE2,0,0},\t// S_BRAIN_DIE1\n    {SPR_BBRN,0,10,{NULL},S_BRAIN_DIE3,0,0},\t// S_BRAIN_DIE2\n    {SPR_BBRN,0,10,{NULL},S_BRAIN_DIE4,0,0},\t// S_BRAIN_DIE3\n    {SPR_BBRN,0,-1,{A_BrainDie},S_NULL,0,0},\t// S_BRAIN_DIE4\n    {SPR_SSWV,0,10,{A_Look},S_BRAINEYE,0,0},\t// S_BRAINEYE\n    {SPR_SSWV,0,181,{A_BrainAwake},S_BRAINEYE1,0,0},\t// S_BRAINEYESEE\n    {SPR_SSWV,0,150,{A_BrainSpit},S_BRAINEYE1,0,0},\t// S_BRAINEYE1\n    {SPR_BOSF,32768,3,{A_SpawnSound},S_SPAWN2,0,0},\t// S_SPAWN1\n    {SPR_BOSF,32769,3,{A_SpawnFly},S_SPAWN3,0,0},\t// S_SPAWN2\n    {SPR_BOSF,32770,3,{A_SpawnFly},S_SPAWN4,0,0},\t// S_SPAWN3\n    {SPR_BOSF,32771,3,{A_SpawnFly},S_SPAWN1,0,0},\t// S_SPAWN4\n    {SPR_FIRE,32768,4,{A_Fire},S_SPAWNFIRE2,0,0},\t// S_SPAWNFIRE1\n    {SPR_FIRE,32769,4,{A_Fire},S_SPAWNFIRE3,0,0},\t// S_SPAWNFIRE2\n    {SPR_FIRE,32770,4,{A_Fire},S_SPAWNFIRE4,0,0},\t// S_SPAWNFIRE3\n    {SPR_FIRE,32771,4,{A_Fire},S_SPAWNFIRE5,0,0},\t// S_SPAWNFIRE4\n    {SPR_FIRE,32772,4,{A_Fire},S_SPAWNFIRE6,0,0},\t// S_SPAWNFIRE5\n    {SPR_FIRE,32773,4,{A_Fire},S_SPAWNFIRE7,0,0},\t// S_SPAWNFIRE6\n    {SPR_FIRE,32774,4,{A_Fire},S_SPAWNFIRE8,0,0},\t// S_SPAWNFIRE7\n    {SPR_FIRE,32775,4,{A_Fire},S_NULL,0,0},\t\t// S_SPAWNFIRE8\n    {SPR_MISL,32769,10,{NULL},S_BRAINEXPLODE2,0,0},\t// S_BRAINEXPLODE1\n    {SPR_MISL,32770,10,{NULL},S_BRAINEXPLODE3,0,0},\t// S_BRAINEXPLODE2\n    {SPR_MISL,32771,10,{A_BrainExplode},S_NULL,0,0},\t// S_BRAINEXPLODE3\n    {SPR_ARM1,0,6,{NULL},S_ARM1A,0,0},\t// S_ARM1\n    {SPR_ARM1,32769,7,{NULL},S_ARM1,0,0},\t// S_ARM1A\n    {SPR_ARM2,0,6,{NULL},S_ARM2A,0,0},\t// S_ARM2\n    {SPR_ARM2,32769,6,{NULL},S_ARM2,0,0},\t// S_ARM2A\n    {SPR_BAR1,0,6,{NULL},S_BAR2,0,0},\t// S_BAR1\n    {SPR_BAR1,1,6,{NULL},S_BAR1,0,0},\t// S_BAR2\n    {SPR_BEXP,32768,5,{NULL},S_BEXP2,0,0},\t// S_BEXP\n    {SPR_BEXP,32769,5,{A_Scream},S_BEXP3,0,0},\t// S_BEXP2\n    {SPR_BEXP,32770,5,{NULL},S_BEXP4,0,0},\t// S_BEXP3\n    {SPR_BEXP,32771,10,{A_Explode},S_BEXP5,0,0},\t// S_BEXP4\n    {SPR_BEXP,32772,10,{NULL},S_NULL,0,0},\t// S_BEXP5\n    {SPR_FCAN,32768,4,{NULL},S_BBAR2,0,0},\t// S_BBAR1\n    {SPR_FCAN,32769,4,{NULL},S_BBAR3,0,0},\t// S_BBAR2\n    {SPR_FCAN,32770,4,{NULL},S_BBAR1,0,0},\t// S_BBAR3\n    {SPR_BON1,0,6,{NULL},S_BON1A,0,0},\t// S_BON1\n    {SPR_BON1,1,6,{NULL},S_BON1B,0,0},\t// S_BON1A\n    {SPR_BON1,2,6,{NULL},S_BON1C,0,0},\t// S_BON1B\n    {SPR_BON1,3,6,{NULL},S_BON1D,0,0},\t// S_BON1C\n    {SPR_BON1,2,6,{NULL},S_BON1E,0,0},\t// S_BON1D\n    {SPR_BON1,1,6,{NULL},S_BON1,0,0},\t// S_BON1E\n    {SPR_BON2,0,6,{NULL},S_BON2A,0,0},\t// S_BON2\n    {SPR_BON2,1,6,{NULL},S_BON2B,0,0},\t// S_BON2A\n    {SPR_BON2,2,6,{NULL},S_BON2C,0,0},\t// S_BON2B\n    {SPR_BON2,3,6,{NULL},S_BON2D,0,0},\t// S_BON2C\n    {SPR_BON2,2,6,{NULL},S_BON2E,0,0},\t// S_BON2D\n    {SPR_BON2,1,6,{NULL},S_BON2,0,0},\t// S_BON2E\n    {SPR_BKEY,0,10,{NULL},S_BKEY2,0,0},\t// S_BKEY\n    {SPR_BKEY,32769,10,{NULL},S_BKEY,0,0},\t// S_BKEY2\n    {SPR_RKEY,0,10,{NULL},S_RKEY2,0,0},\t// S_RKEY\n    {SPR_RKEY,32769,10,{NULL},S_RKEY,0,0},\t// S_RKEY2\n    {SPR_YKEY,0,10,{NULL},S_YKEY2,0,0},\t// S_YKEY\n    {SPR_YKEY,32769,10,{NULL},S_YKEY,0,0},\t// S_YKEY2\n    {SPR_BSKU,0,10,{NULL},S_BSKULL2,0,0},\t// S_BSKULL\n    {SPR_BSKU,32769,10,{NULL},S_BSKULL,0,0},\t// S_BSKULL2\n    {SPR_RSKU,0,10,{NULL},S_RSKULL2,0,0},\t// S_RSKULL\n    {SPR_RSKU,32769,10,{NULL},S_RSKULL,0,0},\t// S_RSKULL2\n    {SPR_YSKU,0,10,{NULL},S_YSKULL2,0,0},\t// S_YSKULL\n    {SPR_YSKU,32769,10,{NULL},S_YSKULL,0,0},\t// S_YSKULL2\n    {SPR_STIM,0,-1,{NULL},S_NULL,0,0},\t// S_STIM\n    {SPR_MEDI,0,-1,{NULL},S_NULL,0,0},\t// S_MEDI\n    {SPR_SOUL,32768,6,{NULL},S_SOUL2,0,0},\t// S_SOUL\n    {SPR_SOUL,32769,6,{NULL},S_SOUL3,0,0},\t// S_SOUL2\n    {SPR_SOUL,32770,6,{NULL},S_SOUL4,0,0},\t// S_SOUL3\n    {SPR_SOUL,32771,6,{NULL},S_SOUL5,0,0},\t// S_SOUL4\n    {SPR_SOUL,32770,6,{NULL},S_SOUL6,0,0},\t// S_SOUL5\n    {SPR_SOUL,32769,6,{NULL},S_SOUL,0,0},\t// S_SOUL6\n    {SPR_PINV,32768,6,{NULL},S_PINV2,0,0},\t// S_PINV\n    {SPR_PINV,32769,6,{NULL},S_PINV3,0,0},\t// S_PINV2\n    {SPR_PINV,32770,6,{NULL},S_PINV4,0,0},\t// S_PINV3\n    {SPR_PINV,32771,6,{NULL},S_PINV,0,0},\t// S_PINV4\n    {SPR_PSTR,32768,-1,{NULL},S_NULL,0,0},\t// S_PSTR\n    {SPR_PINS,32768,6,{NULL},S_PINS2,0,0},\t// S_PINS\n    {SPR_PINS,32769,6,{NULL},S_PINS3,0,0},\t// S_PINS2\n    {SPR_PINS,32770,6,{NULL},S_PINS4,0,0},\t// S_PINS3\n    {SPR_PINS,32771,6,{NULL},S_PINS,0,0},\t// S_PINS4\n    {SPR_MEGA,32768,6,{NULL},S_MEGA2,0,0},\t// S_MEGA\n    {SPR_MEGA,32769,6,{NULL},S_MEGA3,0,0},\t// S_MEGA2\n    {SPR_MEGA,32770,6,{NULL},S_MEGA4,0,0},\t// S_MEGA3\n    {SPR_MEGA,32771,6,{NULL},S_MEGA,0,0},\t// S_MEGA4\n    {SPR_SUIT,32768,-1,{NULL},S_NULL,0,0},\t// S_SUIT\n    {SPR_PMAP,32768,6,{NULL},S_PMAP2,0,0},\t// S_PMAP\n    {SPR_PMAP,32769,6,{NULL},S_PMAP3,0,0},\t// S_PMAP2\n    {SPR_PMAP,32770,6,{NULL},S_PMAP4,0,0},\t// S_PMAP3\n    {SPR_PMAP,32771,6,{NULL},S_PMAP5,0,0},\t// S_PMAP4\n    {SPR_PMAP,32770,6,{NULL},S_PMAP6,0,0},\t// S_PMAP5\n    {SPR_PMAP,32769,6,{NULL},S_PMAP,0,0},\t// S_PMAP6\n    {SPR_PVIS,32768,6,{NULL},S_PVIS2,0,0},\t// S_PVIS\n    {SPR_PVIS,1,6,{NULL},S_PVIS,0,0},\t// S_PVIS2\n    {SPR_CLIP,0,-1,{NULL},S_NULL,0,0},\t// S_CLIP\n    {SPR_AMMO,0,-1,{NULL},S_NULL,0,0},\t// S_AMMO\n    {SPR_ROCK,0,-1,{NULL},S_NULL,0,0},\t// S_ROCK\n    {SPR_BROK,0,-1,{NULL},S_NULL,0,0},\t// S_BROK\n    {SPR_CELL,0,-1,{NULL},S_NULL,0,0},\t// S_CELL\n    {SPR_CELP,0,-1,{NULL},S_NULL,0,0},\t// S_CELP\n    {SPR_SHEL,0,-1,{NULL},S_NULL,0,0},\t// S_SHEL\n    {SPR_SBOX,0,-1,{NULL},S_NULL,0,0},\t// S_SBOX\n    {SPR_BPAK,0,-1,{NULL},S_NULL,0,0},\t// S_BPAK\n    {SPR_BFUG,0,-1,{NULL},S_NULL,0,0},\t// S_BFUG\n    {SPR_MGUN,0,-1,{NULL},S_NULL,0,0},\t// S_MGUN\n    {SPR_CSAW,0,-1,{NULL},S_NULL,0,0},\t// S_CSAW\n    {SPR_LAUN,0,-1,{NULL},S_NULL,0,0},\t// S_LAUN\n    {SPR_PLAS,0,-1,{NULL},S_NULL,0,0},\t// S_PLAS\n    {SPR_SHOT,0,-1,{NULL},S_NULL,0,0},\t// S_SHOT\n    {SPR_SGN2,0,-1,{NULL},S_NULL,0,0},\t// S_SHOT2\n    {SPR_COLU,32768,-1,{NULL},S_NULL,0,0},\t// S_COLU\n    {SPR_SMT2,0,-1,{NULL},S_NULL,0,0},\t// S_STALAG\n    {SPR_GOR1,0,10,{NULL},S_BLOODYTWITCH2,0,0},\t// S_BLOODYTWITCH\n    {SPR_GOR1,1,15,{NULL},S_BLOODYTWITCH3,0,0},\t// S_BLOODYTWITCH2\n    {SPR_GOR1,2,8,{NULL},S_BLOODYTWITCH4,0,0},\t// S_BLOODYTWITCH3\n    {SPR_GOR1,1,6,{NULL},S_BLOODYTWITCH,0,0},\t// S_BLOODYTWITCH4\n    {SPR_PLAY,13,-1,{NULL},S_NULL,0,0},\t// S_DEADTORSO\n    {SPR_PLAY,18,-1,{NULL},S_NULL,0,0},\t// S_DEADBOTTOM\n    {SPR_POL2,0,-1,{NULL},S_NULL,0,0},\t// S_HEADSONSTICK\n    {SPR_POL5,0,-1,{NULL},S_NULL,0,0},\t// S_GIBS\n    {SPR_POL4,0,-1,{NULL},S_NULL,0,0},\t// S_HEADONASTICK\n    {SPR_POL3,32768,6,{NULL},S_HEADCANDLES2,0,0},\t// S_HEADCANDLES\n    {SPR_POL3,32769,6,{NULL},S_HEADCANDLES,0,0},\t// S_HEADCANDLES2\n    {SPR_POL1,0,-1,{NULL},S_NULL,0,0},\t// S_DEADSTICK\n    {SPR_POL6,0,6,{NULL},S_LIVESTICK2,0,0},\t// S_LIVESTICK\n    {SPR_POL6,1,8,{NULL},S_LIVESTICK,0,0},\t// S_LIVESTICK2\n    {SPR_GOR2,0,-1,{NULL},S_NULL,0,0},\t// S_MEAT2\n    {SPR_GOR3,0,-1,{NULL},S_NULL,0,0},\t// S_MEAT3\n    {SPR_GOR4,0,-1,{NULL},S_NULL,0,0},\t// S_MEAT4\n    {SPR_GOR5,0,-1,{NULL},S_NULL,0,0},\t// S_MEAT5\n    {SPR_SMIT,0,-1,{NULL},S_NULL,0,0},\t// S_STALAGTITE\n    {SPR_COL1,0,-1,{NULL},S_NULL,0,0},\t// S_TALLGRNCOL\n    {SPR_COL2,0,-1,{NULL},S_NULL,0,0},\t// S_SHRTGRNCOL\n    {SPR_COL3,0,-1,{NULL},S_NULL,0,0},\t// S_TALLREDCOL\n    {SPR_COL4,0,-1,{NULL},S_NULL,0,0},\t// S_SHRTREDCOL\n    {SPR_CAND,32768,-1,{NULL},S_NULL,0,0},\t// S_CANDLESTIK\n    {SPR_CBRA,32768,-1,{NULL},S_NULL,0,0},\t// S_CANDELABRA\n    {SPR_COL6,0,-1,{NULL},S_NULL,0,0},\t// S_SKULLCOL\n    {SPR_TRE1,0,-1,{NULL},S_NULL,0,0},\t// S_TORCHTREE\n    {SPR_TRE2,0,-1,{NULL},S_NULL,0,0},\t// S_BIGTREE\n    {SPR_ELEC,0,-1,{NULL},S_NULL,0,0},\t// S_TECHPILLAR\n    {SPR_CEYE,32768,6,{NULL},S_EVILEYE2,0,0},\t// S_EVILEYE\n    {SPR_CEYE,32769,6,{NULL},S_EVILEYE3,0,0},\t// S_EVILEYE2\n    {SPR_CEYE,32770,6,{NULL},S_EVILEYE4,0,0},\t// S_EVILEYE3\n    {SPR_CEYE,32769,6,{NULL},S_EVILEYE,0,0},\t// S_EVILEYE4\n    {SPR_FSKU,32768,6,{NULL},S_FLOATSKULL2,0,0},\t// S_FLOATSKULL\n    {SPR_FSKU,32769,6,{NULL},S_FLOATSKULL3,0,0},\t// S_FLOATSKULL2\n    {SPR_FSKU,32770,6,{NULL},S_FLOATSKULL,0,0},\t// S_FLOATSKULL3\n    {SPR_COL5,0,14,{NULL},S_HEARTCOL2,0,0},\t// S_HEARTCOL\n    {SPR_COL5,1,14,{NULL},S_HEARTCOL,0,0},\t// S_HEARTCOL2\n    {SPR_TBLU,32768,4,{NULL},S_BLUETORCH2,0,0},\t// S_BLUETORCH\n    {SPR_TBLU,32769,4,{NULL},S_BLUETORCH3,0,0},\t// S_BLUETORCH2\n    {SPR_TBLU,32770,4,{NULL},S_BLUETORCH4,0,0},\t// S_BLUETORCH3\n    {SPR_TBLU,32771,4,{NULL},S_BLUETORCH,0,0},\t// S_BLUETORCH4\n    {SPR_TGRN,32768,4,{NULL},S_GREENTORCH2,0,0},\t// S_GREENTORCH\n    {SPR_TGRN,32769,4,{NULL},S_GREENTORCH3,0,0},\t// S_GREENTORCH2\n    {SPR_TGRN,32770,4,{NULL},S_GREENTORCH4,0,0},\t// S_GREENTORCH3\n    {SPR_TGRN,32771,4,{NULL},S_GREENTORCH,0,0},\t// S_GREENTORCH4\n    {SPR_TRED,32768,4,{NULL},S_REDTORCH2,0,0},\t// S_REDTORCH\n    {SPR_TRED,32769,4,{NULL},S_REDTORCH3,0,0},\t// S_REDTORCH2\n    {SPR_TRED,32770,4,{NULL},S_REDTORCH4,0,0},\t// S_REDTORCH3\n    {SPR_TRED,32771,4,{NULL},S_REDTORCH,0,0},\t// S_REDTORCH4\n    {SPR_SMBT,32768,4,{NULL},S_BTORCHSHRT2,0,0},\t// S_BTORCHSHRT\n    {SPR_SMBT,32769,4,{NULL},S_BTORCHSHRT3,0,0},\t// S_BTORCHSHRT2\n    {SPR_SMBT,32770,4,{NULL},S_BTORCHSHRT4,0,0},\t// S_BTORCHSHRT3\n    {SPR_SMBT,32771,4,{NULL},S_BTORCHSHRT,0,0},\t// S_BTORCHSHRT4\n    {SPR_SMGT,32768,4,{NULL},S_GTORCHSHRT2,0,0},\t// S_GTORCHSHRT\n    {SPR_SMGT,32769,4,{NULL},S_GTORCHSHRT3,0,0},\t// S_GTORCHSHRT2\n    {SPR_SMGT,32770,4,{NULL},S_GTORCHSHRT4,0,0},\t// S_GTORCHSHRT3\n    {SPR_SMGT,32771,4,{NULL},S_GTORCHSHRT,0,0},\t// S_GTORCHSHRT4\n    {SPR_SMRT,32768,4,{NULL},S_RTORCHSHRT2,0,0},\t// S_RTORCHSHRT\n    {SPR_SMRT,32769,4,{NULL},S_RTORCHSHRT3,0,0},\t// S_RTORCHSHRT2\n    {SPR_SMRT,32770,4,{NULL},S_RTORCHSHRT4,0,0},\t// S_RTORCHSHRT3\n    {SPR_SMRT,32771,4,{NULL},S_RTORCHSHRT,0,0},\t// S_RTORCHSHRT4\n    {SPR_HDB1,0,-1,{NULL},S_NULL,0,0},\t// S_HANGNOGUTS\n    {SPR_HDB2,0,-1,{NULL},S_NULL,0,0},\t// S_HANGBNOBRAIN\n    {SPR_HDB3,0,-1,{NULL},S_NULL,0,0},\t// S_HANGTLOOKDN\n    {SPR_HDB4,0,-1,{NULL},S_NULL,0,0},\t// S_HANGTSKULL\n    {SPR_HDB5,0,-1,{NULL},S_NULL,0,0},\t// S_HANGTLOOKUP\n    {SPR_HDB6,0,-1,{NULL},S_NULL,0,0},\t// S_HANGTNOBRAIN\n    {SPR_POB1,0,-1,{NULL},S_NULL,0,0},\t// S_COLONGIBS\n    {SPR_POB2,0,-1,{NULL},S_NULL,0,0},\t// S_SMALLPOOL\n    {SPR_BRS1,0,-1,{NULL},S_NULL,0,0},\t\t// S_BRAINSTEM\n    {SPR_TLMP,32768,4,{NULL},S_TECHLAMP2,0,0},\t// S_TECHLAMP\n    {SPR_TLMP,32769,4,{NULL},S_TECHLAMP3,0,0},\t// S_TECHLAMP2\n    {SPR_TLMP,32770,4,{NULL},S_TECHLAMP4,0,0},\t// S_TECHLAMP3\n    {SPR_TLMP,32771,4,{NULL},S_TECHLAMP,0,0},\t// S_TECHLAMP4\n    {SPR_TLP2,32768,4,{NULL},S_TECH2LAMP2,0,0},\t// S_TECH2LAMP\n    {SPR_TLP2,32769,4,{NULL},S_TECH2LAMP3,0,0},\t// S_TECH2LAMP2\n    {SPR_TLP2,32770,4,{NULL},S_TECH2LAMP4,0,0},\t// S_TECH2LAMP3\n    {SPR_TLP2,32771,4,{NULL},S_TECH2LAMP,0,0}\t// S_TECH2LAMP4\n};\n\n\nmobjinfo_t mobjinfo[NUMMOBJTYPES] = {\n\n    {\t\t// MT_PLAYER\n\t-1,\t\t// doomednum\n\tS_PLAY,\t\t// spawnstate\n\t100,\t\t// spawnhealth\n\tS_PLAY_RUN1,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t0,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_PLAY_PAIN,\t\t// painstate\n\t255,\t\t// painchance\n\tsfx_plpain,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_PLAY_ATK1,\t\t// missilestate\n\tS_PLAY_DIE1,\t\t// deathstate\n\tS_PLAY_XDIE1,\t\t// xdeathstate\n\tsfx_pldeth,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t56*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_POSSESSED\n\t3004,\t\t// doomednum\n\tS_POSS_STND,\t\t// spawnstate\n\t20,\t\t// spawnhealth\n\tS_POSS_RUN1,\t\t// seestate\n\tsfx_posit1,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_pistol,\t\t// attacksound\n\tS_POSS_PAIN,\t\t// painstate\n\t200,\t\t// painchance\n\tsfx_popain,\t\t// painsound\n\t0,\t\t// meleestate\n\tS_POSS_ATK1,\t\t// missilestate\n\tS_POSS_DIE1,\t\t// deathstate\n\tS_POSS_XDIE1,\t\t// xdeathstate\n\tsfx_podth1,\t\t// deathsound\n\t8,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t56*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_posact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_POSS_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_SHOTGUY\n\t9,\t\t// doomednum\n\tS_SPOS_STND,\t\t// spawnstate\n\t30,\t\t// spawnhealth\n\tS_SPOS_RUN1,\t\t// seestate\n\tsfx_posit2,\t\t// seesound\n\t8,\t\t// reactiontime\n\t0,\t\t// attacksound\n\tS_SPOS_PAIN,\t\t// painstate\n\t170,\t\t// painchance\n\tsfx_popain,\t\t// painsound\n\t0,\t\t// meleestate\n\tS_SPOS_ATK1,\t\t// missilestate\n\tS_SPOS_DIE1,\t\t// deathstate\n\tS_SPOS_XDIE1,\t\t// xdeathstate\n\tsfx_podth2,\t\t// deathsound\n\t8,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t56*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_posact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_SPOS_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_VILE\n\t64,\t\t// doomednum\n\tS_VILE_STND,\t\t// spawnstate\n\t700,\t\t// spawnhealth\n\tS_VILE_RUN1,\t\t// seestate\n\tsfx_vilsit,\t\t// seesound\n\t8,\t\t// reactiontime\n\t0,\t\t// attacksound\n\tS_VILE_PAIN,\t\t// painstate\n\t10,\t\t// painchance\n\tsfx_vipain,\t\t// painsound\n\t0,\t\t// meleestate\n\tS_VILE_ATK1,\t\t// missilestate\n\tS_VILE_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_vildth,\t\t// deathsound\n\t15,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t56*FRACUNIT,\t\t// height\n\t500,\t\t// mass\n\t0,\t\t// damage\n\tsfx_vilact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_FIRE\n\t-1,\t\t// doomednum\n\tS_FIRE1,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_UNDEAD\n\t66,\t\t// doomednum\n\tS_SKEL_STND,\t\t// spawnstate\n\t300,\t\t// spawnhealth\n\tS_SKEL_RUN1,\t\t// seestate\n\tsfx_skesit,\t\t// seesound\n\t8,\t\t// reactiontime\n\t0,\t\t// attacksound\n\tS_SKEL_PAIN,\t\t// painstate\n\t100,\t\t// painchance\n\tsfx_popain,\t\t// painsound\n\tS_SKEL_FIST1,\t\t// meleestate\n\tS_SKEL_MISS1,\t\t// missilestate\n\tS_SKEL_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_skedth,\t\t// deathsound\n\t10,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t56*FRACUNIT,\t\t// height\n\t500,\t\t// mass\n\t0,\t\t// damage\n\tsfx_skeact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_SKEL_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_TRACER\n\t-1,\t\t// doomednum\n\tS_TRACER,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_skeatk,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_TRACEEXP1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_barexp,\t\t// deathsound\n\t10*FRACUNIT,\t\t// speed\n\t11*FRACUNIT,\t\t// radius\n\t8*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t10,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_SMOKE\n\t-1,\t\t// doomednum\n\tS_SMOKE1,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_FATSO\n\t67,\t\t// doomednum\n\tS_FATT_STND,\t\t// spawnstate\n\t600,\t\t// spawnhealth\n\tS_FATT_RUN1,\t\t// seestate\n\tsfx_mansit,\t\t// seesound\n\t8,\t\t// reactiontime\n\t0,\t\t// attacksound\n\tS_FATT_PAIN,\t\t// painstate\n\t80,\t\t// painchance\n\tsfx_mnpain,\t\t// painsound\n\t0,\t\t// meleestate\n\tS_FATT_ATK1,\t\t// missilestate\n\tS_FATT_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_mandth,\t\t// deathsound\n\t8,\t\t// speed\n\t48*FRACUNIT,\t\t// radius\n\t64*FRACUNIT,\t\t// height\n\t1000,\t\t// mass\n\t0,\t\t// damage\n\tsfx_posact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_FATT_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_FATSHOT\n\t-1,\t\t// doomednum\n\tS_FATSHOT1,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_firsht,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_FATSHOTX1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_firxpl,\t\t// deathsound\n\t20*FRACUNIT,\t\t// speed\n\t6*FRACUNIT,\t\t// radius\n\t8*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t8,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_CHAINGUY\n\t65,\t\t// doomednum\n\tS_CPOS_STND,\t\t// spawnstate\n\t70,\t\t// spawnhealth\n\tS_CPOS_RUN1,\t\t// seestate\n\tsfx_posit2,\t\t// seesound\n\t8,\t\t// reactiontime\n\t0,\t\t// attacksound\n\tS_CPOS_PAIN,\t\t// painstate\n\t170,\t\t// painchance\n\tsfx_popain,\t\t// painsound\n\t0,\t\t// meleestate\n\tS_CPOS_ATK1,\t\t// missilestate\n\tS_CPOS_DIE1,\t\t// deathstate\n\tS_CPOS_XDIE1,\t\t// xdeathstate\n\tsfx_podth2,\t\t// deathsound\n\t8,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t56*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_posact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_CPOS_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_TROOP\n\t3001,\t\t// doomednum\n\tS_TROO_STND,\t\t// spawnstate\n\t60,\t\t// spawnhealth\n\tS_TROO_RUN1,\t\t// seestate\n\tsfx_bgsit1,\t\t// seesound\n\t8,\t\t// reactiontime\n\t0,\t\t// attacksound\n\tS_TROO_PAIN,\t\t// painstate\n\t200,\t\t// painchance\n\tsfx_popain,\t\t// painsound\n\tS_TROO_ATK1,\t\t// meleestate\n\tS_TROO_ATK1,\t\t// missilestate\n\tS_TROO_DIE1,\t\t// deathstate\n\tS_TROO_XDIE1,\t\t// xdeathstate\n\tsfx_bgdth1,\t\t// deathsound\n\t8,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t56*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_bgact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_TROO_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_SERGEANT\n\t3002,\t\t// doomednum\n\tS_SARG_STND,\t\t// spawnstate\n\t150,\t\t// spawnhealth\n\tS_SARG_RUN1,\t\t// seestate\n\tsfx_sgtsit,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_sgtatk,\t\t// attacksound\n\tS_SARG_PAIN,\t\t// painstate\n\t180,\t\t// painchance\n\tsfx_dmpain,\t\t// painsound\n\tS_SARG_ATK1,\t\t// meleestate\n\t0,\t\t// missilestate\n\tS_SARG_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_sgtdth,\t\t// deathsound\n\t10,\t\t// speed\n\t30*FRACUNIT,\t\t// radius\n\t56*FRACUNIT,\t\t// height\n\t400,\t\t// mass\n\t0,\t\t// damage\n\tsfx_dmact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_SARG_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_SHADOWS\n\t58,\t\t// doomednum\n\tS_SARG_STND,\t\t// spawnstate\n\t150,\t\t// spawnhealth\n\tS_SARG_RUN1,\t\t// seestate\n\tsfx_sgtsit,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_sgtatk,\t\t// attacksound\n\tS_SARG_PAIN,\t\t// painstate\n\t180,\t\t// painchance\n\tsfx_dmpain,\t\t// painsound\n\tS_SARG_ATK1,\t\t// meleestate\n\t0,\t\t// missilestate\n\tS_SARG_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_sgtdth,\t\t// deathsound\n\t10,\t\t// speed\n\t30*FRACUNIT,\t\t// radius\n\t56*FRACUNIT,\t\t// height\n\t400,\t\t// mass\n\t0,\t\t// damage\n\tsfx_dmact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_SHADOW|MF_COUNTKILL,\t\t// flags\n\tS_SARG_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_HEAD\n\t3005,\t\t// doomednum\n\tS_HEAD_STND,\t\t// spawnstate\n\t400,\t\t// spawnhealth\n\tS_HEAD_RUN1,\t\t// seestate\n\tsfx_cacsit,\t\t// seesound\n\t8,\t\t// reactiontime\n\t0,\t\t// attacksound\n\tS_HEAD_PAIN,\t\t// painstate\n\t128,\t\t// painchance\n\tsfx_dmpain,\t\t// painsound\n\t0,\t\t// meleestate\n\tS_HEAD_ATK1,\t\t// missilestate\n\tS_HEAD_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_cacdth,\t\t// deathsound\n\t8,\t\t// speed\n\t31*FRACUNIT,\t\t// radius\n\t56*FRACUNIT,\t\t// height\n\t400,\t\t// mass\n\t0,\t\t// damage\n\tsfx_dmact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY|MF_COUNTKILL,\t\t// flags\n\tS_HEAD_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_BRUISER\n\t3003,\t\t// doomednum\n\tS_BOSS_STND,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_BOSS_RUN1,\t\t// seestate\n\tsfx_brssit,\t\t// seesound\n\t8,\t\t// reactiontime\n\t0,\t\t// attacksound\n\tS_BOSS_PAIN,\t\t// painstate\n\t50,\t\t// painchance\n\tsfx_dmpain,\t\t// painsound\n\tS_BOSS_ATK1,\t\t// meleestate\n\tS_BOSS_ATK1,\t\t// missilestate\n\tS_BOSS_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_brsdth,\t\t// deathsound\n\t8,\t\t// speed\n\t24*FRACUNIT,\t\t// radius\n\t64*FRACUNIT,\t\t// height\n\t1000,\t\t// mass\n\t0,\t\t// damage\n\tsfx_dmact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_BOSS_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_BRUISERSHOT\n\t-1,\t\t// doomednum\n\tS_BRBALL1,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_firsht,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_BRBALLX1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_firxpl,\t\t// deathsound\n\t15*FRACUNIT,\t\t// speed\n\t6*FRACUNIT,\t\t// radius\n\t8*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t8,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_KNIGHT\n\t69,\t\t// doomednum\n\tS_BOS2_STND,\t\t// spawnstate\n\t500,\t\t// spawnhealth\n\tS_BOS2_RUN1,\t\t// seestate\n\tsfx_kntsit,\t\t// seesound\n\t8,\t\t// reactiontime\n\t0,\t\t// attacksound\n\tS_BOS2_PAIN,\t\t// painstate\n\t50,\t\t// painchance\n\tsfx_dmpain,\t\t// painsound\n\tS_BOS2_ATK1,\t\t// meleestate\n\tS_BOS2_ATK1,\t\t// missilestate\n\tS_BOS2_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_kntdth,\t\t// deathsound\n\t8,\t\t// speed\n\t24*FRACUNIT,\t\t// radius\n\t64*FRACUNIT,\t\t// height\n\t1000,\t\t// mass\n\t0,\t\t// damage\n\tsfx_dmact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_BOS2_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_SKULL\n\t3006,\t\t// doomednum\n\tS_SKULL_STND,\t\t// spawnstate\n\t100,\t\t// spawnhealth\n\tS_SKULL_RUN1,\t\t// seestate\n\t0,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_sklatk,\t\t// attacksound\n\tS_SKULL_PAIN,\t\t// painstate\n\t256,\t\t// painchance\n\tsfx_dmpain,\t\t// painsound\n\t0,\t\t// meleestate\n\tS_SKULL_ATK1,\t\t// missilestate\n\tS_SKULL_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_firxpl,\t\t// deathsound\n\t8,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t56*FRACUNIT,\t\t// height\n\t50,\t\t// mass\n\t3,\t\t// damage\n\tsfx_dmact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_SPIDER\n\t7,\t\t// doomednum\n\tS_SPID_STND,\t\t// spawnstate\n\t3000,\t\t// spawnhealth\n\tS_SPID_RUN1,\t\t// seestate\n\tsfx_spisit,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_shotgn,\t\t// attacksound\n\tS_SPID_PAIN,\t\t// painstate\n\t40,\t\t// painchance\n\tsfx_dmpain,\t\t// painsound\n\t0,\t\t// meleestate\n\tS_SPID_ATK1,\t\t// missilestate\n\tS_SPID_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_spidth,\t\t// deathsound\n\t12,\t\t// speed\n\t128*FRACUNIT,\t\t// radius\n\t100*FRACUNIT,\t\t// height\n\t1000,\t\t// mass\n\t0,\t\t// damage\n\tsfx_dmact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_BABY\n\t68,\t\t// doomednum\n\tS_BSPI_STND,\t\t// spawnstate\n\t500,\t\t// spawnhealth\n\tS_BSPI_SIGHT,\t\t// seestate\n\tsfx_bspsit,\t\t// seesound\n\t8,\t\t// reactiontime\n\t0,\t\t// attacksound\n\tS_BSPI_PAIN,\t\t// painstate\n\t128,\t\t// painchance\n\tsfx_dmpain,\t\t// painsound\n\t0,\t\t// meleestate\n\tS_BSPI_ATK1,\t\t// missilestate\n\tS_BSPI_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_bspdth,\t\t// deathsound\n\t12,\t\t// speed\n\t64*FRACUNIT,\t\t// radius\n\t64*FRACUNIT,\t\t// height\n\t600,\t\t// mass\n\t0,\t\t// damage\n\tsfx_bspact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_BSPI_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_CYBORG\n\t16,\t\t// doomednum\n\tS_CYBER_STND,\t\t// spawnstate\n\t4000,\t\t// spawnhealth\n\tS_CYBER_RUN1,\t\t// seestate\n\tsfx_cybsit,\t\t// seesound\n\t8,\t\t// reactiontime\n\t0,\t\t// attacksound\n\tS_CYBER_PAIN,\t\t// painstate\n\t20,\t\t// painchance\n\tsfx_dmpain,\t\t// painsound\n\t0,\t\t// meleestate\n\tS_CYBER_ATK1,\t\t// missilestate\n\tS_CYBER_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_cybdth,\t\t// deathsound\n\t16,\t\t// speed\n\t40*FRACUNIT,\t\t// radius\n\t110*FRACUNIT,\t\t// height\n\t1000,\t\t// mass\n\t0,\t\t// damage\n\tsfx_dmact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_PAIN\n\t71,\t\t// doomednum\n\tS_PAIN_STND,\t\t// spawnstate\n\t400,\t\t// spawnhealth\n\tS_PAIN_RUN1,\t\t// seestate\n\tsfx_pesit,\t\t// seesound\n\t8,\t\t// reactiontime\n\t0,\t\t// attacksound\n\tS_PAIN_PAIN,\t\t// painstate\n\t128,\t\t// painchance\n\tsfx_pepain,\t\t// painsound\n\t0,\t\t// meleestate\n\tS_PAIN_ATK1,\t\t// missilestate\n\tS_PAIN_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_pedth,\t\t// deathsound\n\t8,\t\t// speed\n\t31*FRACUNIT,\t\t// radius\n\t56*FRACUNIT,\t\t// height\n\t400,\t\t// mass\n\t0,\t\t// damage\n\tsfx_dmact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY|MF_COUNTKILL,\t\t// flags\n\tS_PAIN_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_WOLFSS\n\t84,\t\t// doomednum\n\tS_SSWV_STND,\t\t// spawnstate\n\t50,\t\t// spawnhealth\n\tS_SSWV_RUN1,\t\t// seestate\n\tsfx_sssit,\t\t// seesound\n\t8,\t\t// reactiontime\n\t0,\t\t// attacksound\n\tS_SSWV_PAIN,\t\t// painstate\n\t170,\t\t// painchance\n\tsfx_popain,\t\t// painsound\n\t0,\t\t// meleestate\n\tS_SSWV_ATK1,\t\t// missilestate\n\tS_SSWV_DIE1,\t\t// deathstate\n\tS_SSWV_XDIE1,\t\t// xdeathstate\n\tsfx_ssdth,\t\t// deathsound\n\t8,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t56*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_posact,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_SSWV_RAISE1\t\t// raisestate\n    },\n\n    {\t\t// MT_KEEN\n\t72,\t\t// doomednum\n\tS_KEENSTND,\t\t// spawnstate\n\t100,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_KEENPAIN,\t\t// painstate\n\t256,\t\t// painchance\n\tsfx_keenpn,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_COMMKEEN,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_keendt,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t72*FRACUNIT,\t\t// height\n\t10000000,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY|MF_SHOOTABLE|MF_COUNTKILL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_BOSSBRAIN\n\t88,\t\t// doomednum\n\tS_BRAIN,\t\t// spawnstate\n\t250,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_BRAIN_PAIN,\t\t// painstate\n\t255,\t\t// painchance\n\tsfx_bospn,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_BRAIN_DIE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_bosdth,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t10000000,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_BOSSSPIT\n\t89,\t\t// doomednum\n\tS_BRAINEYE,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_BRAINEYESEE,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t32*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_NOSECTOR,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_BOSSTARGET\n\t87,\t\t// doomednum\n\tS_NULL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t32*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_NOSECTOR,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_SPAWNSHOT\n\t-1,\t\t// doomednum\n\tS_SPAWN1,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_bospit,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_firxpl,\t\t// deathsound\n\t10*FRACUNIT,\t\t// speed\n\t6*FRACUNIT,\t\t// radius\n\t32*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t3,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_NOCLIP,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_SPAWNFIRE\n\t-1,\t\t// doomednum\n\tS_SPAWNFIRE1,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_BARREL\n\t2035,\t\t// doomednum\n\tS_BAR1,\t\t// spawnstate\n\t20,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_BEXP,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_barexp,\t\t// deathsound\n\t0,\t\t// speed\n\t10*FRACUNIT,\t\t// radius\n\t42*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SHOOTABLE|MF_NOBLOOD,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_TROOPSHOT\n\t-1,\t\t// doomednum\n\tS_TBALL1,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_firsht,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_TBALLX1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_firxpl,\t\t// deathsound\n\t10*FRACUNIT,\t\t// speed\n\t6*FRACUNIT,\t\t// radius\n\t8*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t3,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_HEADSHOT\n\t-1,\t\t// doomednum\n\tS_RBALL1,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_firsht,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_RBALLX1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_firxpl,\t\t// deathsound\n\t10*FRACUNIT,\t\t// speed\n\t6*FRACUNIT,\t\t// radius\n\t8*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t5,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_ROCKET\n\t-1,\t\t// doomednum\n\tS_ROCKET,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_rlaunc,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_EXPLODE1,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_barexp,\t\t// deathsound\n\t20*FRACUNIT,\t\t// speed\n\t11*FRACUNIT,\t\t// radius\n\t8*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t20,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_PLASMA\n\t-1,\t\t// doomednum\n\tS_PLASBALL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_plasma,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_PLASEXP,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_firxpl,\t\t// deathsound\n\t25*FRACUNIT,\t\t// speed\n\t13*FRACUNIT,\t\t// radius\n\t8*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t5,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_BFG\n\t-1,\t\t// doomednum\n\tS_BFGSHOT,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\t0,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_BFGLAND,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_rxplod,\t\t// deathsound\n\t25*FRACUNIT,\t\t// speed\n\t13*FRACUNIT,\t\t// radius\n\t8*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t100,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_ARACHPLAZ\n\t-1,\t\t// doomednum\n\tS_ARACH_PLAZ,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_plasma,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_ARACH_PLEX,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_firxpl,\t\t// deathsound\n\t25*FRACUNIT,\t\t// speed\n\t13*FRACUNIT,\t\t// radius\n\t8*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t5,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_PUFF\n\t-1,\t\t// doomednum\n\tS_PUFF1,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_BLOOD\n\t-1,\t\t// doomednum\n\tS_BLOOD1,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_TFOG\n\t-1,\t\t// doomednum\n\tS_TFOG,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_IFOG\n\t-1,\t\t// doomednum\n\tS_IFOG,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_TELEPORTMAN\n\t14,\t\t// doomednum\n\tS_NULL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_NOSECTOR,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_EXTRABFG\n\t-1,\t\t// doomednum\n\tS_BFGEXP,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC0\n\t2018,\t\t// doomednum\n\tS_ARM1,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC1\n\t2019,\t\t// doomednum\n\tS_ARM2,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC2\n\t2014,\t\t// doomednum\n\tS_BON1,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_COUNTITEM,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC3\n\t2015,\t\t// doomednum\n\tS_BON2,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_COUNTITEM,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC4\n\t5,\t\t// doomednum\n\tS_BKEY,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_NOTDMATCH,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC5\n\t13,\t\t// doomednum\n\tS_RKEY,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_NOTDMATCH,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC6\n\t6,\t\t// doomednum\n\tS_YKEY,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_NOTDMATCH,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC7\n\t39,\t\t// doomednum\n\tS_YSKULL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_NOTDMATCH,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC8\n\t38,\t\t// doomednum\n\tS_RSKULL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_NOTDMATCH,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC9\n\t40,\t\t// doomednum\n\tS_BSKULL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_NOTDMATCH,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC10\n\t2011,\t\t// doomednum\n\tS_STIM,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC11\n\t2012,\t\t// doomednum\n\tS_MEDI,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC12\n\t2013,\t\t// doomednum\n\tS_SOUL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_COUNTITEM,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_INV\n\t2022,\t\t// doomednum\n\tS_PINV,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_COUNTITEM,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC13\n\t2023,\t\t// doomednum\n\tS_PSTR,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_COUNTITEM,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_INS\n\t2024,\t\t// doomednum\n\tS_PINS,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_COUNTITEM,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC14\n\t2025,\t\t// doomednum\n\tS_SUIT,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC15\n\t2026,\t\t// doomednum\n\tS_PMAP,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_COUNTITEM,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC16\n\t2045,\t\t// doomednum\n\tS_PVIS,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_COUNTITEM,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MEGA\n\t83,\t\t// doomednum\n\tS_MEGA,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL|MF_COUNTITEM,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_CLIP\n\t2007,\t\t// doomednum\n\tS_CLIP,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC17\n\t2048,\t\t// doomednum\n\tS_AMMO,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC18\n\t2010,\t\t// doomednum\n\tS_ROCK,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC19\n\t2046,\t\t// doomednum\n\tS_BROK,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC20\n\t2047,\t\t// doomednum\n\tS_CELL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC21\n\t17,\t\t// doomednum\n\tS_CELP,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC22\n\t2008,\t\t// doomednum\n\tS_SHEL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC23\n\t2049,\t\t// doomednum\n\tS_SBOX,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC24\n\t8,\t\t// doomednum\n\tS_BPAK,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC25\n\t2006,\t\t// doomednum\n\tS_BFUG,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_CHAINGUN\n\t2002,\t\t// doomednum\n\tS_MGUN,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC26\n\t2005,\t\t// doomednum\n\tS_CSAW,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC27\n\t2003,\t\t// doomednum\n\tS_LAUN,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC28\n\t2004,\t\t// doomednum\n\tS_PLAS,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_SHOTGUN\n\t2001,\t\t// doomednum\n\tS_SHOT,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_SUPERSHOTGUN\n\t82,\t\t// doomednum\n\tS_SHOT2,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPECIAL,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC29\n\t85,\t\t// doomednum\n\tS_TECHLAMP,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC30\n\t86,\t\t// doomednum\n\tS_TECH2LAMP,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC31\n\t2028,\t\t// doomednum\n\tS_COLU,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC32\n\t30,\t\t// doomednum\n\tS_TALLGRNCOL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC33\n\t31,\t\t// doomednum\n\tS_SHRTGRNCOL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC34\n\t32,\t\t// doomednum\n\tS_TALLREDCOL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC35\n\t33,\t\t// doomednum\n\tS_SHRTREDCOL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC36\n\t37,\t\t// doomednum\n\tS_SKULLCOL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC37\n\t36,\t\t// doomednum\n\tS_HEARTCOL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC38\n\t41,\t\t// doomednum\n\tS_EVILEYE,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC39\n\t42,\t\t// doomednum\n\tS_FLOATSKULL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC40\n\t43,\t\t// doomednum\n\tS_TORCHTREE,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC41\n\t44,\t\t// doomednum\n\tS_BLUETORCH,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC42\n\t45,\t\t// doomednum\n\tS_GREENTORCH,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC43\n\t46,\t\t// doomednum\n\tS_REDTORCH,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC44\n\t55,\t\t// doomednum\n\tS_BTORCHSHRT,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC45\n\t56,\t\t// doomednum\n\tS_GTORCHSHRT,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC46\n\t57,\t\t// doomednum\n\tS_RTORCHSHRT,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC47\n\t47,\t\t// doomednum\n\tS_STALAGTITE,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC48\n\t48,\t\t// doomednum\n\tS_TECHPILLAR,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC49\n\t34,\t\t// doomednum\n\tS_CANDLESTIK,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\t0,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC50\n\t35,\t\t// doomednum\n\tS_CANDELABRA,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC51\n\t49,\t\t// doomednum\n\tS_BLOODYTWITCH,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t68*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC52\n\t50,\t\t// doomednum\n\tS_MEAT2,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t84*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC53\n\t51,\t\t// doomednum\n\tS_MEAT3,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t84*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC54\n\t52,\t\t// doomednum\n\tS_MEAT4,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t68*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC55\n\t53,\t\t// doomednum\n\tS_MEAT5,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t52*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC56\n\t59,\t\t// doomednum\n\tS_MEAT2,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t84*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC57\n\t60,\t\t// doomednum\n\tS_MEAT4,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t68*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC58\n\t61,\t\t// doomednum\n\tS_MEAT3,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t52*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC59\n\t62,\t\t// doomednum\n\tS_MEAT5,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t52*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC60\n\t63,\t\t// doomednum\n\tS_BLOODYTWITCH,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t68*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC61\n\t22,\t\t// doomednum\n\tS_HEAD_DIE6,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\t0,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC62\n\t15,\t\t// doomednum\n\tS_PLAY_DIE7,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\t0,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC63\n\t18,\t\t// doomednum\n\tS_POSS_DIE5,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\t0,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC64\n\t21,\t\t// doomednum\n\tS_SARG_DIE6,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\t0,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC65\n\t23,\t\t// doomednum\n\tS_SKULL_DIE6,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\t0,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC66\n\t20,\t\t// doomednum\n\tS_TROO_DIE5,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\t0,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC67\n\t19,\t\t// doomednum\n\tS_SPOS_DIE5,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\t0,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC68\n\t10,\t\t// doomednum\n\tS_PLAY_XDIE9,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\t0,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC69\n\t12,\t\t// doomednum\n\tS_PLAY_XDIE9,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\t0,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC70\n\t28,\t\t// doomednum\n\tS_HEADSONSTICK,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC71\n\t24,\t\t// doomednum\n\tS_GIBS,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\t0,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC72\n\t27,\t\t// doomednum\n\tS_HEADONASTICK,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC73\n\t29,\t\t// doomednum\n\tS_HEADCANDLES,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC74\n\t25,\t\t// doomednum\n\tS_DEADSTICK,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC75\n\t26,\t\t// doomednum\n\tS_LIVESTICK,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC76\n\t54,\t\t// doomednum\n\tS_BIGTREE,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t32*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC77\n\t70,\t\t// doomednum\n\tS_BBAR1,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC78\n\t73,\t\t// doomednum\n\tS_HANGNOGUTS,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t88*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC79\n\t74,\t\t// doomednum\n\tS_HANGBNOBRAIN,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t88*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC80\n\t75,\t\t// doomednum\n\tS_HANGTLOOKDN,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t64*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC81\n\t76,\t\t// doomednum\n\tS_HANGTSKULL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t64*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC82\n\t77,\t\t// doomednum\n\tS_HANGTLOOKUP,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t64*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC83\n\t78,\t\t// doomednum\n\tS_HANGTNOBRAIN,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t16*FRACUNIT,\t\t// radius\n\t64*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC84\n\t79,\t\t// doomednum\n\tS_COLONGIBS,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC85\n\t80,\t\t// doomednum\n\tS_SMALLPOOL,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP,\t\t// flags\n\tS_NULL\t\t// raisestate\n    },\n\n    {\t\t// MT_MISC86\n\t81,\t\t// doomednum\n\tS_BRAINSTEM,\t\t// spawnstate\n\t1000,\t\t// spawnhealth\n\tS_NULL,\t\t// seestate\n\tsfx_None,\t\t// seesound\n\t8,\t\t// reactiontime\n\tsfx_None,\t\t// attacksound\n\tS_NULL,\t\t// painstate\n\t0,\t\t// painchance\n\tsfx_None,\t\t// painsound\n\tS_NULL,\t\t// meleestate\n\tS_NULL,\t\t// missilestate\n\tS_NULL,\t\t// deathstate\n\tS_NULL,\t\t// xdeathstate\n\tsfx_None,\t\t// deathsound\n\t0,\t\t// speed\n\t20*FRACUNIT,\t\t// radius\n\t16*FRACUNIT,\t\t// height\n\t100,\t\t// mass\n\t0,\t\t// damage\n\tsfx_None,\t\t// activesound\n\tMF_NOBLOCKMAP,\t\t// flags\n\tS_NULL\t\t// raisestate\n    }\n};\n\n"
  },
  {
    "path": "fbdoom/info.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tThing frame/state LUT,\n//\tgenerated by multigen utilitiy.\n//\tThis one is the original DOOM version, preserved.\n//\n\n#ifndef __INFO__\n#define __INFO__\n\n// Needed for action function pointer handling.\n#include \"d_think.h\"\n\ntypedef enum\n{\n    SPR_TROO,\n    SPR_SHTG,\n    SPR_PUNG,\n    SPR_PISG,\n    SPR_PISF,\n    SPR_SHTF,\n    SPR_SHT2,\n    SPR_CHGG,\n    SPR_CHGF,\n    SPR_MISG,\n    SPR_MISF,\n    SPR_SAWG,\n    SPR_PLSG,\n    SPR_PLSF,\n    SPR_BFGG,\n    SPR_BFGF,\n    SPR_BLUD,\n    SPR_PUFF,\n    SPR_BAL1,\n    SPR_BAL2,\n    SPR_PLSS,\n    SPR_PLSE,\n    SPR_MISL,\n    SPR_BFS1,\n    SPR_BFE1,\n    SPR_BFE2,\n    SPR_TFOG,\n    SPR_IFOG,\n    SPR_PLAY,\n    SPR_POSS,\n    SPR_SPOS,\n    SPR_VILE,\n    SPR_FIRE,\n    SPR_FATB,\n    SPR_FBXP,\n    SPR_SKEL,\n    SPR_MANF,\n    SPR_FATT,\n    SPR_CPOS,\n    SPR_SARG,\n    SPR_HEAD,\n    SPR_BAL7,\n    SPR_BOSS,\n    SPR_BOS2,\n    SPR_SKUL,\n    SPR_SPID,\n    SPR_BSPI,\n    SPR_APLS,\n    SPR_APBX,\n    SPR_CYBR,\n    SPR_PAIN,\n    SPR_SSWV,\n    SPR_KEEN,\n    SPR_BBRN,\n    SPR_BOSF,\n    SPR_ARM1,\n    SPR_ARM2,\n    SPR_BAR1,\n    SPR_BEXP,\n    SPR_FCAN,\n    SPR_BON1,\n    SPR_BON2,\n    SPR_BKEY,\n    SPR_RKEY,\n    SPR_YKEY,\n    SPR_BSKU,\n    SPR_RSKU,\n    SPR_YSKU,\n    SPR_STIM,\n    SPR_MEDI,\n    SPR_SOUL,\n    SPR_PINV,\n    SPR_PSTR,\n    SPR_PINS,\n    SPR_MEGA,\n    SPR_SUIT,\n    SPR_PMAP,\n    SPR_PVIS,\n    SPR_CLIP,\n    SPR_AMMO,\n    SPR_ROCK,\n    SPR_BROK,\n    SPR_CELL,\n    SPR_CELP,\n    SPR_SHEL,\n    SPR_SBOX,\n    SPR_BPAK,\n    SPR_BFUG,\n    SPR_MGUN,\n    SPR_CSAW,\n    SPR_LAUN,\n    SPR_PLAS,\n    SPR_SHOT,\n    SPR_SGN2,\n    SPR_COLU,\n    SPR_SMT2,\n    SPR_GOR1,\n    SPR_POL2,\n    SPR_POL5,\n    SPR_POL4,\n    SPR_POL3,\n    SPR_POL1,\n    SPR_POL6,\n    SPR_GOR2,\n    SPR_GOR3,\n    SPR_GOR4,\n    SPR_GOR5,\n    SPR_SMIT,\n    SPR_COL1,\n    SPR_COL2,\n    SPR_COL3,\n    SPR_COL4,\n    SPR_CAND,\n    SPR_CBRA,\n    SPR_COL6,\n    SPR_TRE1,\n    SPR_TRE2,\n    SPR_ELEC,\n    SPR_CEYE,\n    SPR_FSKU,\n    SPR_COL5,\n    SPR_TBLU,\n    SPR_TGRN,\n    SPR_TRED,\n    SPR_SMBT,\n    SPR_SMGT,\n    SPR_SMRT,\n    SPR_HDB1,\n    SPR_HDB2,\n    SPR_HDB3,\n    SPR_HDB4,\n    SPR_HDB5,\n    SPR_HDB6,\n    SPR_POB1,\n    SPR_POB2,\n    SPR_BRS1,\n    SPR_TLMP,\n    SPR_TLP2,\n    NUMSPRITES\n\n} spritenum_t;\n\ntypedef enum\n{\n    S_NULL,\n    S_LIGHTDONE,\n    S_PUNCH,\n    S_PUNCHDOWN,\n    S_PUNCHUP,\n    S_PUNCH1,\n    S_PUNCH2,\n    S_PUNCH3,\n    S_PUNCH4,\n    S_PUNCH5,\n    S_PISTOL,\n    S_PISTOLDOWN,\n    S_PISTOLUP,\n    S_PISTOL1,\n    S_PISTOL2,\n    S_PISTOL3,\n    S_PISTOL4,\n    S_PISTOLFLASH,\n    S_SGUN,\n    S_SGUNDOWN,\n    S_SGUNUP,\n    S_SGUN1,\n    S_SGUN2,\n    S_SGUN3,\n    S_SGUN4,\n    S_SGUN5,\n    S_SGUN6,\n    S_SGUN7,\n    S_SGUN8,\n    S_SGUN9,\n    S_SGUNFLASH1,\n    S_SGUNFLASH2,\n    S_DSGUN,\n    S_DSGUNDOWN,\n    S_DSGUNUP,\n    S_DSGUN1,\n    S_DSGUN2,\n    S_DSGUN3,\n    S_DSGUN4,\n    S_DSGUN5,\n    S_DSGUN6,\n    S_DSGUN7,\n    S_DSGUN8,\n    S_DSGUN9,\n    S_DSGUN10,\n    S_DSNR1,\n    S_DSNR2,\n    S_DSGUNFLASH1,\n    S_DSGUNFLASH2,\n    S_CHAIN,\n    S_CHAINDOWN,\n    S_CHAINUP,\n    S_CHAIN1,\n    S_CHAIN2,\n    S_CHAIN3,\n    S_CHAINFLASH1,\n    S_CHAINFLASH2,\n    S_MISSILE,\n    S_MISSILEDOWN,\n    S_MISSILEUP,\n    S_MISSILE1,\n    S_MISSILE2,\n    S_MISSILE3,\n    S_MISSILEFLASH1,\n    S_MISSILEFLASH2,\n    S_MISSILEFLASH3,\n    S_MISSILEFLASH4,\n    S_SAW,\n    S_SAWB,\n    S_SAWDOWN,\n    S_SAWUP,\n    S_SAW1,\n    S_SAW2,\n    S_SAW3,\n    S_PLASMA,\n    S_PLASMADOWN,\n    S_PLASMAUP,\n    S_PLASMA1,\n    S_PLASMA2,\n    S_PLASMAFLASH1,\n    S_PLASMAFLASH2,\n    S_BFG,\n    S_BFGDOWN,\n    S_BFGUP,\n    S_BFG1,\n    S_BFG2,\n    S_BFG3,\n    S_BFG4,\n    S_BFGFLASH1,\n    S_BFGFLASH2,\n    S_BLOOD1,\n    S_BLOOD2,\n    S_BLOOD3,\n    S_PUFF1,\n    S_PUFF2,\n    S_PUFF3,\n    S_PUFF4,\n    S_TBALL1,\n    S_TBALL2,\n    S_TBALLX1,\n    S_TBALLX2,\n    S_TBALLX3,\n    S_RBALL1,\n    S_RBALL2,\n    S_RBALLX1,\n    S_RBALLX2,\n    S_RBALLX3,\n    S_PLASBALL,\n    S_PLASBALL2,\n    S_PLASEXP,\n    S_PLASEXP2,\n    S_PLASEXP3,\n    S_PLASEXP4,\n    S_PLASEXP5,\n    S_ROCKET,\n    S_BFGSHOT,\n    S_BFGSHOT2,\n    S_BFGLAND,\n    S_BFGLAND2,\n    S_BFGLAND3,\n    S_BFGLAND4,\n    S_BFGLAND5,\n    S_BFGLAND6,\n    S_BFGEXP,\n    S_BFGEXP2,\n    S_BFGEXP3,\n    S_BFGEXP4,\n    S_EXPLODE1,\n    S_EXPLODE2,\n    S_EXPLODE3,\n    S_TFOG,\n    S_TFOG01,\n    S_TFOG02,\n    S_TFOG2,\n    S_TFOG3,\n    S_TFOG4,\n    S_TFOG5,\n    S_TFOG6,\n    S_TFOG7,\n    S_TFOG8,\n    S_TFOG9,\n    S_TFOG10,\n    S_IFOG,\n    S_IFOG01,\n    S_IFOG02,\n    S_IFOG2,\n    S_IFOG3,\n    S_IFOG4,\n    S_IFOG5,\n    S_PLAY,\n    S_PLAY_RUN1,\n    S_PLAY_RUN2,\n    S_PLAY_RUN3,\n    S_PLAY_RUN4,\n    S_PLAY_ATK1,\n    S_PLAY_ATK2,\n    S_PLAY_PAIN,\n    S_PLAY_PAIN2,\n    S_PLAY_DIE1,\n    S_PLAY_DIE2,\n    S_PLAY_DIE3,\n    S_PLAY_DIE4,\n    S_PLAY_DIE5,\n    S_PLAY_DIE6,\n    S_PLAY_DIE7,\n    S_PLAY_XDIE1,\n    S_PLAY_XDIE2,\n    S_PLAY_XDIE3,\n    S_PLAY_XDIE4,\n    S_PLAY_XDIE5,\n    S_PLAY_XDIE6,\n    S_PLAY_XDIE7,\n    S_PLAY_XDIE8,\n    S_PLAY_XDIE9,\n    S_POSS_STND,\n    S_POSS_STND2,\n    S_POSS_RUN1,\n    S_POSS_RUN2,\n    S_POSS_RUN3,\n    S_POSS_RUN4,\n    S_POSS_RUN5,\n    S_POSS_RUN6,\n    S_POSS_RUN7,\n    S_POSS_RUN8,\n    S_POSS_ATK1,\n    S_POSS_ATK2,\n    S_POSS_ATK3,\n    S_POSS_PAIN,\n    S_POSS_PAIN2,\n    S_POSS_DIE1,\n    S_POSS_DIE2,\n    S_POSS_DIE3,\n    S_POSS_DIE4,\n    S_POSS_DIE5,\n    S_POSS_XDIE1,\n    S_POSS_XDIE2,\n    S_POSS_XDIE3,\n    S_POSS_XDIE4,\n    S_POSS_XDIE5,\n    S_POSS_XDIE6,\n    S_POSS_XDIE7,\n    S_POSS_XDIE8,\n    S_POSS_XDIE9,\n    S_POSS_RAISE1,\n    S_POSS_RAISE2,\n    S_POSS_RAISE3,\n    S_POSS_RAISE4,\n    S_SPOS_STND,\n    S_SPOS_STND2,\n    S_SPOS_RUN1,\n    S_SPOS_RUN2,\n    S_SPOS_RUN3,\n    S_SPOS_RUN4,\n    S_SPOS_RUN5,\n    S_SPOS_RUN6,\n    S_SPOS_RUN7,\n    S_SPOS_RUN8,\n    S_SPOS_ATK1,\n    S_SPOS_ATK2,\n    S_SPOS_ATK3,\n    S_SPOS_PAIN,\n    S_SPOS_PAIN2,\n    S_SPOS_DIE1,\n    S_SPOS_DIE2,\n    S_SPOS_DIE3,\n    S_SPOS_DIE4,\n    S_SPOS_DIE5,\n    S_SPOS_XDIE1,\n    S_SPOS_XDIE2,\n    S_SPOS_XDIE3,\n    S_SPOS_XDIE4,\n    S_SPOS_XDIE5,\n    S_SPOS_XDIE6,\n    S_SPOS_XDIE7,\n    S_SPOS_XDIE8,\n    S_SPOS_XDIE9,\n    S_SPOS_RAISE1,\n    S_SPOS_RAISE2,\n    S_SPOS_RAISE3,\n    S_SPOS_RAISE4,\n    S_SPOS_RAISE5,\n    S_VILE_STND,\n    S_VILE_STND2,\n    S_VILE_RUN1,\n    S_VILE_RUN2,\n    S_VILE_RUN3,\n    S_VILE_RUN4,\n    S_VILE_RUN5,\n    S_VILE_RUN6,\n    S_VILE_RUN7,\n    S_VILE_RUN8,\n    S_VILE_RUN9,\n    S_VILE_RUN10,\n    S_VILE_RUN11,\n    S_VILE_RUN12,\n    S_VILE_ATK1,\n    S_VILE_ATK2,\n    S_VILE_ATK3,\n    S_VILE_ATK4,\n    S_VILE_ATK5,\n    S_VILE_ATK6,\n    S_VILE_ATK7,\n    S_VILE_ATK8,\n    S_VILE_ATK9,\n    S_VILE_ATK10,\n    S_VILE_ATK11,\n    S_VILE_HEAL1,\n    S_VILE_HEAL2,\n    S_VILE_HEAL3,\n    S_VILE_PAIN,\n    S_VILE_PAIN2,\n    S_VILE_DIE1,\n    S_VILE_DIE2,\n    S_VILE_DIE3,\n    S_VILE_DIE4,\n    S_VILE_DIE5,\n    S_VILE_DIE6,\n    S_VILE_DIE7,\n    S_VILE_DIE8,\n    S_VILE_DIE9,\n    S_VILE_DIE10,\n    S_FIRE1,\n    S_FIRE2,\n    S_FIRE3,\n    S_FIRE4,\n    S_FIRE5,\n    S_FIRE6,\n    S_FIRE7,\n    S_FIRE8,\n    S_FIRE9,\n    S_FIRE10,\n    S_FIRE11,\n    S_FIRE12,\n    S_FIRE13,\n    S_FIRE14,\n    S_FIRE15,\n    S_FIRE16,\n    S_FIRE17,\n    S_FIRE18,\n    S_FIRE19,\n    S_FIRE20,\n    S_FIRE21,\n    S_FIRE22,\n    S_FIRE23,\n    S_FIRE24,\n    S_FIRE25,\n    S_FIRE26,\n    S_FIRE27,\n    S_FIRE28,\n    S_FIRE29,\n    S_FIRE30,\n    S_SMOKE1,\n    S_SMOKE2,\n    S_SMOKE3,\n    S_SMOKE4,\n    S_SMOKE5,\n    S_TRACER,\n    S_TRACER2,\n    S_TRACEEXP1,\n    S_TRACEEXP2,\n    S_TRACEEXP3,\n    S_SKEL_STND,\n    S_SKEL_STND2,\n    S_SKEL_RUN1,\n    S_SKEL_RUN2,\n    S_SKEL_RUN3,\n    S_SKEL_RUN4,\n    S_SKEL_RUN5,\n    S_SKEL_RUN6,\n    S_SKEL_RUN7,\n    S_SKEL_RUN8,\n    S_SKEL_RUN9,\n    S_SKEL_RUN10,\n    S_SKEL_RUN11,\n    S_SKEL_RUN12,\n    S_SKEL_FIST1,\n    S_SKEL_FIST2,\n    S_SKEL_FIST3,\n    S_SKEL_FIST4,\n    S_SKEL_MISS1,\n    S_SKEL_MISS2,\n    S_SKEL_MISS3,\n    S_SKEL_MISS4,\n    S_SKEL_PAIN,\n    S_SKEL_PAIN2,\n    S_SKEL_DIE1,\n    S_SKEL_DIE2,\n    S_SKEL_DIE3,\n    S_SKEL_DIE4,\n    S_SKEL_DIE5,\n    S_SKEL_DIE6,\n    S_SKEL_RAISE1,\n    S_SKEL_RAISE2,\n    S_SKEL_RAISE3,\n    S_SKEL_RAISE4,\n    S_SKEL_RAISE5,\n    S_SKEL_RAISE6,\n    S_FATSHOT1,\n    S_FATSHOT2,\n    S_FATSHOTX1,\n    S_FATSHOTX2,\n    S_FATSHOTX3,\n    S_FATT_STND,\n    S_FATT_STND2,\n    S_FATT_RUN1,\n    S_FATT_RUN2,\n    S_FATT_RUN3,\n    S_FATT_RUN4,\n    S_FATT_RUN5,\n    S_FATT_RUN6,\n    S_FATT_RUN7,\n    S_FATT_RUN8,\n    S_FATT_RUN9,\n    S_FATT_RUN10,\n    S_FATT_RUN11,\n    S_FATT_RUN12,\n    S_FATT_ATK1,\n    S_FATT_ATK2,\n    S_FATT_ATK3,\n    S_FATT_ATK4,\n    S_FATT_ATK5,\n    S_FATT_ATK6,\n    S_FATT_ATK7,\n    S_FATT_ATK8,\n    S_FATT_ATK9,\n    S_FATT_ATK10,\n    S_FATT_PAIN,\n    S_FATT_PAIN2,\n    S_FATT_DIE1,\n    S_FATT_DIE2,\n    S_FATT_DIE3,\n    S_FATT_DIE4,\n    S_FATT_DIE5,\n    S_FATT_DIE6,\n    S_FATT_DIE7,\n    S_FATT_DIE8,\n    S_FATT_DIE9,\n    S_FATT_DIE10,\n    S_FATT_RAISE1,\n    S_FATT_RAISE2,\n    S_FATT_RAISE3,\n    S_FATT_RAISE4,\n    S_FATT_RAISE5,\n    S_FATT_RAISE6,\n    S_FATT_RAISE7,\n    S_FATT_RAISE8,\n    S_CPOS_STND,\n    S_CPOS_STND2,\n    S_CPOS_RUN1,\n    S_CPOS_RUN2,\n    S_CPOS_RUN3,\n    S_CPOS_RUN4,\n    S_CPOS_RUN5,\n    S_CPOS_RUN6,\n    S_CPOS_RUN7,\n    S_CPOS_RUN8,\n    S_CPOS_ATK1,\n    S_CPOS_ATK2,\n    S_CPOS_ATK3,\n    S_CPOS_ATK4,\n    S_CPOS_PAIN,\n    S_CPOS_PAIN2,\n    S_CPOS_DIE1,\n    S_CPOS_DIE2,\n    S_CPOS_DIE3,\n    S_CPOS_DIE4,\n    S_CPOS_DIE5,\n    S_CPOS_DIE6,\n    S_CPOS_DIE7,\n    S_CPOS_XDIE1,\n    S_CPOS_XDIE2,\n    S_CPOS_XDIE3,\n    S_CPOS_XDIE4,\n    S_CPOS_XDIE5,\n    S_CPOS_XDIE6,\n    S_CPOS_RAISE1,\n    S_CPOS_RAISE2,\n    S_CPOS_RAISE3,\n    S_CPOS_RAISE4,\n    S_CPOS_RAISE5,\n    S_CPOS_RAISE6,\n    S_CPOS_RAISE7,\n    S_TROO_STND,\n    S_TROO_STND2,\n    S_TROO_RUN1,\n    S_TROO_RUN2,\n    S_TROO_RUN3,\n    S_TROO_RUN4,\n    S_TROO_RUN5,\n    S_TROO_RUN6,\n    S_TROO_RUN7,\n    S_TROO_RUN8,\n    S_TROO_ATK1,\n    S_TROO_ATK2,\n    S_TROO_ATK3,\n    S_TROO_PAIN,\n    S_TROO_PAIN2,\n    S_TROO_DIE1,\n    S_TROO_DIE2,\n    S_TROO_DIE3,\n    S_TROO_DIE4,\n    S_TROO_DIE5,\n    S_TROO_XDIE1,\n    S_TROO_XDIE2,\n    S_TROO_XDIE3,\n    S_TROO_XDIE4,\n    S_TROO_XDIE5,\n    S_TROO_XDIE6,\n    S_TROO_XDIE7,\n    S_TROO_XDIE8,\n    S_TROO_RAISE1,\n    S_TROO_RAISE2,\n    S_TROO_RAISE3,\n    S_TROO_RAISE4,\n    S_TROO_RAISE5,\n    S_SARG_STND,\n    S_SARG_STND2,\n    S_SARG_RUN1,\n    S_SARG_RUN2,\n    S_SARG_RUN3,\n    S_SARG_RUN4,\n    S_SARG_RUN5,\n    S_SARG_RUN6,\n    S_SARG_RUN7,\n    S_SARG_RUN8,\n    S_SARG_ATK1,\n    S_SARG_ATK2,\n    S_SARG_ATK3,\n    S_SARG_PAIN,\n    S_SARG_PAIN2,\n    S_SARG_DIE1,\n    S_SARG_DIE2,\n    S_SARG_DIE3,\n    S_SARG_DIE4,\n    S_SARG_DIE5,\n    S_SARG_DIE6,\n    S_SARG_RAISE1,\n    S_SARG_RAISE2,\n    S_SARG_RAISE3,\n    S_SARG_RAISE4,\n    S_SARG_RAISE5,\n    S_SARG_RAISE6,\n    S_HEAD_STND,\n    S_HEAD_RUN1,\n    S_HEAD_ATK1,\n    S_HEAD_ATK2,\n    S_HEAD_ATK3,\n    S_HEAD_PAIN,\n    S_HEAD_PAIN2,\n    S_HEAD_PAIN3,\n    S_HEAD_DIE1,\n    S_HEAD_DIE2,\n    S_HEAD_DIE3,\n    S_HEAD_DIE4,\n    S_HEAD_DIE5,\n    S_HEAD_DIE6,\n    S_HEAD_RAISE1,\n    S_HEAD_RAISE2,\n    S_HEAD_RAISE3,\n    S_HEAD_RAISE4,\n    S_HEAD_RAISE5,\n    S_HEAD_RAISE6,\n    S_BRBALL1,\n    S_BRBALL2,\n    S_BRBALLX1,\n    S_BRBALLX2,\n    S_BRBALLX3,\n    S_BOSS_STND,\n    S_BOSS_STND2,\n    S_BOSS_RUN1,\n    S_BOSS_RUN2,\n    S_BOSS_RUN3,\n    S_BOSS_RUN4,\n    S_BOSS_RUN5,\n    S_BOSS_RUN6,\n    S_BOSS_RUN7,\n    S_BOSS_RUN8,\n    S_BOSS_ATK1,\n    S_BOSS_ATK2,\n    S_BOSS_ATK3,\n    S_BOSS_PAIN,\n    S_BOSS_PAIN2,\n    S_BOSS_DIE1,\n    S_BOSS_DIE2,\n    S_BOSS_DIE3,\n    S_BOSS_DIE4,\n    S_BOSS_DIE5,\n    S_BOSS_DIE6,\n    S_BOSS_DIE7,\n    S_BOSS_RAISE1,\n    S_BOSS_RAISE2,\n    S_BOSS_RAISE3,\n    S_BOSS_RAISE4,\n    S_BOSS_RAISE5,\n    S_BOSS_RAISE6,\n    S_BOSS_RAISE7,\n    S_BOS2_STND,\n    S_BOS2_STND2,\n    S_BOS2_RUN1,\n    S_BOS2_RUN2,\n    S_BOS2_RUN3,\n    S_BOS2_RUN4,\n    S_BOS2_RUN5,\n    S_BOS2_RUN6,\n    S_BOS2_RUN7,\n    S_BOS2_RUN8,\n    S_BOS2_ATK1,\n    S_BOS2_ATK2,\n    S_BOS2_ATK3,\n    S_BOS2_PAIN,\n    S_BOS2_PAIN2,\n    S_BOS2_DIE1,\n    S_BOS2_DIE2,\n    S_BOS2_DIE3,\n    S_BOS2_DIE4,\n    S_BOS2_DIE5,\n    S_BOS2_DIE6,\n    S_BOS2_DIE7,\n    S_BOS2_RAISE1,\n    S_BOS2_RAISE2,\n    S_BOS2_RAISE3,\n    S_BOS2_RAISE4,\n    S_BOS2_RAISE5,\n    S_BOS2_RAISE6,\n    S_BOS2_RAISE7,\n    S_SKULL_STND,\n    S_SKULL_STND2,\n    S_SKULL_RUN1,\n    S_SKULL_RUN2,\n    S_SKULL_ATK1,\n    S_SKULL_ATK2,\n    S_SKULL_ATK3,\n    S_SKULL_ATK4,\n    S_SKULL_PAIN,\n    S_SKULL_PAIN2,\n    S_SKULL_DIE1,\n    S_SKULL_DIE2,\n    S_SKULL_DIE3,\n    S_SKULL_DIE4,\n    S_SKULL_DIE5,\n    S_SKULL_DIE6,\n    S_SPID_STND,\n    S_SPID_STND2,\n    S_SPID_RUN1,\n    S_SPID_RUN2,\n    S_SPID_RUN3,\n    S_SPID_RUN4,\n    S_SPID_RUN5,\n    S_SPID_RUN6,\n    S_SPID_RUN7,\n    S_SPID_RUN8,\n    S_SPID_RUN9,\n    S_SPID_RUN10,\n    S_SPID_RUN11,\n    S_SPID_RUN12,\n    S_SPID_ATK1,\n    S_SPID_ATK2,\n    S_SPID_ATK3,\n    S_SPID_ATK4,\n    S_SPID_PAIN,\n    S_SPID_PAIN2,\n    S_SPID_DIE1,\n    S_SPID_DIE2,\n    S_SPID_DIE3,\n    S_SPID_DIE4,\n    S_SPID_DIE5,\n    S_SPID_DIE6,\n    S_SPID_DIE7,\n    S_SPID_DIE8,\n    S_SPID_DIE9,\n    S_SPID_DIE10,\n    S_SPID_DIE11,\n    S_BSPI_STND,\n    S_BSPI_STND2,\n    S_BSPI_SIGHT,\n    S_BSPI_RUN1,\n    S_BSPI_RUN2,\n    S_BSPI_RUN3,\n    S_BSPI_RUN4,\n    S_BSPI_RUN5,\n    S_BSPI_RUN6,\n    S_BSPI_RUN7,\n    S_BSPI_RUN8,\n    S_BSPI_RUN9,\n    S_BSPI_RUN10,\n    S_BSPI_RUN11,\n    S_BSPI_RUN12,\n    S_BSPI_ATK1,\n    S_BSPI_ATK2,\n    S_BSPI_ATK3,\n    S_BSPI_ATK4,\n    S_BSPI_PAIN,\n    S_BSPI_PAIN2,\n    S_BSPI_DIE1,\n    S_BSPI_DIE2,\n    S_BSPI_DIE3,\n    S_BSPI_DIE4,\n    S_BSPI_DIE5,\n    S_BSPI_DIE6,\n    S_BSPI_DIE7,\n    S_BSPI_RAISE1,\n    S_BSPI_RAISE2,\n    S_BSPI_RAISE3,\n    S_BSPI_RAISE4,\n    S_BSPI_RAISE5,\n    S_BSPI_RAISE6,\n    S_BSPI_RAISE7,\n    S_ARACH_PLAZ,\n    S_ARACH_PLAZ2,\n    S_ARACH_PLEX,\n    S_ARACH_PLEX2,\n    S_ARACH_PLEX3,\n    S_ARACH_PLEX4,\n    S_ARACH_PLEX5,\n    S_CYBER_STND,\n    S_CYBER_STND2,\n    S_CYBER_RUN1,\n    S_CYBER_RUN2,\n    S_CYBER_RUN3,\n    S_CYBER_RUN4,\n    S_CYBER_RUN5,\n    S_CYBER_RUN6,\n    S_CYBER_RUN7,\n    S_CYBER_RUN8,\n    S_CYBER_ATK1,\n    S_CYBER_ATK2,\n    S_CYBER_ATK3,\n    S_CYBER_ATK4,\n    S_CYBER_ATK5,\n    S_CYBER_ATK6,\n    S_CYBER_PAIN,\n    S_CYBER_DIE1,\n    S_CYBER_DIE2,\n    S_CYBER_DIE3,\n    S_CYBER_DIE4,\n    S_CYBER_DIE5,\n    S_CYBER_DIE6,\n    S_CYBER_DIE7,\n    S_CYBER_DIE8,\n    S_CYBER_DIE9,\n    S_CYBER_DIE10,\n    S_PAIN_STND,\n    S_PAIN_RUN1,\n    S_PAIN_RUN2,\n    S_PAIN_RUN3,\n    S_PAIN_RUN4,\n    S_PAIN_RUN5,\n    S_PAIN_RUN6,\n    S_PAIN_ATK1,\n    S_PAIN_ATK2,\n    S_PAIN_ATK3,\n    S_PAIN_ATK4,\n    S_PAIN_PAIN,\n    S_PAIN_PAIN2,\n    S_PAIN_DIE1,\n    S_PAIN_DIE2,\n    S_PAIN_DIE3,\n    S_PAIN_DIE4,\n    S_PAIN_DIE5,\n    S_PAIN_DIE6,\n    S_PAIN_RAISE1,\n    S_PAIN_RAISE2,\n    S_PAIN_RAISE3,\n    S_PAIN_RAISE4,\n    S_PAIN_RAISE5,\n    S_PAIN_RAISE6,\n    S_SSWV_STND,\n    S_SSWV_STND2,\n    S_SSWV_RUN1,\n    S_SSWV_RUN2,\n    S_SSWV_RUN3,\n    S_SSWV_RUN4,\n    S_SSWV_RUN5,\n    S_SSWV_RUN6,\n    S_SSWV_RUN7,\n    S_SSWV_RUN8,\n    S_SSWV_ATK1,\n    S_SSWV_ATK2,\n    S_SSWV_ATK3,\n    S_SSWV_ATK4,\n    S_SSWV_ATK5,\n    S_SSWV_ATK6,\n    S_SSWV_PAIN,\n    S_SSWV_PAIN2,\n    S_SSWV_DIE1,\n    S_SSWV_DIE2,\n    S_SSWV_DIE3,\n    S_SSWV_DIE4,\n    S_SSWV_DIE5,\n    S_SSWV_XDIE1,\n    S_SSWV_XDIE2,\n    S_SSWV_XDIE3,\n    S_SSWV_XDIE4,\n    S_SSWV_XDIE5,\n    S_SSWV_XDIE6,\n    S_SSWV_XDIE7,\n    S_SSWV_XDIE8,\n    S_SSWV_XDIE9,\n    S_SSWV_RAISE1,\n    S_SSWV_RAISE2,\n    S_SSWV_RAISE3,\n    S_SSWV_RAISE4,\n    S_SSWV_RAISE5,\n    S_KEENSTND,\n    S_COMMKEEN,\n    S_COMMKEEN2,\n    S_COMMKEEN3,\n    S_COMMKEEN4,\n    S_COMMKEEN5,\n    S_COMMKEEN6,\n    S_COMMKEEN7,\n    S_COMMKEEN8,\n    S_COMMKEEN9,\n    S_COMMKEEN10,\n    S_COMMKEEN11,\n    S_COMMKEEN12,\n    S_KEENPAIN,\n    S_KEENPAIN2,\n    S_BRAIN,\n    S_BRAIN_PAIN,\n    S_BRAIN_DIE1,\n    S_BRAIN_DIE2,\n    S_BRAIN_DIE3,\n    S_BRAIN_DIE4,\n    S_BRAINEYE,\n    S_BRAINEYESEE,\n    S_BRAINEYE1,\n    S_SPAWN1,\n    S_SPAWN2,\n    S_SPAWN3,\n    S_SPAWN4,\n    S_SPAWNFIRE1,\n    S_SPAWNFIRE2,\n    S_SPAWNFIRE3,\n    S_SPAWNFIRE4,\n    S_SPAWNFIRE5,\n    S_SPAWNFIRE6,\n    S_SPAWNFIRE7,\n    S_SPAWNFIRE8,\n    S_BRAINEXPLODE1,\n    S_BRAINEXPLODE2,\n    S_BRAINEXPLODE3,\n    S_ARM1,\n    S_ARM1A,\n    S_ARM2,\n    S_ARM2A,\n    S_BAR1,\n    S_BAR2,\n    S_BEXP,\n    S_BEXP2,\n    S_BEXP3,\n    S_BEXP4,\n    S_BEXP5,\n    S_BBAR1,\n    S_BBAR2,\n    S_BBAR3,\n    S_BON1,\n    S_BON1A,\n    S_BON1B,\n    S_BON1C,\n    S_BON1D,\n    S_BON1E,\n    S_BON2,\n    S_BON2A,\n    S_BON2B,\n    S_BON2C,\n    S_BON2D,\n    S_BON2E,\n    S_BKEY,\n    S_BKEY2,\n    S_RKEY,\n    S_RKEY2,\n    S_YKEY,\n    S_YKEY2,\n    S_BSKULL,\n    S_BSKULL2,\n    S_RSKULL,\n    S_RSKULL2,\n    S_YSKULL,\n    S_YSKULL2,\n    S_STIM,\n    S_MEDI,\n    S_SOUL,\n    S_SOUL2,\n    S_SOUL3,\n    S_SOUL4,\n    S_SOUL5,\n    S_SOUL6,\n    S_PINV,\n    S_PINV2,\n    S_PINV3,\n    S_PINV4,\n    S_PSTR,\n    S_PINS,\n    S_PINS2,\n    S_PINS3,\n    S_PINS4,\n    S_MEGA,\n    S_MEGA2,\n    S_MEGA3,\n    S_MEGA4,\n    S_SUIT,\n    S_PMAP,\n    S_PMAP2,\n    S_PMAP3,\n    S_PMAP4,\n    S_PMAP5,\n    S_PMAP6,\n    S_PVIS,\n    S_PVIS2,\n    S_CLIP,\n    S_AMMO,\n    S_ROCK,\n    S_BROK,\n    S_CELL,\n    S_CELP,\n    S_SHEL,\n    S_SBOX,\n    S_BPAK,\n    S_BFUG,\n    S_MGUN,\n    S_CSAW,\n    S_LAUN,\n    S_PLAS,\n    S_SHOT,\n    S_SHOT2,\n    S_COLU,\n    S_STALAG,\n    S_BLOODYTWITCH,\n    S_BLOODYTWITCH2,\n    S_BLOODYTWITCH3,\n    S_BLOODYTWITCH4,\n    S_DEADTORSO,\n    S_DEADBOTTOM,\n    S_HEADSONSTICK,\n    S_GIBS,\n    S_HEADONASTICK,\n    S_HEADCANDLES,\n    S_HEADCANDLES2,\n    S_DEADSTICK,\n    S_LIVESTICK,\n    S_LIVESTICK2,\n    S_MEAT2,\n    S_MEAT3,\n    S_MEAT4,\n    S_MEAT5,\n    S_STALAGTITE,\n    S_TALLGRNCOL,\n    S_SHRTGRNCOL,\n    S_TALLREDCOL,\n    S_SHRTREDCOL,\n    S_CANDLESTIK,\n    S_CANDELABRA,\n    S_SKULLCOL,\n    S_TORCHTREE,\n    S_BIGTREE,\n    S_TECHPILLAR,\n    S_EVILEYE,\n    S_EVILEYE2,\n    S_EVILEYE3,\n    S_EVILEYE4,\n    S_FLOATSKULL,\n    S_FLOATSKULL2,\n    S_FLOATSKULL3,\n    S_HEARTCOL,\n    S_HEARTCOL2,\n    S_BLUETORCH,\n    S_BLUETORCH2,\n    S_BLUETORCH3,\n    S_BLUETORCH4,\n    S_GREENTORCH,\n    S_GREENTORCH2,\n    S_GREENTORCH3,\n    S_GREENTORCH4,\n    S_REDTORCH,\n    S_REDTORCH2,\n    S_REDTORCH3,\n    S_REDTORCH4,\n    S_BTORCHSHRT,\n    S_BTORCHSHRT2,\n    S_BTORCHSHRT3,\n    S_BTORCHSHRT4,\n    S_GTORCHSHRT,\n    S_GTORCHSHRT2,\n    S_GTORCHSHRT3,\n    S_GTORCHSHRT4,\n    S_RTORCHSHRT,\n    S_RTORCHSHRT2,\n    S_RTORCHSHRT3,\n    S_RTORCHSHRT4,\n    S_HANGNOGUTS,\n    S_HANGBNOBRAIN,\n    S_HANGTLOOKDN,\n    S_HANGTSKULL,\n    S_HANGTLOOKUP,\n    S_HANGTNOBRAIN,\n    S_COLONGIBS,\n    S_SMALLPOOL,\n    S_BRAINSTEM,\n    S_TECHLAMP,\n    S_TECHLAMP2,\n    S_TECHLAMP3,\n    S_TECHLAMP4,\n    S_TECH2LAMP,\n    S_TECH2LAMP2,\n    S_TECH2LAMP3,\n    S_TECH2LAMP4,\n    NUMSTATES\n} statenum_t;\n\n\ntypedef struct\n{\n    spritenum_t sprite;\n    int frame;\n    int tics;\n    // void (*action) ();\n    actionf_t action;\n    statenum_t nextstate;\n    int misc1;\n    int misc2;\n} state_t;\n\nextern state_t\tstates[NUMSTATES];\nextern char *sprnames[];\n\ntypedef enum {\n    MT_PLAYER,\n    MT_POSSESSED,\n    MT_SHOTGUY,\n    MT_VILE,\n    MT_FIRE,\n    MT_UNDEAD,\n    MT_TRACER,\n    MT_SMOKE,\n    MT_FATSO,\n    MT_FATSHOT,\n    MT_CHAINGUY,\n    MT_TROOP,\n    MT_SERGEANT,\n    MT_SHADOWS,\n    MT_HEAD,\n    MT_BRUISER,\n    MT_BRUISERSHOT,\n    MT_KNIGHT,\n    MT_SKULL,\n    MT_SPIDER,\n    MT_BABY,\n    MT_CYBORG,\n    MT_PAIN,\n    MT_WOLFSS,\n    MT_KEEN,\n    MT_BOSSBRAIN,\n    MT_BOSSSPIT,\n    MT_BOSSTARGET,\n    MT_SPAWNSHOT,\n    MT_SPAWNFIRE,\n    MT_BARREL,\n    MT_TROOPSHOT,\n    MT_HEADSHOT,\n    MT_ROCKET,\n    MT_PLASMA,\n    MT_BFG,\n    MT_ARACHPLAZ,\n    MT_PUFF,\n    MT_BLOOD,\n    MT_TFOG,\n    MT_IFOG,\n    MT_TELEPORTMAN,\n    MT_EXTRABFG,\n    MT_MISC0,\n    MT_MISC1,\n    MT_MISC2,\n    MT_MISC3,\n    MT_MISC4,\n    MT_MISC5,\n    MT_MISC6,\n    MT_MISC7,\n    MT_MISC8,\n    MT_MISC9,\n    MT_MISC10,\n    MT_MISC11,\n    MT_MISC12,\n    MT_INV,\n    MT_MISC13,\n    MT_INS,\n    MT_MISC14,\n    MT_MISC15,\n    MT_MISC16,\n    MT_MEGA,\n    MT_CLIP,\n    MT_MISC17,\n    MT_MISC18,\n    MT_MISC19,\n    MT_MISC20,\n    MT_MISC21,\n    MT_MISC22,\n    MT_MISC23,\n    MT_MISC24,\n    MT_MISC25,\n    MT_CHAINGUN,\n    MT_MISC26,\n    MT_MISC27,\n    MT_MISC28,\n    MT_SHOTGUN,\n    MT_SUPERSHOTGUN,\n    MT_MISC29,\n    MT_MISC30,\n    MT_MISC31,\n    MT_MISC32,\n    MT_MISC33,\n    MT_MISC34,\n    MT_MISC35,\n    MT_MISC36,\n    MT_MISC37,\n    MT_MISC38,\n    MT_MISC39,\n    MT_MISC40,\n    MT_MISC41,\n    MT_MISC42,\n    MT_MISC43,\n    MT_MISC44,\n    MT_MISC45,\n    MT_MISC46,\n    MT_MISC47,\n    MT_MISC48,\n    MT_MISC49,\n    MT_MISC50,\n    MT_MISC51,\n    MT_MISC52,\n    MT_MISC53,\n    MT_MISC54,\n    MT_MISC55,\n    MT_MISC56,\n    MT_MISC57,\n    MT_MISC58,\n    MT_MISC59,\n    MT_MISC60,\n    MT_MISC61,\n    MT_MISC62,\n    MT_MISC63,\n    MT_MISC64,\n    MT_MISC65,\n    MT_MISC66,\n    MT_MISC67,\n    MT_MISC68,\n    MT_MISC69,\n    MT_MISC70,\n    MT_MISC71,\n    MT_MISC72,\n    MT_MISC73,\n    MT_MISC74,\n    MT_MISC75,\n    MT_MISC76,\n    MT_MISC77,\n    MT_MISC78,\n    MT_MISC79,\n    MT_MISC80,\n    MT_MISC81,\n    MT_MISC82,\n    MT_MISC83,\n    MT_MISC84,\n    MT_MISC85,\n    MT_MISC86,\n    NUMMOBJTYPES\n\n} mobjtype_t;\n\ntypedef struct\n{\n    int\tdoomednum;\n    int\tspawnstate;\n    int\tspawnhealth;\n    int\tseestate;\n    int\tseesound;\n    int\treactiontime;\n    int\tattacksound;\n    int\tpainstate;\n    int\tpainchance;\n    int\tpainsound;\n    int\tmeleestate;\n    int\tmissilestate;\n    int\tdeathstate;\n    int\txdeathstate;\n    int\tdeathsound;\n    int\tspeed;\n    int\tradius;\n    int\theight;\n    int\tmass;\n    int\tdamage;\n    int\tactivesound;\n    int\tflags;\n    int\traisestate;\n\n} mobjinfo_t;\n\nextern mobjinfo_t mobjinfo[NUMMOBJTYPES];\n\n#endif\n"
  },
  {
    "path": "fbdoom/m_argv.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\n\n\n#include <ctype.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"doomtype.h\"\n#include \"i_system.h\"\n#include \"m_misc.h\"\n#include \"m_argv.h\"  // haleyjd 20110212: warning fix\n\nint\t\tmyargc;\nchar**\t\tmyargv;\n\n\n\n\n//\n// M_CheckParm\n// Checks for the given parameter\n// in the program's command line arguments.\n// Returns the argument number (1 to argc-1)\n// or 0 if not present\n//\n\nint M_CheckParmWithArgs(char *check, int num_args)\n{\n    int i;\n\n    for (i = 1; i < myargc - num_args; i++)\n    {\n\tif (!strcasecmp(check, myargv[i]))\n\t    return i;\n    }\n\n    return 0;\n}\n\n//\n// M_ParmExists\n//\n// Returns true if the given parameter exists in the program's command\n// line arguments, false if not.\n//\n\nboolean M_ParmExists(char *check)\n{\n    return M_CheckParm(check) != 0;\n}\n\nint M_CheckParm(char *check)\n{\n    return M_CheckParmWithArgs(check, 0);\n}\n\n#define MAXARGVS        100\n\nstatic void LoadResponseFile(int argv_index)\n{\n#if ORIGCODE\n    FILE *handle;\n    int size;\n    char *infile;\n    char *file;\n    char *response_filename;\n    char **newargv;\n    int newargc;\n    int i, k;\n\n    response_filename = myargv[argv_index] + 1;\n\n    // Read the response file into memory\n    handle = fopen(response_filename, \"rb\");\n\n    if (handle == NULL)\n    {\n        printf (\"\\nNo such response file!\");\n#if ORIGCODE\n        exit(1);\n#endif\n    }\n\n    printf(\"Found response file %s!\\n\", response_filename);\n\n    size = M_FileLength(handle);\n\n    // Read in the entire file\n    // Allocate one byte extra - this is in case there is an argument\n    // at the end of the response file, in which case a '\\0' will be\n    // needed.\n\n    file = malloc(size + 1);\n\n    i = 0;\n\n    while (i < size)\n    {\n        k = fread(file + i, 1, size - i, handle);\n\n        if (k < 0)\n        {\n            I_Error(\"Failed to read full contents of '%s'\", response_filename);\n        }\n\n        i += k;\n    }\n\n    fclose(handle);\n\n    // Create new arguments list array\n\n    newargv = malloc(sizeof(char *) * MAXARGVS);\n    newargc = 0;\n    memset(newargv, 0, sizeof(char *) * MAXARGVS);\n\n    // Copy all the arguments in the list up to the response file\n\n    for (i=0; i<argv_index; ++i)\n    {\n        newargv[i] = myargv[i];\n        ++newargc;\n    }\n\n    infile = file;\n    k = 0;\n\n    while(k < size)\n    {\n        // Skip past space characters to the next argument\n\n        while(k < size && isspace((int)infile[k]))\n        {\n            ++k;\n        }\n\n        if (k >= size)\n        {\n            break;\n        }\n\n        // If the next argument is enclosed in quote marks, treat\n        // the contents as a single argument.  This allows long filenames\n        // to be specified.\n\n        if (infile[k] == '\\\"')\n        {\n            // Skip the first character(\")\n            ++k;\n\n            newargv[newargc++] = &infile[k];\n\n            // Read all characters between quotes\n\n            while (k < size && infile[k] != '\\\"' && infile[k] != '\\n')\n            {\n                ++k;\n            }\n\n            if (k >= size || infile[k] == '\\n')\n            {\n                I_Error(\"Quotes unclosed in response file '%s'\",\n                        response_filename);\n            }\n\n            // Cut off the string at the closing quote\n\n            infile[k] = '\\0';\n            ++k;\n        }\n        else\n        {\n            // Read in the next argument until a space is reached\n\n            newargv[newargc++] = &infile[k];\n\n            while(k < size && !isspace((int)infile[k]))\n            {\n                ++k;\n            }\n\n            // Cut off the end of the argument at the first space\n\n            infile[k] = '\\0';\n\n            ++k;\n        }\n    }\n\n    // Add arguments following the response file argument\n\n    for (i=argv_index + 1; i<myargc; ++i)\n    {\n        newargv[newargc] = myargv[i];\n        ++newargc;\n    }\n\n    myargv = newargv;\n    myargc = newargc;\n\n#if 0\n    // Disabled - Vanilla Doom does not do this.\n    // Display arguments\n\n    printf(\"%d command-line args:\\n\", myargc);\n\n    for (k=1; k<myargc; k++)\n    {\n        printf(\"'%s'\\n\", myargv[k]);\n    }\n#endif\n#endif\n}\n\n//\n// Find a Response File\n//\n\nvoid M_FindResponseFile(void)\n{\n    int             i;\n\n    for (i = 1; i < myargc; i++)\n    {\n        if (myargv[i][0] == '@')\n        {\n            LoadResponseFile(i);\n        }\n    }\n}\n\n// Return the name of the executable used to start the program:\n\nchar *M_GetExecutableName(void)\n{\n    char *sep;\n\n    sep = strrchr(myargv[0], DIR_SEPARATOR);\n\n    if (sep == NULL)\n    {\n        return myargv[0];\n    }\n    else\n    {\n        return sep + 1;\n    }\n}\n\n"
  },
  {
    "path": "fbdoom/m_argv.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//  Nil.\n//    \n\n\n#ifndef __M_ARGV__\n#define __M_ARGV__\n\n#include \"doomtype.h\"\n\n//\n// MISC\n//\nextern  int\tmyargc;\nextern  char**\tmyargv;\n\n// Returns the position of the given parameter\n// in the arg list (0 if not found).\nint M_CheckParm (char* check);\n\n// Same as M_CheckParm, but checks that num_args arguments are available\n// following the specified argument.\nint M_CheckParmWithArgs(char *check, int num_args);\n\nvoid M_FindResponseFile(void);\n\n// Parameter has been specified?\n\nboolean M_ParmExists(char *check);\n\n// Get name of executable used to run this program:\n\nchar *M_GetExecutableName(void);\n\n#endif\n"
  },
  {
    "path": "fbdoom/m_bbox.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tMain loop menu stuff.\n//\tRandom number LUT.\n//\tDefault Config File.\n//\tPCX Screenshots.\n//\n\n\n\n#include \"m_bbox.h\"\n\n\n\n\nvoid M_ClearBox (fixed_t *box)\n{\n    box[BOXTOP] = box[BOXRIGHT] = INT_MIN;\n    box[BOXBOTTOM] = box[BOXLEFT] = INT_MAX;\n}\n\nvoid\nM_AddToBox\n( fixed_t*\tbox,\n  fixed_t\tx,\n  fixed_t\ty )\n{\n    if (x<box[BOXLEFT])\n\tbox[BOXLEFT] = x;\n    else if (x>box[BOXRIGHT])\n\tbox[BOXRIGHT] = x;\n    if (y<box[BOXBOTTOM])\n\tbox[BOXBOTTOM] = y;\n    else if (y>box[BOXTOP])\n\tbox[BOXTOP] = y;\n}\n\n\n\n\n\n"
  },
  {
    "path": "fbdoom/m_bbox.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//    Nil.\n//    \n\n\n#ifndef __M_BBOX__\n#define __M_BBOX__\n\n#include <limits.h>\n\n#include \"m_fixed.h\"\n\n\n// Bounding box coordinate storage.\nenum\n{\n    BOXTOP,\n    BOXBOTTOM,\n    BOXLEFT,\n    BOXRIGHT\n};\t// bbox coordinates\n\n// Bounding box functions.\nvoid M_ClearBox (fixed_t*\tbox);\n\nvoid\nM_AddToBox\n( fixed_t*\tbox,\n  fixed_t\tx,\n  fixed_t\ty );\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/m_cheat.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tCheat sequence checking.\n//\n\n\n\n#include <string.h>\n\n#include \"doomtype.h\"\n#include \"m_cheat.h\"\n\n//\n// CHEAT SEQUENCE PACKAGE\n//\n\n//\n// Called in st_stuff module, which handles the input.\n// Returns a 1 if the cheat was successful, 0 if failed.\n//\nint\ncht_CheckCheat\n( cheatseq_t*\tcht,\n  char\t\tkey )\n{\n    // if we make a short sequence on a cheat with parameters, this \n    // will not work in vanilla doom.  behave the same.\n\n    if (cht->parameter_chars > 0 && strlen(cht->sequence) < cht->sequence_len)\n        return false;\n    \n    if (cht->chars_read < strlen(cht->sequence))\n    {\n        // still reading characters from the cheat code\n        // and verifying.  reset back to the beginning \n        // if a key is wrong\n\n        if (key == cht->sequence[cht->chars_read])\n            ++cht->chars_read;\n        else\n            cht->chars_read = 0;\n        \n        cht->param_chars_read = 0;\n    }\n    else if (cht->param_chars_read < cht->parameter_chars)\n    {\n        // we have passed the end of the cheat sequence and are \n        // entering parameters now \n        \n        cht->parameter_buf[cht->param_chars_read] = key;\n        \n        ++cht->param_chars_read;\n    }\n\n    if (cht->chars_read >= strlen(cht->sequence)\n     && cht->param_chars_read >= cht->parameter_chars)\n    {\n        cht->chars_read = cht->param_chars_read = 0;\n\n        return true;\n    }\n    \n    // cheat not matched yet\n\n    return false;\n}\n\nvoid\ncht_GetParam\n( cheatseq_t*\tcht,\n  char*\t\tbuffer )\n{\n    memcpy(buffer, cht->parameter_buf, cht->parameter_chars);\n}\n\n\n"
  },
  {
    "path": "fbdoom/m_cheat.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tCheat code checking.\n//\n\n\n#ifndef __M_CHEAT__\n#define __M_CHEAT__\n\n//\n// CHEAT SEQUENCE PACKAGE\n//\n\n// declaring a cheat\n\n#define CHEAT(value, parameters) \\\n    { value, sizeof(value) - 1, parameters, 0, 0, \"\" }\n\n#define MAX_CHEAT_LEN 25\n#define MAX_CHEAT_PARAMS 5\n\ntypedef struct\n{\n    // settings for this cheat\n\n    char sequence[MAX_CHEAT_LEN];\n    size_t sequence_len;\n    int parameter_chars;\n\n    // state used during the game\n\n    size_t chars_read;\n    int param_chars_read;\n    char parameter_buf[MAX_CHEAT_PARAMS];\n} cheatseq_t;\n\nint\ncht_CheckCheat\n( cheatseq_t*\t\tcht,\n  char\t\t\tkey );\n\n\nvoid\ncht_GetParam\n( cheatseq_t*\t\tcht,\n  char*\t\t\tbuffer );\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/m_config.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 1993-2008 Raven Software\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//    Configuration file interface.\n//\n\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <errno.h>\n\n#include \"config.h\"\n\n#include \"doomtype.h\"\n#include \"doomkeys.h\"\n#include \"doomfeatures.h\"\n#include \"i_system.h\"\n#include \"m_argv.h\"\n#include \"m_misc.h\"\n\n#include \"z_zone.h\"\n\n//\n// DEFAULTS\n//\n\n// Location where all configuration data is stored - \n// default.cfg, savegames, etc.\n\nchar *configdir;\n\n// Default filenames for configuration files.\n\nstatic char *default_main_config;\nstatic char *default_extra_config;\n\ntypedef enum \n{\n    DEFAULT_INT,\n    DEFAULT_INT_HEX,\n    DEFAULT_STRING,\n    DEFAULT_FLOAT,\n    DEFAULT_KEY,\n} default_type_t;\n\ntypedef struct\n{\n    // Name of the variable\n    char *name;\n\n    // Pointer to the location in memory of the variable\n    void *location;\n\n    // Type of the variable\n    default_type_t type;\n\n    // If this is a key value, the original integer scancode we read from\n    // the config file before translating it to the internal key value.\n    // If zero, we didn't read this value from a config file.\n    int untranslated;\n\n    // The value we translated the scancode into when we read the \n    // config file on startup.  If the variable value is different from\n    // this, it has been changed and needs to be converted; otherwise,\n    // use the 'untranslated' value.\n    int original_translated;\n\n    // If true, this config variable has been bound to a variable\n    // and is being used.\n    boolean bound;\n} default_t;\n\ntypedef struct\n{\n    default_t *defaults;\n    int numdefaults;\n    char *filename;\n} default_collection_t;\n\n#define CONFIG_VARIABLE_GENERIC(name, type) \\\n    { #name, NULL, type, 0, 0, false }\n\n#define CONFIG_VARIABLE_KEY(name) \\\n    CONFIG_VARIABLE_GENERIC(name, DEFAULT_KEY)\n#define CONFIG_VARIABLE_INT(name) \\\n    CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT)\n#define CONFIG_VARIABLE_INT_HEX(name) \\\n    CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT_HEX)\n#define CONFIG_VARIABLE_FLOAT(name) \\\n    CONFIG_VARIABLE_GENERIC(name, DEFAULT_FLOAT)\n#define CONFIG_VARIABLE_STRING(name) \\\n    CONFIG_VARIABLE_GENERIC(name, DEFAULT_STRING)\n\n//! @begin_config_file default\n\nstatic default_t\tdoom_defaults_list[] =\n{\n    //!\n    // Mouse sensitivity.  This value is used to multiply input mouse\n    // movement to control the effect of moving the mouse.\n    //\n    // The \"normal\" maximum value available for this through the\n    // in-game options menu is 9. A value of 31 or greater will cause\n    // the game to crash when entering the options menu.\n    //\n\n    CONFIG_VARIABLE_INT(mouse_sensitivity),\n\n    //!\n    // Volume of sound effects, range 0-15.\n    //\n\n    CONFIG_VARIABLE_INT(sfx_volume),\n\n    //!\n    // Volume of in-game music, range 0-15.\n    //\n\n    CONFIG_VARIABLE_INT(music_volume),\n\n    //!\n    // @game strife\n    //\n    // If non-zero, dialogue text is displayed over characters' pictures\n    // when engaging actors who have voices.\n    //\n\n    CONFIG_VARIABLE_INT(show_talk),\n\n    //!\n    // @game strife\n    //\n    // Volume of voice sound effects, range 0-15.\n    //\n\n    CONFIG_VARIABLE_INT(voice_volume),\n\n    //!\n    // @game doom\n    //\n    // If non-zero, messages are displayed on the heads-up display\n    // in the game (\"picked up a clip\", etc).  If zero, these messages\n    // are not displayed.\n    //\n\n    CONFIG_VARIABLE_INT(show_messages),\n\n    //!\n    // Keyboard key to turn right.\n    //\n\n    CONFIG_VARIABLE_KEY(key_right),\n\n    //!\n    // Keyboard key to turn left.\n    //\n\n    CONFIG_VARIABLE_KEY(key_left),\n\n    //!\n    // Keyboard key to move forward.\n    //\n\n    CONFIG_VARIABLE_KEY(key_up),\n\n    //!\n    // Keyboard key to move backward.\n    //\n\n    CONFIG_VARIABLE_KEY(key_down),\n\n    //!\n    // Keyboard key to strafe left.\n    //\n\n    CONFIG_VARIABLE_KEY(key_strafeleft),\n\n    //!\n    // Keyboard key to strafe right.\n    //\n\n    CONFIG_VARIABLE_KEY(key_straferight),\n\n    //!\n    // @game strife\n    //\n    // Keyboard key to use health.\n    //\n\n    CONFIG_VARIABLE_KEY(key_useHealth),\n\n    //!\n    // @game hexen\n    //\n    // Keyboard key to jump.\n    //\n\n    CONFIG_VARIABLE_KEY(key_jump),\n\n    //!\n    // @game heretic hexen\n    //\n    // Keyboard key to fly upward.\n    //\n\n    CONFIG_VARIABLE_KEY(key_flyup),\n\n    //!\n    // @game heretic hexen\n    //\n    // Keyboard key to fly downwards.\n    //\n\n    CONFIG_VARIABLE_KEY(key_flydown),\n\n    //!\n    // @game heretic hexen\n    //\n    // Keyboard key to center flying.\n    //\n\n    CONFIG_VARIABLE_KEY(key_flycenter),\n\n    //!\n    // @game heretic hexen\n    //\n    // Keyboard key to look up.\n    //\n\n    CONFIG_VARIABLE_KEY(key_lookup),\n\n    //!\n    // @game heretic hexen\n    //\n    // Keyboard key to look down.\n    //\n\n    CONFIG_VARIABLE_KEY(key_lookdown),\n\n    //!\n    // @game heretic hexen\n    //\n    // Keyboard key to center the view.\n    //\n\n    CONFIG_VARIABLE_KEY(key_lookcenter),\n\n    //!\n    // @game strife\n    //\n    // Keyboard key to query inventory.\n    //\n\n    CONFIG_VARIABLE_KEY(key_invquery),\n\n    //!\n    // @game strife\n    //\n    // Keyboard key to display mission objective.\n    //\n\n    CONFIG_VARIABLE_KEY(key_mission),\n\n    //!\n    // @game strife\n    //\n    // Keyboard key to display inventory popup.\n    //\n\n    CONFIG_VARIABLE_KEY(key_invPop),\n\n    //!\n    // @game strife\n    //\n    // Keyboard key to display keys popup.\n    //\n\n    CONFIG_VARIABLE_KEY(key_invKey),\n\n    //!\n    // @game strife\n    //\n    // Keyboard key to jump to start of inventory.\n    //\n\n    CONFIG_VARIABLE_KEY(key_invHome),\n\n    //!\n    // @game strife\n    //\n    // Keyboard key to jump to end of inventory.\n    //\n\n    CONFIG_VARIABLE_KEY(key_invEnd),\n\n    //!\n    // @game heretic hexen\n    //\n    // Keyboard key to scroll left in the inventory.\n    //\n\n    CONFIG_VARIABLE_KEY(key_invleft),\n\n    //!\n    // @game heretic hexen\n    //\n    // Keyboard key to scroll right in the inventory.\n    //\n\n    CONFIG_VARIABLE_KEY(key_invright),\n\n    //!\n    // @game strife\n    //\n    // Keyboard key to scroll left in the inventory.\n    //\n\n    CONFIG_VARIABLE_KEY(key_invLeft),\n\n    //!\n    // @game strife\n    //\n    // Keyboard key to scroll right in the inventory.\n    //\n\n    CONFIG_VARIABLE_KEY(key_invRight),\n\n    //!\n    // @game heretic hexen\n    //\n    // Keyboard key to use the current item in the inventory.\n    //\n\n    CONFIG_VARIABLE_KEY(key_useartifact),\n\n    //!\n    // @game strife\n    //\n    // Keyboard key to use inventory item.\n    //\n\n    CONFIG_VARIABLE_KEY(key_invUse),\n\n    //!\n    // @game strife\n    //\n    // Keyboard key to drop an inventory item.\n    //\n\n    CONFIG_VARIABLE_KEY(key_invDrop),\n\n    //!\n    // @game strife\n    //\n    // Keyboard key to look up.\n    //\n\n    CONFIG_VARIABLE_KEY(key_lookUp),\n\n    //!\n    // @game strife\n    //\n    // Keyboard key to look down.\n    //\n\n    CONFIG_VARIABLE_KEY(key_lookDown),\n\n    //!\n    // Keyboard key to fire the currently selected weapon.\n    //\n\n    CONFIG_VARIABLE_KEY(key_fire),\n\n    //!\n    // Keyboard key to \"use\" an object, eg. a door or switch.\n    //\n\n    CONFIG_VARIABLE_KEY(key_use),\n\n    //!\n    // Keyboard key to turn on strafing.  When held down, pressing the\n    // key to turn left or right causes the player to strafe left or\n    // right instead.\n    //\n\n    CONFIG_VARIABLE_KEY(key_strafe),\n\n    //!\n    // Keyboard key to make the player run.\n    //\n\n    CONFIG_VARIABLE_KEY(key_speed),\n\n    //!\n    // If non-zero, mouse input is enabled.  If zero, mouse input is\n    // disabled.\n    //\n\n    CONFIG_VARIABLE_INT(use_mouse),\n\n    //!\n    // Mouse button to fire the currently selected weapon.\n    //\n\n    CONFIG_VARIABLE_INT(mouseb_fire),\n\n    //!\n    // Mouse button to turn on strafing.  When held down, the player\n    // will strafe left and right instead of turning left and right.\n    //\n\n    CONFIG_VARIABLE_INT(mouseb_strafe),\n\n    //!\n    // Mouse button to move forward.\n    //\n\n    CONFIG_VARIABLE_INT(mouseb_forward),\n\n    //!\n    // @game hexen strife\n    //\n    // Mouse button to jump.\n    //\n\n    CONFIG_VARIABLE_INT(mouseb_jump),\n\n    //!\n    // If non-zero, joystick input is enabled.\n    //\n\n    CONFIG_VARIABLE_INT(use_joystick),\n\n    //!\n    // Joystick virtual button that fires the current weapon.\n    //\n\n    CONFIG_VARIABLE_INT(joyb_fire),\n\n    //!\n    // Joystick virtual button that makes the player strafe while\n    // held down.\n    //\n\n    CONFIG_VARIABLE_INT(joyb_strafe),\n\n    //!\n    // Joystick virtual button to \"use\" an object, eg. a door or switch.\n    //\n\n    CONFIG_VARIABLE_INT(joyb_use),\n\n    //!\n    // Joystick virtual button that makes the player run while held\n    // down.\n    //\n    // If this has a value of 20 or greater, the player will always run,\n    // even if use_joystick is 0.\n    //\n\n    CONFIG_VARIABLE_INT(joyb_speed),\n\n    //!\n    // @game hexen strife\n    //\n    // Joystick virtual button that makes the player jump.\n    //\n\n    CONFIG_VARIABLE_INT(joyb_jump),\n\n    //!\n    // @game doom heretic hexen\n    //\n    // Screen size, range 3-11.\n    //\n    // A value of 11 gives a full-screen view with the status bar not\n    // displayed.  A value of 10 gives a full-screen view with the\n    // status bar displayed.\n    //\n\n    CONFIG_VARIABLE_INT(screenblocks),\n\n    //!\n    // @game strife\n    //\n    // Screen size, range 3-11.\n    //\n    // A value of 11 gives a full-screen view with the status bar not\n    // displayed.  A value of 10 gives a full-screen view with the\n    // status bar displayed.\n    //\n\n    CONFIG_VARIABLE_INT(screensize),\n\n    //!\n    // @game doom\n    //\n    // Screen detail.  Zero gives normal \"high detail\" mode, while\n    // a non-zero value gives \"low detail\" mode.\n    //\n\n    CONFIG_VARIABLE_INT(detaillevel),\n\n    //!\n    // Number of sounds that will be played simultaneously.\n    //\n\n    CONFIG_VARIABLE_INT(snd_channels),\n\n    //!\n    // Music output device.  A non-zero value gives MIDI sound output,\n    // while a value of zero disables music.\n    //\n\n    CONFIG_VARIABLE_INT(snd_musicdevice),\n\n    //!\n    // Sound effects device.  A value of zero disables in-game sound\n    // effects, a value of 1 enables PC speaker sound effects, while\n    // a value in the range 2-9 enables the \"normal\" digital sound\n    // effects.\n    //\n\n    CONFIG_VARIABLE_INT(snd_sfxdevice),\n\n    //!\n    // SoundBlaster I/O port. Unused.\n    //\n\n    CONFIG_VARIABLE_INT(snd_sbport),\n\n    //!\n    // SoundBlaster IRQ.  Unused.\n    //\n\n    CONFIG_VARIABLE_INT(snd_sbirq),\n\n    //!\n    // SoundBlaster DMA channel.  Unused.\n    //\n\n    CONFIG_VARIABLE_INT(snd_sbdma),\n\n    //!\n    // Output port to use for OPL MIDI playback.  Unused.\n    //\n\n    CONFIG_VARIABLE_INT(snd_mport),\n\n    //!\n    // Gamma correction level.  A value of zero disables gamma\n    // correction, while a value in the range 1-4 gives increasing\n    // levels of gamma correction.\n    //\n\n    CONFIG_VARIABLE_INT(usegamma),\n\n    //!\n    // @game hexen\n    //\n    // Directory in which to store savegames.\n    //\n\n    CONFIG_VARIABLE_STRING(savedir),\n\n    //!\n    // @game hexen\n    //\n    // Controls whether messages are displayed in the heads-up display.\n    // If this has a non-zero value, messages are displayed.\n    //\n\n    CONFIG_VARIABLE_INT(messageson),\n\n    //!\n    // @game strife\n    //\n    // Name of background flat used by view border.\n    //\n\n    CONFIG_VARIABLE_STRING(back_flat),\n\n    //!\n    // @game strife\n    //\n    // Multiplayer nickname (?).\n    //\n\n    CONFIG_VARIABLE_STRING(nickname),\n\n    //!\n    // Multiplayer chat macro: message to send when alt+0 is pressed.\n    //\n\n    CONFIG_VARIABLE_STRING(chatmacro0),\n\n    //!\n    // Multiplayer chat macro: message to send when alt+1 is pressed.\n    //\n\n    CONFIG_VARIABLE_STRING(chatmacro1),\n\n    //!\n    // Multiplayer chat macro: message to send when alt+2 is pressed.\n    //\n\n    CONFIG_VARIABLE_STRING(chatmacro2),\n\n    //!\n    // Multiplayer chat macro: message to send when alt+3 is pressed.\n    //\n\n    CONFIG_VARIABLE_STRING(chatmacro3),\n\n    //!\n    // Multiplayer chat macro: message to send when alt+4 is pressed.\n    //\n\n    CONFIG_VARIABLE_STRING(chatmacro4),\n\n    //!\n    // Multiplayer chat macro: message to send when alt+5 is pressed.\n    //\n\n    CONFIG_VARIABLE_STRING(chatmacro5),\n\n    //!\n    // Multiplayer chat macro: message to send when alt+6 is pressed.\n    //\n\n    CONFIG_VARIABLE_STRING(chatmacro6),\n\n    //!\n    // Multiplayer chat macro: message to send when alt+7 is pressed.\n    //\n\n    CONFIG_VARIABLE_STRING(chatmacro7),\n\n    //!\n    // Multiplayer chat macro: message to send when alt+8 is pressed.\n    //\n\n    CONFIG_VARIABLE_STRING(chatmacro8),\n\n    //!\n    // Multiplayer chat macro: message to send when alt+9 is pressed.\n    //\n\n    CONFIG_VARIABLE_STRING(chatmacro9),\n\n    //!\n    // @game strife\n    //\n    // Serial port number to use for SERSETUP.EXE (unused).\n    //\n\n    CONFIG_VARIABLE_INT(comport),\n};\n\nstatic default_collection_t doom_defaults =\n{\n    doom_defaults_list,\n    arrlen(doom_defaults_list),\n    NULL,\n};\n\n//! @begin_config_file extended\n\nstatic default_t extra_defaults_list[] =\n{\n    //!\n    // @game heretic hexen strife\n    //\n    // If non-zero, display the graphical startup screen.\n    //\n\n    CONFIG_VARIABLE_INT(graphical_startup),\n\n    //!\n    // If non-zero, video settings will be autoadjusted to a valid\n    // configuration when the screen_width and screen_height variables\n    // do not match any valid configuration.\n    //\n\n    CONFIG_VARIABLE_INT(autoadjust_video_settings),\n\n    //!\n    // If non-zero, the game will run in full screen mode.  If zero,\n    // the game will run in a window.\n    //\n\n    CONFIG_VARIABLE_INT(fullscreen),\n\n    //!\n    // If non-zero, the screen will be stretched vertically to display\n    // correctly on a square pixel video mode.\n    //\n\n    CONFIG_VARIABLE_INT(aspect_ratio_correct),\n\n    //!\n    // Number of milliseconds to wait on startup after the video mode\n    // has been set, before the game will start.  This allows the\n    // screen to settle on some monitors that do not display an image\n    // for a brief interval after changing video modes.\n    //\n\n    CONFIG_VARIABLE_INT(startup_delay),\n\n    //!\n    // Screen width in pixels.  If running in full screen mode, this is\n    // the X dimension of the video mode to use.  If running in\n    // windowed mode, this is the width of the window in which the game\n    // will run.\n    //\n\n    CONFIG_VARIABLE_INT(screen_width),\n\n    //!\n    // Screen height in pixels.  If running in full screen mode, this is\n    // the Y dimension of the video mode to use.  If running in\n    // windowed mode, this is the height of the window in which the game\n    // will run.\n    //\n\n    CONFIG_VARIABLE_INT(screen_height),\n\n    //!\n    // Color depth of the screen, in bits.\n    // If this is set to zero, the color depth will be automatically set\n    // on startup to the machine's default/native color depth.\n    //\n\n    CONFIG_VARIABLE_INT(screen_bpp),\n\n    //!\n    // If this is non-zero, the mouse will be \"grabbed\" when running\n    // in windowed mode so that it can be used as an input device.\n    // When running full screen, this has no effect.\n    //\n\n    CONFIG_VARIABLE_INT(grabmouse),\n\n    //!\n    // If non-zero, all vertical mouse movement is ignored.  This\n    // emulates the behavior of the \"novert\" tool available under DOS\n    // that performs the same function.\n    //\n\n    CONFIG_VARIABLE_INT(novert),\n\n    //!\n    // Mouse acceleration factor.  When the speed of mouse movement\n    // exceeds the threshold value (mouse_threshold), the speed is\n    // multiplied by this value.\n    //\n\n    CONFIG_VARIABLE_FLOAT(mouse_acceleration),\n\n    //!\n    // Mouse acceleration threshold.  When the speed of mouse movement\n    // exceeds this threshold value, the speed is multiplied by an\n    // acceleration factor (mouse_acceleration).\n    //\n\n    CONFIG_VARIABLE_INT(mouse_threshold),\n\n    //!\n    // Sound output sample rate, in Hz.  Typical values to use are\n    // 11025, 22050, 44100 and 48000.\n    //\n\n    CONFIG_VARIABLE_INT(snd_samplerate),\n\n    //!\n    // Maximum number of bytes to allocate for caching converted sound\n    // effects in memory. If set to zero, there is no limit applied.\n    //\n\n    CONFIG_VARIABLE_INT(snd_cachesize),\n\n    //!\n    // Maximum size of the output sound buffer size in milliseconds.\n    // Sound output is generated periodically in slices. Higher values\n    // might be more efficient but will introduce latency to the\n    // sound output. The default is 28ms (one slice per tic with the\n    // 35fps timer).\n\n    CONFIG_VARIABLE_INT(snd_maxslicetime_ms),\n\n    //!\n    // External command to invoke to perform MIDI playback. If set to\n    // the empty string, SDL_mixer's internal MIDI playback is used.\n    // This only has any effect when snd_musicdevice is set to General\n    // MIDI output.\n\n    CONFIG_VARIABLE_STRING(snd_musiccmd),\n\n    //!\n    // The I/O port to use to access the OPL chip.  Only relevant when\n    // using native OPL music playback.\n    //\n\n    CONFIG_VARIABLE_INT_HEX(opl_io_port),\n\n    //!\n    // @game doom heretic strife\n    //\n    // If non-zero, the ENDOOM text screen is displayed when exiting the\n    // game. If zero, the ENDOOM screen is not displayed.\n    //\n\n    CONFIG_VARIABLE_INT(show_endoom),\n\n    //!\n    // If non-zero, save screenshots in PNG format.\n    //\n\n    CONFIG_VARIABLE_INT(png_screenshots),\n\n    //!\n    // @game doom strife\n    //\n    // If non-zero, the Vanilla savegame limit is enforced; if the\n    // savegame exceeds 180224 bytes in size, the game will exit with\n    // an error.  If this has a value of zero, there is no limit to\n    // the size of savegames.\n    //\n\n    CONFIG_VARIABLE_INT(vanilla_savegame_limit),\n\n    //!\n    // @game doom strife\n    //\n    // If non-zero, the Vanilla demo size limit is enforced; the game\n    // exits with an error when a demo exceeds the demo size limit\n    // (128KiB by default).  If this has a value of zero, there is no\n    // limit to the size of demos.\n    //\n\n    CONFIG_VARIABLE_INT(vanilla_demo_limit),\n\n    //!\n    // If non-zero, the game behaves like Vanilla Doom, always assuming\n    // an American keyboard mapping.  If this has a value of zero, the\n    // native keyboard mapping of the keyboard is used.\n    //\n\n    CONFIG_VARIABLE_INT(vanilla_keyboard_mapping),\n\n    //!\n    // Name of the SDL video driver to use.  If this is an empty string,\n    // the default video driver is used.\n    //\n\n    CONFIG_VARIABLE_STRING(video_driver),\n\n    //!\n    // Position of the window on the screen when running in windowed\n    // mode. Accepted values are: \"\" (empty string) - don't care,\n    // \"center\" - place window at center of screen, \"x,y\" - place\n    // window at the specified coordinates.\n\n    CONFIG_VARIABLE_STRING(window_position),\n\n#ifdef FEATURE_MULTIPLAYER\n\n    //!\n    // Name to use in network games for identification.  This is only\n    // used on the \"waiting\" screen while waiting for the game to start.\n    //\n\n    CONFIG_VARIABLE_STRING(player_name),\n\n#endif\n\n    //!\n    // Joystick number to use; '0' is the first joystick.  A negative\n    // value ('-1') indicates that no joystick is configured.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_index),\n\n    //!\n    // Joystick axis to use to for horizontal (X) movement.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_x_axis),\n\n    //!\n    // If non-zero, movement on the horizontal joystick axis is inverted.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_x_invert),\n\n    //!\n    // Joystick axis to use to for vertical (Y) movement.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_y_axis),\n\n    //!\n    // If non-zero, movement on the vertical joystick axis is inverted.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_y_invert),\n\n    //!\n    // Joystick axis to use to for strafing movement.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_strafe_axis),\n\n    //!\n    // If non-zero, movement on the joystick axis used for strafing\n    // is inverted.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_strafe_invert),\n\n    //!\n    // The physical joystick button that corresponds to joystick\n    // virtual button #0.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_physical_button0),\n\n    //!\n    // The physical joystick button that corresponds to joystick\n    // virtual button #1.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_physical_button1),\n\n    //!\n    // The physical joystick button that corresponds to joystick\n    // virtual button #2.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_physical_button2),\n\n    //!\n    // The physical joystick button that corresponds to joystick\n    // virtual button #3.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_physical_button3),\n\n    //!\n    // The physical joystick button that corresponds to joystick\n    // virtual button #4.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_physical_button4),\n\n    //!\n    // The physical joystick button that corresponds to joystick\n    // virtual button #5.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_physical_button5),\n\n    //!\n    // The physical joystick button that corresponds to joystick\n    // virtual button #6.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_physical_button6),\n\n    //!\n    // The physical joystick button that corresponds to joystick\n    // virtual button #7.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_physical_button7),\n\n    //!\n    // The physical joystick button that corresponds to joystick\n    // virtual button #8.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_physical_button8),\n\n    //!\n    // The physical joystick button that corresponds to joystick\n    // virtual button #9.\n    //\n\n    CONFIG_VARIABLE_INT(joystick_physical_button9),\n\n    //!\n    // Joystick virtual button to make the player strafe left.\n    //\n\n    CONFIG_VARIABLE_INT(joyb_strafeleft),\n\n    //!\n    // Joystick virtual button to make the player strafe right.\n    //\n\n    CONFIG_VARIABLE_INT(joyb_straferight),\n\n    //!\n    // Joystick virtual button to activate the menu.\n    //\n\n    CONFIG_VARIABLE_INT(joyb_menu_activate),\n\n    //!\n    // Joystick virtual button that cycles to the previous weapon.\n    //\n\n    CONFIG_VARIABLE_INT(joyb_prevweapon),\n\n    //!\n    // Joystick virtual button that cycles to the next weapon.\n    //\n\n    CONFIG_VARIABLE_INT(joyb_nextweapon),\n\n    //!\n    // Mouse button to strafe left.\n    //\n\n    CONFIG_VARIABLE_INT(mouseb_strafeleft),\n\n    //!\n    // Mouse button to strafe right.\n    //\n\n    CONFIG_VARIABLE_INT(mouseb_straferight),\n\n    //!\n    // Mouse button to \"use\" an object, eg. a door or switch.\n    //\n\n    CONFIG_VARIABLE_INT(mouseb_use),\n\n    //!\n    // Mouse button to move backwards.\n    //\n\n    CONFIG_VARIABLE_INT(mouseb_backward),\n\n    //!\n    // Mouse button to cycle to the previous weapon.\n    //\n\n    CONFIG_VARIABLE_INT(mouseb_prevweapon),\n\n    //!\n    // Mouse button to cycle to the next weapon.\n    //\n\n    CONFIG_VARIABLE_INT(mouseb_nextweapon),\n\n    //!\n    // If non-zero, double-clicking a mouse button acts like pressing\n    // the \"use\" key to use an object in-game, eg. a door or switch.\n    //\n\n    CONFIG_VARIABLE_INT(dclick_use),\n\n#ifdef FEATURE_SOUND\n\n    //!\n    // Controls whether libsamplerate support is used for performing\n    // sample rate conversions of sound effects.  Support for this\n    // must be compiled into the program.\n    //\n    // If zero, libsamplerate support is disabled.  If non-zero,\n    // libsamplerate is enabled. Increasing values roughly correspond\n    // to higher quality conversion; the higher the quality, the\n    // slower the conversion process.  Linear conversion = 1;\n    // Zero order hold = 2; Fast Sinc filter = 3; Medium quality\n    // Sinc filter = 4; High quality Sinc filter = 5.\n    //\n\n    CONFIG_VARIABLE_INT(use_libsamplerate),\n\n    //!\n    // Scaling factor used by libsamplerate. This is used when converting\n    // sounds internally back into integer form; normally it should not\n    // be necessary to change it from the default value. The only time\n    // it might be needed is if a PWAD file is loaded that contains very\n    // loud sounds, in which case the conversion may cause sound clipping\n    // and the scale factor should be reduced. The lower the value, the\n    // quieter the sound effects become, so it should be set as high as is\n    // possible without clipping occurring.\n\n    CONFIG_VARIABLE_FLOAT(libsamplerate_scale),\n\n    //!\n    // Full path to a Timidity configuration file to use for MIDI\n    // playback. The file will be evaluated from the directory where\n    // it is evaluated, so there is no need to add \"dir\" commands\n    // into it.\n    //\n\n    CONFIG_VARIABLE_STRING(timidity_cfg_path),\n\n    //!\n    // Path to GUS patch files to use when operating in GUS emulation\n    // mode.\n    //\n\n    CONFIG_VARIABLE_STRING(gus_patch_path),\n\n    //!\n    // Number of kilobytes of RAM to use in GUS emulation mode. Valid\n    // values are 256, 512, 768 or 1024.\n    //\n\n    CONFIG_VARIABLE_INT(gus_ram_kb),\n\n#endif\n\n    //!\n    // Key to pause or unpause the game.\n    //\n\n    CONFIG_VARIABLE_KEY(key_pause),\n\n    //!\n    // Key that activates the menu when pressed.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_activate),\n\n    //!\n    // Key that moves the cursor up on the menu.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_up),\n\n    //!\n    // Key that moves the cursor down on the menu.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_down),\n\n    //!\n    // Key that moves the currently selected slider on the menu left.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_left),\n\n    //!\n    // Key that moves the currently selected slider on the menu right.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_right),\n\n    //!\n    // Key to go back to the previous menu.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_back),\n\n    //!\n    // Key to activate the currently selected menu item.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_forward),\n\n    //!\n    // Key to answer 'yes' to a question in the menu.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_confirm),\n\n    //!\n    // Key to answer 'no' to a question in the menu.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_abort),\n\n    //!\n    // Keyboard shortcut to bring up the help screen.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_help),\n\n    //!\n    // Keyboard shortcut to bring up the save game menu.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_save),\n\n    //!\n    // Keyboard shortcut to bring up the load game menu.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_load),\n\n    //!\n    // Keyboard shortcut to bring up the sound volume menu.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_volume),\n\n    //!\n    // Keyboard shortcut to toggle the detail level.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_detail),\n\n    //!\n    // Keyboard shortcut to quicksave the current game.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_qsave),\n\n    //!\n    // Keyboard shortcut to end the game.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_endgame),\n\n    //!\n    // Keyboard shortcut to toggle heads-up messages.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_messages),\n\n    //!\n    // Keyboard shortcut to load the last quicksave.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_qload),\n\n    //!\n    // Keyboard shortcut to quit the game.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_quit),\n\n    //!\n    // Keyboard shortcut to toggle the gamma correction level.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_gamma),\n\n    //!\n    // Keyboard shortcut to switch view in multiplayer.\n    //\n\n    CONFIG_VARIABLE_KEY(key_spy),\n\n    //!\n    // Keyboard shortcut to increase the screen size.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_incscreen),\n\n    //!\n    // Keyboard shortcut to decrease the screen size.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_decscreen),\n\n    //!\n    // Keyboard shortcut to save a screenshot.\n    //\n\n    CONFIG_VARIABLE_KEY(key_menu_screenshot),\n\n    //!\n    // Key to toggle the map view.\n    //\n\n    CONFIG_VARIABLE_KEY(key_map_toggle),\n\n    //!\n    // Key to pan north when in the map view.\n    //\n\n    CONFIG_VARIABLE_KEY(key_map_north),\n\n    //!\n    // Key to pan south when in the map view.\n    //\n\n    CONFIG_VARIABLE_KEY(key_map_south),\n\n    //!\n    // Key to pan east when in the map view.\n    //\n\n    CONFIG_VARIABLE_KEY(key_map_east),\n\n    //!\n    // Key to pan west when in the map view.\n    //\n\n    CONFIG_VARIABLE_KEY(key_map_west),\n\n    //!\n    // Key to zoom in when in the map view.\n    //\n\n    CONFIG_VARIABLE_KEY(key_map_zoomin),\n\n    //!\n    // Key to zoom out when in the map view.\n    //\n\n    CONFIG_VARIABLE_KEY(key_map_zoomout),\n\n    //!\n    // Key to zoom out the maximum amount when in the map view.\n    //\n\n    CONFIG_VARIABLE_KEY(key_map_maxzoom),\n\n    //!\n    // Key to toggle follow mode when in the map view.\n    //\n\n    CONFIG_VARIABLE_KEY(key_map_follow),\n\n    //!\n    // Key to toggle the grid display when in the map view.\n    //\n\n    CONFIG_VARIABLE_KEY(key_map_grid),\n\n    //!\n    // Key to set a mark when in the map view.\n    //\n\n    CONFIG_VARIABLE_KEY(key_map_mark),\n\n    //!\n    // Key to clear all marks when in the map view.\n    //\n\n    CONFIG_VARIABLE_KEY(key_map_clearmark),\n\n    //!\n    // Key to select weapon 1.\n    //\n\n    CONFIG_VARIABLE_KEY(key_weapon1),\n\n    //!\n    // Key to select weapon 2.\n    //\n\n    CONFIG_VARIABLE_KEY(key_weapon2),\n\n    //!\n    // Key to select weapon 3.\n    //\n\n    CONFIG_VARIABLE_KEY(key_weapon3),\n\n    //!\n    // Key to select weapon 4.\n    //\n\n    CONFIG_VARIABLE_KEY(key_weapon4),\n\n    //!\n    // Key to select weapon 5.\n    //\n\n    CONFIG_VARIABLE_KEY(key_weapon5),\n\n    //!\n    // Key to select weapon 6.\n    //\n\n    CONFIG_VARIABLE_KEY(key_weapon6),\n\n    //!\n    // Key to select weapon 7.\n    //\n\n    CONFIG_VARIABLE_KEY(key_weapon7),\n\n    //!\n    // Key to select weapon 8.\n    //\n\n    CONFIG_VARIABLE_KEY(key_weapon8),\n\n    //!\n    // Key to cycle to the previous weapon.\n    //\n\n    CONFIG_VARIABLE_KEY(key_prevweapon),\n\n    //!\n    // Key to cycle to the next weapon.\n    //\n\n    CONFIG_VARIABLE_KEY(key_nextweapon),\n\n    //!\n    // @game hexen\n    //\n    // Key to use one of each artifact.\n    //\n\n    CONFIG_VARIABLE_KEY(key_arti_all),\n\n    //!\n    // @game hexen\n    //\n    // Key to use \"quartz flask\" artifact.\n    //\n\n    CONFIG_VARIABLE_KEY(key_arti_health),\n\n    //!\n    // @game hexen\n    //\n    // Key to use \"flechette\" artifact.\n    //\n\n    CONFIG_VARIABLE_KEY(key_arti_poisonbag),\n\n    //!\n    // @game hexen\n    //\n    // Key to use \"disc of repulsion\" artifact.\n    //\n\n    CONFIG_VARIABLE_KEY(key_arti_blastradius),\n\n    //!\n    // @game hexen\n    //\n    // Key to use \"chaos device\" artifact.\n    //\n\n    CONFIG_VARIABLE_KEY(key_arti_teleport),\n\n    //!\n    // @game hexen\n    //\n    // Key to use \"banishment device\" artifact.\n    //\n\n    CONFIG_VARIABLE_KEY(key_arti_teleportother),\n\n    //!\n    // @game hexen\n    //\n    // Key to use \"porkalator\" artifact.\n    //\n\n    CONFIG_VARIABLE_KEY(key_arti_egg),\n\n    //!\n    // @game hexen\n    //\n    // Key to use \"icon of the defender\" artifact.\n    //\n\n    CONFIG_VARIABLE_KEY(key_arti_invulnerability),\n\n    //!\n    // Key to re-display last message.\n    //\n\n    CONFIG_VARIABLE_KEY(key_message_refresh),\n\n    //!\n    // Key to quit the game when recording a demo.\n    //\n\n    CONFIG_VARIABLE_KEY(key_demo_quit),\n\n    //!\n    // Key to send a message during multiplayer games.\n    //\n\n    CONFIG_VARIABLE_KEY(key_multi_msg),\n\n    //!\n    // Key to send a message to player 1 during multiplayer games.\n    //\n\n    CONFIG_VARIABLE_KEY(key_multi_msgplayer1),\n\n    //!\n    // Key to send a message to player 2 during multiplayer games.\n    //\n\n    CONFIG_VARIABLE_KEY(key_multi_msgplayer2),\n\n    //!\n    // Key to send a message to player 3 during multiplayer games.\n    //\n\n    CONFIG_VARIABLE_KEY(key_multi_msgplayer3),\n\n    //!\n    // Key to send a message to player 4 during multiplayer games.\n    //\n\n    CONFIG_VARIABLE_KEY(key_multi_msgplayer4),\n\n    //!\n    // @game hexen strife\n    //\n    // Key to send a message to player 5 during multiplayer games.\n    //\n\n    CONFIG_VARIABLE_KEY(key_multi_msgplayer5),\n\n    //!\n    // @game hexen strife\n    //\n    // Key to send a message to player 6 during multiplayer games.\n    //\n\n    CONFIG_VARIABLE_KEY(key_multi_msgplayer6),\n\n    //!\n    // @game hexen strife\n    //\n    // Key to send a message to player 7 during multiplayer games.\n    //\n\n    CONFIG_VARIABLE_KEY(key_multi_msgplayer7),\n\n    //!\n    // @game hexen strife\n    //\n    // Key to send a message to player 8 during multiplayer games.\n    //\n\n    CONFIG_VARIABLE_KEY(key_multi_msgplayer8),\n};\n\nstatic default_collection_t extra_defaults =\n{\n    extra_defaults_list,\n    arrlen(extra_defaults_list),\n    NULL,\n};\n\n// Search a collection for a variable\n\nstatic default_t *SearchCollection(default_collection_t *collection, char *name)\n{\n    int i;\n\n    for (i=0; i<collection->numdefaults; ++i) \n    {\n        if (!strcmp(name, collection->defaults[i].name))\n        {\n            return &collection->defaults[i];\n        }\n    }\n\n    return NULL;\n}\n\n// Mapping from DOS keyboard scan code to internal key code (as defined\n// in doomkey.h). I think I (fraggle) reused this from somewhere else\n// but I can't find where. Anyway, notes:\n//  * KEY_PAUSE is wrong - it's in the KEY_NUMLOCK spot. This shouldn't\n//    matter in terms of Vanilla compatibility because neither of\n//    those were valid for key bindings.\n//  * There is no proper scan code for PrintScreen (on DOS machines it\n//    sends an interrupt). So I added a fake scan code of 126 for it.\n//    The presence of this is important so we can bind PrintScreen as\n//    a screenshot key.\nstatic const int scantokey[128] =\n{\n    0  ,    27,     '1',    '2',    '3',    '4',    '5',    '6',\n    '7',    '8',    '9',    '0',    '-',    '=',    KEY_BACKSPACE, 9,\n    'q',    'w',    'e',    'r',    't',    'y',    'u',    'i',\n    'o',    'p',    '[',    ']',    13,\t\tKEY_RCTRL, 'a',    's',\n    'd',    'f',    'g',    'h',    'j',    'k',    'l',    ';',\n    '\\'',   '`',    KEY_RSHIFT,'\\\\',   'z',    'x',    'c',    'v',\n    'b',    'n',    'm',    ',',    '.',    '/',    KEY_RSHIFT,KEYP_MULTIPLY,\n    KEY_RALT,  ' ',  KEY_CAPSLOCK,KEY_F1,  KEY_F2,   KEY_F3,   KEY_F4,   KEY_F5,\n    KEY_F6,   KEY_F7,   KEY_F8,   KEY_F9,   KEY_F10,  /*KEY_NUMLOCK?*/KEY_PAUSE,KEY_SCRLCK,KEY_HOME,\n    KEY_UPARROW,KEY_PGUP,KEY_MINUS,KEY_LEFTARROW,KEYP_5,KEY_RIGHTARROW,KEYP_PLUS,KEY_END,\n    KEY_DOWNARROW,KEY_PGDN,KEY_INS,KEY_DEL,0,   0,      0,      KEY_F11,\n    KEY_F12,  0,      0,      0,      0,      0,      0,      0,\n    0,      0,      0,      0,      0,      0,      0,      0,\n    0,      0,      0,      0,      0,      0,      0,      0,\n    0,      0,      0,      0,      0,      0,      0,      0,\n    0,      0,      0,      0,      0,      0,      KEY_PRTSCR, 0\n};\n\n\nstatic void SaveDefaultCollection(default_collection_t *collection)\n{\n#if ORIGCODE\n    default_t *defaults;\n    int i, v;\n    FILE *f;\n\t\n    f = fopen (collection->filename, \"w\");\n    if (!f)\n\treturn; // can't write the file, but don't complain\n\n    defaults = collection->defaults;\n\t\t\n    for (i=0 ; i<collection->numdefaults ; i++)\n    {\n        int chars_written;\n\n        // Ignore unbound variables\n\n        if (!defaults[i].bound)\n        {\n            continue;\n        }\n\n        // Print the name and line up all values at 30 characters\n\n        chars_written = fprintf(f, \"%s \", defaults[i].name);\n\n        for (; chars_written < 30; ++chars_written)\n            fprintf(f, \" \");\n\n        // Print the value\n\n        switch (defaults[i].type) \n        {\n            case DEFAULT_KEY:\n\n                // use the untranslated version if we can, to reduce\n                // the possibility of screwing up the user's config\n                // file\n                \n                v = * (int *) defaults[i].location;\n\n                if (v == KEY_RSHIFT)\n                {\n                    // Special case: for shift, force scan code for\n                    // right shift, as this is what Vanilla uses.\n                    // This overrides the change check below, to fix\n                    // configuration files made by old versions that\n                    // mistakenly used the scan code for left shift.\n\n                    v = 54;\n                }\n                else if (defaults[i].untranslated\n                      && v == defaults[i].original_translated)\n                {\n                    // Has not been changed since the last time we\n                    // read the config file.\n\n                    v = defaults[i].untranslated;\n                }\n                else\n                {\n                    // search for a reverse mapping back to a scancode\n                    // in the scantokey table\n\n                    int s;\n\n                    for (s=0; s<128; ++s)\n                    {\n                        if (scantokey[s] == v)\n                        {\n                            v = s;\n                            break;\n                        }\n                    }\n                }\n\n\t        fprintf(f, \"%i\", v);\n                break;\n\n            case DEFAULT_INT:\n\t        fprintf(f, \"%i\", * (int *) defaults[i].location);\n                break;\n\n            case DEFAULT_INT_HEX:\n\t        fprintf(f, \"0x%x\", * (int *) defaults[i].location);\n                break;\n\n            case DEFAULT_FLOAT:\n                fprintf(f, \"%f\", * (float *) defaults[i].location);\n                break;\n\n            case DEFAULT_STRING:\n\t        fprintf(f,\"\\\"%s\\\"\", * (char **) (defaults[i].location));\n                break;\n        }\n\n        fprintf(f, \"\\n\");\n    }\n\n    fclose (f);\n#endif\n}\n\n// Parses integer values in the configuration file\n\nstatic int ParseIntParameter(char *strparm)\n{\n    int parm;\n\n    if (strparm[0] == '0' && strparm[1] == 'x')\n        sscanf(strparm+2, \"%x\", &parm);\n    else\n        sscanf(strparm, \"%i\", &parm);\n\n    return parm;\n}\n\nstatic void SetVariable(default_t *def, char *value)\n{\n    int intparm;\n\n    // parameter found\n\n    switch (def->type)\n    {\n        case DEFAULT_STRING:\n            * (char **) def->location = strdup(value);\n            break;\n\n        case DEFAULT_INT:\n        case DEFAULT_INT_HEX:\n            * (int *) def->location = ParseIntParameter(value);\n            break;\n\n        case DEFAULT_KEY:\n\n            // translate scancodes read from config\n            // file (save the old value in untranslated)\n\n            intparm = ParseIntParameter(value);\n            def->untranslated = intparm;\n            if (intparm >= 0 && intparm < 128)\n            {\n                intparm = scantokey[intparm];\n            }\n            else\n            {\n                intparm = 0;\n            }\n\n            def->original_translated = intparm;\n            * (int *) def->location = intparm;\n            break;\n\n        case DEFAULT_FLOAT:\n            * (float *) def->location = (float) atof(value);\n            break;\n    }\n}\n\nstatic void LoadDefaultCollection(default_collection_t *collection)\n{\n#if ORIGCODE\n    FILE *f;\n    default_t *def;\n    char defname[80];\n    char strparm[100];\n\n    // read the file in, overriding any set defaults\n    f = fopen(collection->filename, \"r\");\n\n    if (f == NULL)\n    {\n        // File not opened, but don't complain. \n        // It's probably just the first time they ran the game.\n\n        return;\n    }\n\n    while (!feof(f))\n    {\n        if (fscanf(f, \"%79s %99[^\\n]\\n\", defname, strparm) != 2)\n        {\n            // This line doesn't match\n\n            continue;\n        }\n\n        // Find the setting in the list\n\n        def = SearchCollection(collection, defname);\n\n        if (def == NULL || !def->bound)\n        {\n            // Unknown variable?  Unbound variables are also treated\n            // as unknown.\n\n            continue;\n        }\n\n        // Strip off trailing non-printable characters (\\r characters\n        // from DOS text files)\n\n        while (strlen(strparm) > 0 && !isprint(strparm[strlen(strparm)-1]))\n        {\n            strparm[strlen(strparm)-1] = '\\0';\n        }\n\n        // Surrounded by quotes? If so, remove them.\n        if (strlen(strparm) >= 2\n         && strparm[0] == '\"' && strparm[strlen(strparm) - 1] == '\"')\n        {\n            strparm[strlen(strparm) - 1] = '\\0';\n            memmove(strparm, strparm + 1, sizeof(strparm) - 1);\n        }\n\n        SetVariable(def, strparm);\n    }\n\n    fclose (f);\n#endif\n}\n\n// Set the default filenames to use for configuration files.\n\nvoid M_SetConfigFilenames(char *main_config, char *extra_config)\n{\n    default_main_config = main_config;\n    default_extra_config = extra_config;\n}\n\n//\n// M_SaveDefaults\n//\n\nvoid M_SaveDefaults (void)\n{\n    SaveDefaultCollection(&doom_defaults);\n    SaveDefaultCollection(&extra_defaults);\n}\n\n//\n// Save defaults to alternate filenames\n//\n\nvoid M_SaveDefaultsAlternate(char *main, char *extra)\n{\n    char *orig_main;\n    char *orig_extra;\n\n    // Temporarily change the filenames\n\n    orig_main = doom_defaults.filename;\n    orig_extra = extra_defaults.filename;\n\n    doom_defaults.filename = main;\n    extra_defaults.filename = extra;\n\n    M_SaveDefaults();\n\n    // Restore normal filenames\n\n    doom_defaults.filename = orig_main;\n    extra_defaults.filename = orig_extra;\n}\n\n//\n// M_LoadDefaults\n//\n\nvoid M_LoadDefaults (void)\n{\n    int i;\n \n    // check for a custom default file\n\n    //!\n    // @arg <file>\n    // @vanilla\n    //\n    // Load main configuration from the specified file, instead of the\n    // default.\n    //\n\n    i = M_CheckParmWithArgs(\"-config\", 1);\n\n    if (i)\n    {\n\tdoom_defaults.filename = myargv[i+1];\n\tprintf (\"\tdefault file: %s\\n\",doom_defaults.filename);\n    }\n    else\n    {\n        doom_defaults.filename\n            = M_StringJoin(configdir, default_main_config, NULL);\n    }\n\n    printf(\"saving config in %s\\n\", doom_defaults.filename);\n\n    //!\n    // @arg <file>\n    //\n    // Load additional configuration from the specified file, instead of\n    // the default.\n    //\n\n    i = M_CheckParmWithArgs(\"-extraconfig\", 1);\n\n    if (i)\n    {\n        extra_defaults.filename = myargv[i+1];\n        printf(\"        extra configuration file: %s\\n\", \n               extra_defaults.filename);\n    }\n    else\n    {\n        extra_defaults.filename\n            = M_StringJoin(configdir, default_extra_config, NULL);\n    }\n\n    LoadDefaultCollection(&doom_defaults);\n    LoadDefaultCollection(&extra_defaults);\n}\n\n// Get a configuration file variable by its name\n\nstatic default_t *GetDefaultForName(char *name)\n{\n    default_t *result;\n\n    // Try the main list and the extras\n\n    result = SearchCollection(&doom_defaults, name);\n\n    if (result == NULL)\n    {\n        result = SearchCollection(&extra_defaults, name);\n    }\n\n    // Not found? Internal error.\n\n    if (result == NULL)\n    {\n        I_Error(\"Unknown configuration variable: '%s'\", name);\n    }\n\n    return result;\n}\n\n//\n// Bind a variable to a given configuration file variable, by name.\n//\n\nvoid M_BindVariable(char *name, void *location)\n{\n    default_t *variable;\n\n    variable = GetDefaultForName(name);\n\n    variable->location = location;\n    variable->bound = true;\n}\n\n// Set the value of a particular variable; an API function for other\n// parts of the program to assign values to config variables by name.\n\nboolean M_SetVariable(char *name, char *value)\n{\n    default_t *variable;\n\n    variable = GetDefaultForName(name);\n\n    if (variable == NULL || !variable->bound)\n    {\n        return false;\n    }\n\n    SetVariable(variable, value);\n\n    return true;\n}\n\n// Get the value of a variable.\n\nint M_GetIntVariable(char *name)\n{\n    default_t *variable;\n\n    variable = GetDefaultForName(name);\n\n    if (variable == NULL || !variable->bound\n     || (variable->type != DEFAULT_INT && variable->type != DEFAULT_INT_HEX))\n    {\n        return 0;\n    }\n\n    return *((int *) variable->location);\n}\n\nconst char *M_GetStrVariable(char *name)\n{\n    default_t *variable;\n\n    variable = GetDefaultForName(name);\n\n    if (variable == NULL || !variable->bound\n     || variable->type != DEFAULT_STRING)\n    {\n        return NULL;\n    }\n\n    return *((const char **) variable->location);\n}\n\nfloat M_GetFloatVariable(char *name)\n{\n    default_t *variable;\n\n    variable = GetDefaultForName(name);\n\n    if (variable == NULL || !variable->bound\n     || variable->type != DEFAULT_FLOAT)\n    {\n        return 0;\n    }\n\n    return *((float *) variable->location);\n}\n\n// Get the path to the default configuration dir to use, if NULL\n// is passed to M_SetConfigDir.\n\nstatic char *GetDefaultConfigDir(void)\n{\n#if !defined(_WIN32) || defined(_WIN32_WCE)\n\n    // Configuration settings are stored in ~/.chocolate-doom/,\n    // except on Windows, where we behave like Vanilla Doom and\n    // save in the current directory.\n\n    char *homedir;\n    char *result;\n\n    // frosted HACK - homedir = getenv(\"HOME\");\n    homedir = \"/mnt\";\n\n    if (homedir != NULL)\n    {\n        // put all configuration in a config directory off the\n        // homedir\n\n        result = M_StringJoin(homedir, DIR_SEPARATOR_S,\n                              \".\" PACKAGE_TARNAME, DIR_SEPARATOR_S, NULL);\n\n        return result;\n    }\n    else\n#endif /* #ifndef _WIN32 */\n    {\n        return strdup(FILES_DIR\"/\");\n    }\n}\n\n// \n// SetConfigDir:\n//\n// Sets the location of the configuration directory, where configuration\n// files are stored - default.cfg, chocolate-doom.cfg, savegames, etc.\n//\n\nvoid M_SetConfigDir(char *dir)\n{\n    // Use the directory that was passed, or find the default.\n\n    if (dir != NULL)\n    {\n        configdir = dir;\n    }\n    else\n    {\n        configdir = GetDefaultConfigDir();\n    }\n\n    if (strcmp(configdir, \"\") != 0)\n    {\n        printf(\"Using %s for configuration and saves\\n\", configdir);\n    }\n\n    // Make the directory if it doesn't already exist:\n\n    M_MakeDirectory(configdir);\n}\n\n//\n// Calculate the path to the directory to use to store save games.\n// Creates the directory as necessary.\n//\n\nchar *M_GetSaveGameDir(char *iwadname)\n{\n    char *savegamedir;\n#if ORIGCODE\n    char *topdir;\n#endif\n\n    // If not \"doing\" a configuration directory (Windows), don't \"do\"\n    // a savegame directory, either.\n\n    if (!strcmp(configdir, \"\"))\n    {\n    \tsavegamedir = strdup(\"\");\n    }\n    else\n    {\n#if ORIGCODE\n        // ~/.chocolate-doom/savegames\n\n        topdir = M_StringJoin(configdir, \"savegame\", NULL);\n        M_MakeDirectory(topdir);\n\n        // eg. ~/.chocolate-doom/savegames/doom2.wad/\n\n        savegamedir = M_StringJoin(topdir, DIR_SEPARATOR_S, iwadname,\n                                   DIR_SEPARATOR_S, NULL);\n\n        M_MakeDirectory(savegamedir);\n\n        free(topdir);\n#else\n        savegamedir = M_StringJoin(configdir, \"savegame/\", NULL);\n\n        M_MakeDirectory(savegamedir);\n\n        printf (\"Using %s for savegames\\n\", savegamedir);\n#endif\n    }\n\n    return savegamedir;\n}\n\n"
  },
  {
    "path": "fbdoom/m_config.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//      Configuration file interface.\n//    \n\n\n#ifndef __M_CONFIG__\n#define __M_CONFIG__\n\n#include \"doomtype.h\"\n\nvoid M_LoadDefaults(void);\nvoid M_SaveDefaults(void);\nvoid M_SaveDefaultsAlternate(char *main, char *extra);\nvoid M_SetConfigDir(char *dir);\nvoid M_BindVariable(char *name, void *variable);\nboolean M_SetVariable(char *name, char *value);\nint M_GetIntVariable(char *name);\nconst char *M_GetStrVariable(char *name);\nfloat M_GetFloatVariable(char *name);\nvoid M_SetConfigFilenames(char *main_config, char *extra_config);\nchar *M_GetSaveGameDir(char *iwadname);\n\nextern char *configdir;\n\n#endif\n"
  },
  {
    "path": "fbdoom/m_controls.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 1993-2008 Raven Software\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n\n#include <stdio.h>\n\n#include \"doomtype.h\"\n#include \"doomkeys.h\"\n\n#include \"m_config.h\"\n#include \"m_misc.h\"\n\n//\n// Keyboard controls\n//\n\nint key_right = KEY_RIGHTARROW;\nint key_left = KEY_LEFTARROW;\nint key_up = KEY_UPARROW;\nint key_down = KEY_DOWNARROW; \nint key_strafeleft = KEY_STRAFE_L;\nint key_straferight = KEY_STRAFE_R;\nint key_fire = KEY_FIRE;\nint key_use = KEY_USE;\nint key_strafe = KEY_RALT;\nint key_speed = KEY_RSHIFT; \n\n// \n// Heretic keyboard controls\n//\n \nint key_flyup = KEY_PGUP;\nint key_flydown = KEY_INS;\nint key_flycenter = KEY_HOME;\n\nint key_lookup = KEY_PGDN;\nint key_lookdown = KEY_DEL;\nint key_lookcenter = KEY_END;\n\nint key_invleft = '[';\nint key_invright = ']';\nint key_useartifact = KEY_ENTER;\n\n//\n// Hexen key controls\n//\n\nint key_jump = '/';\n\nint key_arti_all             = KEY_BACKSPACE;\nint key_arti_health          = '\\\\';\nint key_arti_poisonbag       = '0';\nint key_arti_blastradius     = '9';\nint key_arti_teleport        = '8';\nint key_arti_teleportother   = '7';\nint key_arti_egg             = '6';\nint key_arti_invulnerability = '5';\n\n//\n// Strife key controls\n//\n// haleyjd 09/01/10\n//\n\n// Note: Strife also uses key_invleft, key_invright, key_jump, key_lookup, and\n// key_lookdown, but with different default values.\n\nint key_usehealth = 'h';\nint key_invquery  = 'q';\nint key_mission   = 'w';\nint key_invpop    = 'z';\nint key_invkey    = 'k';\nint key_invhome   = KEY_HOME;\nint key_invend    = KEY_END;\nint key_invuse    = KEY_ENTER;\nint key_invdrop   = KEY_BACKSPACE;\n\n\n//\n// Mouse controls\n//\n\nint mousebfire = 0;\nint mousebstrafe = 1;\nint mousebforward = 2;\n\nint mousebjump = -1;\n\nint mousebstrafeleft = -1;\nint mousebstraferight = -1;\nint mousebbackward = -1;\nint mousebuse = -1;\n\nint mousebprevweapon = -1;\nint mousebnextweapon = -1;\n\n\nint key_message_refresh = KEY_ENTER;\nint key_pause = KEY_PAUSE;\nint key_demo_quit = 'q';\nint key_spy = KEY_F12;\n\n// Multiplayer chat keys:\n\nint key_multi_msg = 't';\nint key_multi_msgplayer[8];\n\n// Weapon selection keys:\n\nint key_weapon1 = '1';\nint key_weapon2 = '2';\nint key_weapon3 = '3';\nint key_weapon4 = '4';\nint key_weapon5 = '5';\nint key_weapon6 = '6';\nint key_weapon7 = '7';\nint key_weapon8 = '8';\nint key_prevweapon = 0;\nint key_nextweapon = 0;\n\n// Map control keys:\n\nint key_map_north     = KEY_UPARROW;\nint key_map_south     = KEY_DOWNARROW;\nint key_map_east      = KEY_RIGHTARROW;\nint key_map_west      = KEY_LEFTARROW;\nint key_map_zoomin    = '=';\nint key_map_zoomout   = '-';\nint key_map_toggle    = KEY_TAB;\nint key_map_maxzoom   = '0';\nint key_map_follow    = 'f';\nint key_map_grid      = 'g';\nint key_map_mark      = 'm';\nint key_map_clearmark = 'c';\n\n// menu keys:\n\nint key_menu_activate  = KEY_ESCAPE;\nint key_menu_up        = KEY_UPARROW;\nint key_menu_down      = KEY_DOWNARROW;\nint key_menu_left      = KEY_LEFTARROW;\nint key_menu_right     = KEY_RIGHTARROW;\nint key_menu_back      = KEY_BACKSPACE;\nint key_menu_forward   = KEY_ENTER;\nint key_menu_confirm   = 'y';\nint key_menu_abort     = 'n';\n\nint key_menu_help      = KEY_F1;\nint key_menu_save      = KEY_F2;\nint key_menu_load      = KEY_F3;\nint key_menu_volume    = KEY_F4;\nint key_menu_detail    = KEY_F5;\nint key_menu_qsave     = KEY_F6;\nint key_menu_endgame   = KEY_F7;\nint key_menu_messages  = KEY_F8;\nint key_menu_qload     = KEY_F9;\nint key_menu_quit      = KEY_F10;\nint key_menu_gamma     = KEY_F11;\n\nint key_menu_incscreen = KEY_EQUALS;\nint key_menu_decscreen = KEY_MINUS;\nint key_menu_screenshot = 0;\n\n//\n// Joystick controls\n//\n\nint joybfire = 0;\nint joybstrafe = 1;\nint joybuse = 3;\nint joybspeed = 2;\n\nint joybstrafeleft = -1;\nint joybstraferight = -1;\n\nint joybjump = -1;\n\nint joybprevweapon = -1;\nint joybnextweapon = -1;\n\nint joybmenu = -1;\n\n// Control whether if a mouse button is double clicked, it acts like \n// \"use\" has been pressed\n\nint dclick_use = 1;\n \n// \n// Bind all of the common controls used by Doom and all other games.\n//\n\nvoid M_BindBaseControls(void)\n{\n    M_BindVariable(\"key_right\",          &key_right);\n    M_BindVariable(\"key_left\",           &key_left);\n    M_BindVariable(\"key_up\",             &key_up);\n    M_BindVariable(\"key_down\",           &key_down);\n    M_BindVariable(\"key_strafeleft\",     &key_strafeleft);\n    M_BindVariable(\"key_straferight\",    &key_straferight);\n    M_BindVariable(\"key_fire\",           &key_fire);\n    M_BindVariable(\"key_use\",            &key_use);\n    M_BindVariable(\"key_strafe\",         &key_strafe);\n    M_BindVariable(\"key_speed\",          &key_speed);\n\n    M_BindVariable(\"mouseb_fire\",        &mousebfire);\n    M_BindVariable(\"mouseb_strafe\",      &mousebstrafe);\n    M_BindVariable(\"mouseb_forward\",     &mousebforward);\n\n    M_BindVariable(\"joyb_fire\",          &joybfire);\n    M_BindVariable(\"joyb_strafe\",        &joybstrafe);\n    M_BindVariable(\"joyb_use\",           &joybuse);\n    M_BindVariable(\"joyb_speed\",         &joybspeed);\n\n    M_BindVariable(\"joyb_menu_activate\", &joybmenu);\n\n    // Extra controls that are not in the Vanilla versions:\n\n    M_BindVariable(\"joyb_strafeleft\",    &joybstrafeleft);\n    M_BindVariable(\"joyb_straferight\",   &joybstraferight);\n    M_BindVariable(\"mouseb_strafeleft\",  &mousebstrafeleft);\n    M_BindVariable(\"mouseb_straferight\", &mousebstraferight);\n    M_BindVariable(\"mouseb_use\",         &mousebuse);\n    M_BindVariable(\"mouseb_backward\",    &mousebbackward);\n    M_BindVariable(\"dclick_use\",         &dclick_use);\n    M_BindVariable(\"key_pause\",          &key_pause);\n    M_BindVariable(\"key_message_refresh\", &key_message_refresh);\n}\n\nvoid M_BindHereticControls(void)\n{\n    M_BindVariable(\"key_flyup\",          &key_flyup);\n    M_BindVariable(\"key_flydown\",        &key_flydown);\n    M_BindVariable(\"key_flycenter\",      &key_flycenter);\n\n    M_BindVariable(\"key_lookup\",         &key_lookup);\n    M_BindVariable(\"key_lookdown\",       &key_lookdown);\n    M_BindVariable(\"key_lookcenter\",     &key_lookcenter);\n\n    M_BindVariable(\"key_invleft\",        &key_invleft);\n    M_BindVariable(\"key_invright\",       &key_invright);\n    M_BindVariable(\"key_useartifact\",    &key_useartifact);\n}\n\nvoid M_BindHexenControls(void)\n{\n    M_BindVariable(\"key_jump\",           &key_jump);\n    M_BindVariable(\"mouseb_jump\",        &mousebjump);\n    M_BindVariable(\"joyb_jump\",          &joybjump);\n\n    M_BindVariable(\"key_arti_all\",             &key_arti_all);\n    M_BindVariable(\"key_arti_health\",          &key_arti_health);\n    M_BindVariable(\"key_arti_poisonbag\",       &key_arti_poisonbag);\n    M_BindVariable(\"key_arti_blastradius\",     &key_arti_blastradius);\n    M_BindVariable(\"key_arti_teleport\",        &key_arti_teleport);\n    M_BindVariable(\"key_arti_teleportother\",   &key_arti_teleportother);\n    M_BindVariable(\"key_arti_egg\",             &key_arti_egg);\n    M_BindVariable(\"key_arti_invulnerability\", &key_arti_invulnerability);\n}\n\nvoid M_BindStrifeControls(void)\n{\n    // These are shared with all games, but have different defaults:\n    key_message_refresh = '/';\n\n    // These keys are shared with Heretic/Hexen but have different defaults:\n    key_jump     = 'a';\n    key_lookup   = KEY_PGUP;\n    key_lookdown = KEY_PGDN;\n    key_invleft  = KEY_INS;\n    key_invright = KEY_DEL;\n\n    M_BindVariable(\"key_jump\",           &key_jump);\n    M_BindVariable(\"key_lookUp\",         &key_lookup);\n    M_BindVariable(\"key_lookDown\",       &key_lookdown);\n    M_BindVariable(\"key_invLeft\",        &key_invleft);\n    M_BindVariable(\"key_invRight\",       &key_invright);\n\n    // Custom Strife-only Keys:\n    M_BindVariable(\"key_useHealth\",      &key_usehealth);\n    M_BindVariable(\"key_invquery\",       &key_invquery);\n    M_BindVariable(\"key_mission\",        &key_mission);\n    M_BindVariable(\"key_invPop\",         &key_invpop);\n    M_BindVariable(\"key_invKey\",         &key_invkey);\n    M_BindVariable(\"key_invHome\",        &key_invhome);\n    M_BindVariable(\"key_invEnd\",         &key_invend);\n    M_BindVariable(\"key_invUse\",         &key_invuse);\n    M_BindVariable(\"key_invDrop\",        &key_invdrop);\n\n    // Strife also supports jump on mouse and joystick, and in the exact same\n    // manner as Hexen!\n    M_BindVariable(\"mouseb_jump\",        &mousebjump);\n    M_BindVariable(\"joyb_jump\",          &joybjump);\n}\n\nvoid M_BindWeaponControls(void)\n{\n    M_BindVariable(\"key_weapon1\",        &key_weapon1);\n    M_BindVariable(\"key_weapon2\",        &key_weapon2);\n    M_BindVariable(\"key_weapon3\",        &key_weapon3);\n    M_BindVariable(\"key_weapon4\",        &key_weapon4);\n    M_BindVariable(\"key_weapon5\",        &key_weapon5);\n    M_BindVariable(\"key_weapon6\",        &key_weapon6);\n    M_BindVariable(\"key_weapon7\",        &key_weapon7);\n    M_BindVariable(\"key_weapon8\",        &key_weapon8);\n\n    M_BindVariable(\"key_prevweapon\",     &key_prevweapon);\n    M_BindVariable(\"key_nextweapon\",     &key_nextweapon);\n\n    M_BindVariable(\"joyb_prevweapon\",    &joybprevweapon);\n    M_BindVariable(\"joyb_nextweapon\",    &joybnextweapon);\n\n    M_BindVariable(\"mouseb_prevweapon\",  &mousebprevweapon);\n    M_BindVariable(\"mouseb_nextweapon\",  &mousebnextweapon);\n}\n\nvoid M_BindMapControls(void)\n{\n    M_BindVariable(\"key_map_north\",      &key_map_north);\n    M_BindVariable(\"key_map_south\",      &key_map_south);\n    M_BindVariable(\"key_map_east\",       &key_map_east);\n    M_BindVariable(\"key_map_west\",       &key_map_west);\n    M_BindVariable(\"key_map_zoomin\",     &key_map_zoomin);\n    M_BindVariable(\"key_map_zoomout\",    &key_map_zoomout);\n    M_BindVariable(\"key_map_toggle\",     &key_map_toggle);\n    M_BindVariable(\"key_map_maxzoom\",    &key_map_maxzoom);\n    M_BindVariable(\"key_map_follow\",     &key_map_follow);\n    M_BindVariable(\"key_map_grid\",       &key_map_grid);\n    M_BindVariable(\"key_map_mark\",       &key_map_mark);\n    M_BindVariable(\"key_map_clearmark\",  &key_map_clearmark);\n}\n\nvoid M_BindMenuControls(void)\n{\n    M_BindVariable(\"key_menu_activate\",  &key_menu_activate);\n    M_BindVariable(\"key_menu_up\",        &key_menu_up);\n    M_BindVariable(\"key_menu_down\",      &key_menu_down);\n    M_BindVariable(\"key_menu_left\",      &key_menu_left);\n    M_BindVariable(\"key_menu_right\",     &key_menu_right);\n    M_BindVariable(\"key_menu_back\",      &key_menu_back);\n    M_BindVariable(\"key_menu_forward\",   &key_menu_forward);\n    M_BindVariable(\"key_menu_confirm\",   &key_menu_confirm);\n    M_BindVariable(\"key_menu_abort\",     &key_menu_abort);\n\n    M_BindVariable(\"key_menu_help\",      &key_menu_help);\n    M_BindVariable(\"key_menu_save\",      &key_menu_save);\n    M_BindVariable(\"key_menu_load\",      &key_menu_load);\n    M_BindVariable(\"key_menu_volume\",    &key_menu_volume);\n    M_BindVariable(\"key_menu_detail\",    &key_menu_detail);\n    M_BindVariable(\"key_menu_qsave\",     &key_menu_qsave);\n    M_BindVariable(\"key_menu_endgame\",   &key_menu_endgame);\n    M_BindVariable(\"key_menu_messages\",  &key_menu_messages);\n    M_BindVariable(\"key_menu_qload\",     &key_menu_qload);\n    M_BindVariable(\"key_menu_quit\",      &key_menu_quit);\n    M_BindVariable(\"key_menu_gamma\",     &key_menu_gamma);\n\n    M_BindVariable(\"key_menu_incscreen\", &key_menu_incscreen);\n    M_BindVariable(\"key_menu_decscreen\", &key_menu_decscreen);\n    M_BindVariable(\"key_menu_screenshot\",&key_menu_screenshot);\n    M_BindVariable(\"key_demo_quit\",      &key_demo_quit);\n    M_BindVariable(\"key_spy\",            &key_spy);\n}\n\nvoid M_BindChatControls(unsigned int num_players)\n{\n    char name[32];  // haleyjd: 20 not large enough - Thank you, come again!\n    unsigned int i; // haleyjd: signedness conflict\n\n    M_BindVariable(\"key_multi_msg\",     &key_multi_msg);\n\n    for (i=0; i<num_players; ++i)\n    {\n        M_snprintf(name, sizeof(name), \"key_multi_msgplayer%i\", i + 1);\n        M_BindVariable(name, &key_multi_msgplayer[i]);\n    }\n}\n\n//\n// Apply custom patches to the default values depending on the\n// platform we are running on.\n//\n\nvoid M_ApplyPlatformDefaults(void)\n{\n    // no-op. Add your platform-specific patches here.\n}\n\n"
  },
  {
    "path": "fbdoom/m_controls.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 1993-2008 Raven Software\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n\n#ifndef __M_CONTROLS_H__\n#define __M_CONTROLS_H__\n \nextern int key_right;\nextern int key_left;\n\nextern int key_up;\nextern int key_down;\nextern int key_strafeleft;\nextern int key_straferight;\nextern int key_fire;\nextern int key_use;\nextern int key_strafe;\nextern int key_speed;\n\nextern int key_jump;\n \nextern int key_flyup;\nextern int key_flydown;\nextern int key_flycenter;\nextern int key_lookup;\nextern int key_lookdown;\nextern int key_lookcenter;\nextern int key_invleft;\nextern int key_invright;\nextern int key_useartifact;\n\n// villsa [STRIFE] strife keys\nextern int key_usehealth;\nextern int key_invquery;\nextern int key_mission;\nextern int key_invpop;\nextern int key_invkey;\nextern int key_invhome;\nextern int key_invend;\nextern int key_invuse;\nextern int key_invdrop;\n\nextern int key_message_refresh;\nextern int key_pause;\n\nextern int key_multi_msg;\nextern int key_multi_msgplayer[8];\n\nextern int key_weapon1;\nextern int key_weapon2;\nextern int key_weapon3;\nextern int key_weapon4;\nextern int key_weapon5;\nextern int key_weapon6;\nextern int key_weapon7;\nextern int key_weapon8;\n\nextern int key_arti_all;\nextern int key_arti_health;\nextern int key_arti_poisonbag;\nextern int key_arti_blastradius;\nextern int key_arti_teleport;\nextern int key_arti_teleportother;\nextern int key_arti_egg;\nextern int key_arti_invulnerability;\n\nextern int key_demo_quit;\nextern int key_spy;\nextern int key_prevweapon;\nextern int key_nextweapon;\n\nextern int key_map_north;\nextern int key_map_south;\nextern int key_map_east;\nextern int key_map_west;\nextern int key_map_zoomin;\nextern int key_map_zoomout;\nextern int key_map_toggle;\nextern int key_map_maxzoom;\nextern int key_map_follow;\nextern int key_map_grid;\nextern int key_map_mark;\nextern int key_map_clearmark;\n\n// menu keys:\n\nextern int key_menu_activate;\nextern int key_menu_up;\nextern int key_menu_down;\nextern int key_menu_left;\nextern int key_menu_right;\nextern int key_menu_back;\nextern int key_menu_forward;\nextern int key_menu_confirm;\nextern int key_menu_abort;\n\nextern int key_menu_help;\nextern int key_menu_save;\nextern int key_menu_load;\nextern int key_menu_volume;\nextern int key_menu_detail;\nextern int key_menu_qsave;\nextern int key_menu_endgame;\nextern int key_menu_messages;\nextern int key_menu_qload;\nextern int key_menu_quit;\nextern int key_menu_gamma;\n\nextern int key_menu_incscreen;\nextern int key_menu_decscreen;\nextern int key_menu_screenshot;\n\nextern int mousebfire;\nextern int mousebstrafe;\nextern int mousebforward;\n\nextern int mousebjump;\n\nextern int mousebstrafeleft;\nextern int mousebstraferight;\nextern int mousebbackward;\nextern int mousebuse;\n\nextern int mousebprevweapon;\nextern int mousebnextweapon;\n\nextern int joybfire;\nextern int joybstrafe;\nextern int joybuse;\nextern int joybspeed;\n\nextern int joybjump;\n\nextern int joybstrafeleft;\nextern int joybstraferight;\n\nextern int joybprevweapon;\nextern int joybnextweapon;\n\nextern int joybmenu;\n\nextern int dclick_use;\n\nvoid M_BindBaseControls(void);\nvoid M_BindHereticControls(void);\nvoid M_BindHexenControls(void);\nvoid M_BindStrifeControls(void);\nvoid M_BindWeaponControls(void);\nvoid M_BindMapControls(void);\nvoid M_BindMenuControls(void);\nvoid M_BindChatControls(unsigned int num_players);\n\nvoid M_ApplyPlatformDefaults(void);\n\n#endif /* #ifndef __M_CONTROLS_H__ */\n\n"
  },
  {
    "path": "fbdoom/m_fixed.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tFixed point implementation.\n//\n\n\n\n#include \"stdlib.h\"\n\n#include \"doomtype.h\"\n#include \"i_system.h\"\n\n#include \"m_fixed.h\"\n\n\n\n\n// Fixme. __USE_C_FIXED__ or something.\n\nfixed_t\nFixedMul\n( fixed_t\ta,\n  fixed_t\tb )\n{\n    return ((int64_t) a * (int64_t) b) >> FRACBITS;\n}\n\n\n\n//\n// FixedDiv, C version.\n//\n\nfixed_t FixedDiv(fixed_t a, fixed_t b)\n{\n    if ((abs(a) >> 14) >= abs(b))\n    {\n\treturn (a^b) < 0 ? INT_MIN : INT_MAX;\n    }\n    else\n    {\n\tint64_t result;\n\n\tresult = ((int64_t) a << 16) / b;\n\n\treturn (fixed_t) result;\n    }\n}\n\n"
  },
  {
    "path": "fbdoom/m_fixed.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tFixed point arithemtics, implementation.\n//\n\n\n#ifndef __M_FIXED__\n#define __M_FIXED__\n\n\n\n\n//\n// Fixed point, 32bit as 16.16.\n//\n#define FRACBITS\t\t16\n#define FRACUNIT\t\t(1<<FRACBITS)\n\ntypedef int fixed_t;\n\nfixed_t FixedMul\t(fixed_t a, fixed_t b);\nfixed_t FixedDiv\t(fixed_t a, fixed_t b);\n\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/m_menu.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tDOOM selection menu, options, episode etc.\n//\tSliders and icons. Kinda widget stuff.\n//\n\n\n#include <stdlib.h>\n#include <ctype.h>\n\n\n#include \"doomdef.h\"\n#include \"doomkeys.h\"\n#include \"dstrings.h\"\n\n#include \"d_main.h\"\n#include \"deh_main.h\"\n\n#include \"i_input_tty.h\"\n#include \"i_swap.h\"\n#include \"i_system.h\"\n#include \"i_timer.h\"\n#include \"i_video.h\"\n#include \"m_misc.h\"\n#include \"v_video.h\"\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\n#include \"r_local.h\"\n\n\n#include \"hu_stuff.h\"\n\n#include \"g_game.h\"\n\n#include \"m_argv.h\"\n#include \"m_controls.h\"\n#include \"p_saveg.h\"\n\n#include \"s_sound.h\"\n\n#include \"doomstat.h\"\n\n// Data.\n#include \"sounds.h\"\n\n#include \"m_menu.h\"\n\n\nextern patch_t*\t\thu_font[HU_FONTSIZE];\nextern boolean\t\tmessage_dontfuckwithme;\n\nextern boolean\t\tchat_on;\t\t// in heads-up code\n\n//\n// defaulted values\n//\nint\t\t\tmouseSensitivity = 5;\n\n// Show messages has default, 0 = off, 1 = on\nint\t\t\tshowMessages = 1;\n\t\n\n// Blocky mode, has default, 0 = high, 1 = normal\nint\t\t\tdetailLevel = 0;\nint\t\t\tscreenblocks = 9;\n\n// temp for screenblocks (0-9)\nint\t\t\tscreenSize;\n\n// -1 = no quicksave slot picked!\nint\t\t\tquickSaveSlot;\n\n // 1 = message to be printed\nint\t\t\tmessageToPrint;\n// ...and here is the message string!\nchar*\t\t\tmessageString;\n\n// message x & y\nint\t\t\tmessx;\nint\t\t\tmessy;\nint\t\t\tmessageLastMenuActive;\n\n// timed message = no input from user\nboolean\t\t\tmessageNeedsInput;\n\nvoid    (*messageRoutine)(int response);\n\nchar gammamsg[5][26] =\n{\n    GAMMALVL0,\n    GAMMALVL1,\n    GAMMALVL2,\n    GAMMALVL3,\n    GAMMALVL4\n};\n\n// we are going to be entering a savegame string\nint\t\t\tsaveStringEnter;              \nint             \tsaveSlot;\t// which slot to save in\nint\t\t\tsaveCharIndex;\t// which char we're editing\n// old save description before edit\nchar\t\t\tsaveOldString[SAVESTRINGSIZE];  \n\nboolean\t\t\tinhelpscreens;\nboolean\t\t\tmenuactive;\n\n#define SKULLXOFF\t\t-32\n#define LINEHEIGHT\t\t16\n\nextern boolean\t\tsendpause;\nchar\t\t\tsavegamestrings[10][SAVESTRINGSIZE];\n\nchar\tendstring[160];\n\n//static boolean opldev;\n\n//\n// MENU TYPEDEFS\n//\ntypedef struct\n{\n    // 0 = no cursor here, 1 = ok, 2 = arrows ok\n    short\tstatus;\n    \n    char\tname[10];\n    \n    // choice = menu item #.\n    // if status = 2,\n    //   choice=0:leftarrow,1:rightarrow\n    void\t(*routine)(int choice);\n    \n    // hotkey in menu\n    char\talphaKey;\t\t\t\n} menuitem_t;\n\n\n\ntypedef struct menu_s\n{\n    short\t\tnumitems;\t// # of menu items\n    struct menu_s*\tprevMenu;\t// previous menu\n    menuitem_t*\t\tmenuitems;\t// menu items\n    void\t\t(*routine)();\t// draw routine\n    short\t\tx;\n    short\t\ty;\t\t// x,y of menu\n    short\t\tlastOn;\t\t// last item user was on in menu\n} menu_t;\n\nshort\t\titemOn;\t\t\t// menu item skull is on\nshort\t\tskullAnimCounter;\t// skull animation counter\nshort\t\twhichSkull;\t\t// which skull to draw\n\n// graphic name of skulls\n// warning: initializer-string for array of chars is too long\nchar    *skullName[2] = {\"M_SKULL1\",\"M_SKULL2\"};\n\n// current menudef\nmenu_t*\tcurrentMenu;                          \n\n//\n// PROTOTYPES\n//\nvoid M_NewGame(int choice);\nvoid M_Episode(int choice);\nvoid M_ChooseSkill(int choice);\nvoid M_LoadGame(int choice);\nvoid M_SaveGame(int choice);\nvoid M_Options(int choice);\nvoid M_EndGame(int choice);\nvoid M_ReadThis(int choice);\nvoid M_ReadThis2(int choice);\nvoid M_QuitDOOM(int choice);\n\nvoid M_ChangeMessages(int choice);\nvoid M_ChangeSensitivity(int choice);\nvoid M_SfxVol(int choice);\nvoid M_MusicVol(int choice);\nvoid M_ChangeDetail(int choice);\nvoid M_SizeDisplay(int choice);\nvoid M_StartGame(int choice);\nvoid M_Sound(int choice);\n\nvoid M_FinishReadThis(int choice);\nvoid M_LoadSelect(int choice);\nvoid M_SaveSelect(int choice);\nvoid M_ReadSaveStrings(void);\nvoid M_QuickSave(void);\nvoid M_QuickLoad(void);\n\nvoid M_DrawMainMenu(void);\nvoid M_DrawReadThis1(void);\nvoid M_DrawReadThis2(void);\nvoid M_DrawNewGame(void);\nvoid M_DrawEpisode(void);\nvoid M_DrawOptions(void);\nvoid M_DrawSound(void);\nvoid M_DrawLoad(void);\nvoid M_DrawSave(void);\n\nvoid M_DrawSaveLoadBorder(int x,int y);\nvoid M_SetupNextMenu(menu_t *menudef);\nvoid M_DrawThermo(int x,int y,int thermWidth,int thermDot);\nvoid M_DrawEmptyCell(menu_t *menu,int item);\nvoid M_DrawSelCell(menu_t *menu,int item);\nvoid M_WriteText(int x, int y, char *string);\nint  M_StringWidth(char *string);\nint  M_StringHeight(char *string);\nvoid M_StartMessage(char *string,void *routine,boolean input);\nvoid M_StopMessage(void);\nvoid M_ClearMenus (void);\n\n\n\n\n//\n// DOOM MENU\n//\nenum\n{\n    newgame = 0,\n    options,\n    loadgame,\n    savegame,\n    readthis,\n    quitdoom,\n    main_end\n} main_e;\n\nmenuitem_t MainMenu[]=\n{\n    {1,\"M_NGAME\",M_NewGame,'n'},\n    {1,\"M_OPTION\",M_Options,'o'},\n    {1,\"M_LOADG\",M_LoadGame,'l'},\n    {1,\"M_SAVEG\",M_SaveGame,'s'},\n    // Another hickup with Special edition.\n    {1,\"M_RDTHIS\",M_ReadThis,'r'},\n    {1,\"M_QUITG\",M_QuitDOOM,'q'}\n};\n\nmenu_t  MainDef =\n{\n    main_end,\n    NULL,\n    MainMenu,\n    M_DrawMainMenu,\n    97,64,\n    0\n};\n\n\n//\n// EPISODE SELECT\n//\nenum\n{\n    ep1,\n    ep2,\n    ep3,\n    ep4,\n    ep_end\n} episodes_e;\n\nmenuitem_t EpisodeMenu[]=\n{\n    {1,\"M_EPI1\", M_Episode,'k'},\n    {1,\"M_EPI2\", M_Episode,'t'},\n    {1,\"M_EPI3\", M_Episode,'i'},\n    {1,\"M_EPI4\", M_Episode,'t'}\n};\n\nmenu_t  EpiDef =\n{\n    ep_end,\t\t// # of menu items\n    &MainDef,\t\t// previous menu\n    EpisodeMenu,\t// menuitem_t ->\n    M_DrawEpisode,\t// drawing routine ->\n    48,63,              // x,y\n    ep1\t\t\t// lastOn\n};\n\n//\n// NEW GAME\n//\nenum\n{\n    killthings,\n    toorough,\n    hurtme,\n    violence,\n    nightmare,\n    newg_end\n} newgame_e;\n\nmenuitem_t NewGameMenu[]=\n{\n    {1,\"M_JKILL\",\tM_ChooseSkill, 'i'},\n    {1,\"M_ROUGH\",\tM_ChooseSkill, 'h'},\n    {1,\"M_HURT\",\tM_ChooseSkill, 'h'},\n    {1,\"M_ULTRA\",\tM_ChooseSkill, 'u'},\n    {1,\"M_NMARE\",\tM_ChooseSkill, 'n'}\n};\n\nmenu_t  NewDef =\n{\n    newg_end,\t\t// # of menu items\n    &EpiDef,\t\t// previous menu\n    NewGameMenu,\t// menuitem_t ->\n    M_DrawNewGame,\t// drawing routine ->\n    48,63,              // x,y\n    hurtme\t\t// lastOn\n};\n\n\n\n//\n// OPTIONS MENU\n//\nenum\n{\n    endgame,\n    messages,\n    detail,\n    scrnsize,\n    option_empty1,\n    mousesens,\n    option_empty2,\n    soundvol,\n    opt_end\n} options_e;\n\nmenuitem_t OptionsMenu[]=\n{\n    {1,\"M_ENDGAM\",\tM_EndGame,'e'},\n    {1,\"M_MESSG\",\tM_ChangeMessages,'m'},\n    {1,\"M_DETAIL\",\tM_ChangeDetail,'g'},\n    {2,\"M_SCRNSZ\",\tM_SizeDisplay,'s'},\n    {-1,\"\",0,'\\0'},\n    {2,\"M_MSENS\",\tM_ChangeSensitivity,'m'},\n    {-1,\"\",0,'\\0'},\n    {1,\"M_SVOL\",\tM_Sound,'s'}\n};\n\nmenu_t  OptionsDef =\n{\n    opt_end,\n    &MainDef,\n    OptionsMenu,\n    M_DrawOptions,\n    60,37,\n    0\n};\n\n//\n// Read This! MENU 1 & 2\n//\nenum\n{\n    rdthsempty1,\n    read1_end\n} read_e;\n\nmenuitem_t ReadMenu1[] =\n{\n    {1,\"\",M_ReadThis2,0}\n};\n\nmenu_t  ReadDef1 =\n{\n    read1_end,\n    &MainDef,\n    ReadMenu1,\n    M_DrawReadThis1,\n    280,185,\n    0\n};\n\nenum\n{\n    rdthsempty2,\n    read2_end\n} read_e2;\n\nmenuitem_t ReadMenu2[]=\n{\n    {1,\"\",M_FinishReadThis,0}\n};\n\nmenu_t  ReadDef2 =\n{\n    read2_end,\n    &ReadDef1,\n    ReadMenu2,\n    M_DrawReadThis2,\n    330,175,\n    0\n};\n\n//\n// SOUND VOLUME MENU\n//\nenum\n{\n    sfx_vol,\n    sfx_empty1,\n    music_vol,\n    sfx_empty2,\n    sound_end\n} sound_e;\n\nmenuitem_t SoundMenu[]=\n{\n    {2,\"M_SFXVOL\",M_SfxVol,'s'},\n    {-1,\"\",0,'\\0'},\n    {2,\"M_MUSVOL\",M_MusicVol,'m'},\n    {-1,\"\",0,'\\0'}\n};\n\nmenu_t  SoundDef =\n{\n    sound_end,\n    &OptionsDef,\n    SoundMenu,\n    M_DrawSound,\n    80,64,\n    0\n};\n\n//\n// LOAD GAME MENU\n//\nenum\n{\n    load1,\n    load2,\n    load3,\n    load4,\n    load5,\n    load6,\n    load_end\n} load_e;\n\nmenuitem_t LoadMenu[]=\n{\n    {1,\"\", M_LoadSelect,'1'},\n    {1,\"\", M_LoadSelect,'2'},\n    {1,\"\", M_LoadSelect,'3'},\n    {1,\"\", M_LoadSelect,'4'},\n    {1,\"\", M_LoadSelect,'5'},\n    {1,\"\", M_LoadSelect,'6'}\n};\n\nmenu_t  LoadDef =\n{\n    load_end,\n    &MainDef,\n    LoadMenu,\n    M_DrawLoad,\n    80,54,\n    0\n};\n\n//\n// SAVE GAME MENU\n//\nmenuitem_t SaveMenu[]=\n{\n    {1,\"\", M_SaveSelect,'1'},\n    {1,\"\", M_SaveSelect,'2'},\n    {1,\"\", M_SaveSelect,'3'},\n    {1,\"\", M_SaveSelect,'4'},\n    {1,\"\", M_SaveSelect,'5'},\n    {1,\"\", M_SaveSelect,'6'}\n};\n\nmenu_t  SaveDef =\n{\n    load_end,\n    &MainDef,\n    SaveMenu,\n    M_DrawSave,\n    80,54,\n    0\n};\n\n\n//\n// M_ReadSaveStrings\n//  read the strings from the savegame files\n//\nvoid M_ReadSaveStrings(void)\n{\n    FILE   *handle;\n    int     i;\n    char    name[256];\n\n    for (i = 0;i < load_end;i++)\n    {\n        M_StringCopy(name, P_SaveGameFile(i), sizeof(name));\n\n\thandle = fopen(name, \"rb\");\n        if (handle == NULL)\n        {\n            M_StringCopy(savegamestrings[i], EMPTYSTRING, SAVESTRINGSIZE);\n            LoadMenu[i].status = 0;\n            continue;\n        }\n\tfread(&savegamestrings[i], 1, SAVESTRINGSIZE, handle);\n\tfclose(handle);\n\tLoadMenu[i].status = 1;\n    }\n}\n\n\n//\n// M_LoadGame & Cie.\n//\nvoid M_DrawLoad(void)\n{\n    int             i;\n\t\n    V_DrawPatchDirect(72, 28, \n                      W_CacheLumpName(DEH_String(\"M_LOADG\"), PU_CACHE));\n\n    for (i = 0;i < load_end; i++)\n    {\n\tM_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);\n\tM_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);\n    }\n}\n\n\n\n//\n// Draw border for the savegame description\n//\nvoid M_DrawSaveLoadBorder(int x,int y)\n{\n    int             i;\n\t\n    V_DrawPatchDirect(x - 8, y + 7,\n                      W_CacheLumpName(DEH_String(\"M_LSLEFT\"), PU_CACHE));\n\t\n    for (i = 0;i < 24;i++)\n    {\n\tV_DrawPatchDirect(x, y + 7,\n                          W_CacheLumpName(DEH_String(\"M_LSCNTR\"), PU_CACHE));\n\tx += 8;\n    }\n\n    V_DrawPatchDirect(x, y + 7, \n                      W_CacheLumpName(DEH_String(\"M_LSRGHT\"), PU_CACHE));\n}\n\n\n\n//\n// User wants to load this game\n//\nvoid M_LoadSelect(int choice)\n{\n    char    name[256];\n\t\n    M_StringCopy(name, P_SaveGameFile(choice), sizeof(name));\n\n    G_LoadGame (name);\n    M_ClearMenus ();\n}\n\n//\n// Selected from DOOM menu\n//\nvoid M_LoadGame (int choice)\n{\n    if (netgame)\n    {\n\tM_StartMessage(DEH_String(LOADNET),NULL,false);\n\treturn;\n    }\n\t\n    M_SetupNextMenu(&LoadDef);\n    M_ReadSaveStrings();\n}\n\n\n//\n//  M_SaveGame & Cie.\n//\nvoid M_DrawSave(void)\n{\n    int             i;\n\t\n    V_DrawPatchDirect(72, 28, W_CacheLumpName(DEH_String(\"M_SAVEG\"), PU_CACHE));\n    for (i = 0;i < load_end; i++)\n    {\n\tM_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);\n\tM_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);\n    }\n\t\n    if (saveStringEnter)\n    {\n\ti = M_StringWidth(savegamestrings[saveSlot]);\n\tM_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*saveSlot,\"_\");\n    }\n}\n\n//\n// M_Responder calls this when user is finished\n//\nvoid M_DoSave(int slot)\n{\n    G_SaveGame (slot,savegamestrings[slot]);\n    M_ClearMenus ();\n\n    // PICK QUICKSAVE SLOT YET?\n    if (quickSaveSlot == -2)\n\tquickSaveSlot = slot;\n}\n\n//\n// User wants to save. Start string input for M_Responder\n//\nvoid M_SaveSelect(int choice)\n{\n    // we are going to be intercepting all chars\n    saveStringEnter = 1;\n    \n    saveSlot = choice;\n    M_StringCopy(saveOldString,savegamestrings[choice], SAVESTRINGSIZE);\n    if (!strcmp(savegamestrings[choice], EMPTYSTRING))\n\tsavegamestrings[choice][0] = 0;\n    saveCharIndex = strlen(savegamestrings[choice]);\n}\n\n//\n// Selected from DOOM menu\n//\nvoid M_SaveGame (int choice)\n{\n    if (!usergame)\n    {\n\tM_StartMessage(DEH_String(SAVEDEAD),NULL,false);\n\treturn;\n    }\n\t\n    if (gamestate != GS_LEVEL)\n\treturn;\n\t\n    M_SetupNextMenu(&SaveDef);\n    M_ReadSaveStrings();\n}\n\n\n\n//\n//      M_QuickSave\n//\nchar    tempstring[80];\n\nvoid M_QuickSaveResponse(int key)\n{\n    if (key == key_menu_confirm)\n    {\n\tM_DoSave(quickSaveSlot);\n\tS_StartSound(NULL,sfx_swtchx);\n    }\n}\n\nvoid M_QuickSave(void)\n{\n    if (!usergame)\n    {\n\tS_StartSound(NULL,sfx_oof);\n\treturn;\n    }\n\n    if (gamestate != GS_LEVEL)\n\treturn;\n\t\n    if (quickSaveSlot < 0)\n    {\n\tM_StartControlPanel();\n\tM_ReadSaveStrings();\n\tM_SetupNextMenu(&SaveDef);\n\tquickSaveSlot = -2;\t// means to pick a slot now\n\treturn;\n    }\n    DEH_snprintf(tempstring, 80, QSPROMPT, savegamestrings[quickSaveSlot]);\n    M_StartMessage(tempstring,M_QuickSaveResponse,true);\n}\n\n\n\n//\n// M_QuickLoad\n//\nvoid M_QuickLoadResponse(int key)\n{\n    if (key == key_menu_confirm)\n    {\n\tM_LoadSelect(quickSaveSlot);\n\tS_StartSound(NULL,sfx_swtchx);\n    }\n}\n\n\nvoid M_QuickLoad(void)\n{\n    if (netgame)\n    {\n\tM_StartMessage(DEH_String(QLOADNET),NULL,false);\n\treturn;\n    }\n\t\n    if (quickSaveSlot < 0)\n    {\n\tM_StartMessage(DEH_String(QSAVESPOT),NULL,false);\n\treturn;\n    }\n    DEH_snprintf(tempstring, 80, QLPROMPT, savegamestrings[quickSaveSlot]);\n    M_StartMessage(tempstring,M_QuickLoadResponse,true);\n}\n\n\n\n\n//\n// Read This Menus\n// Had a \"quick hack to fix romero bug\"\n//\nvoid M_DrawReadThis1(void)\n{\n    char *lumpname = \"CREDIT\";\n    int skullx = 330, skully = 175;\n\n    inhelpscreens = true;\n    \n    // Different versions of Doom 1.9 work differently\n\n    switch (gameversion)\n    {\n        case exe_doom_1_666:\n        case exe_doom_1_7:\n        case exe_doom_1_8:\n        case exe_doom_1_9:\n        case exe_hacx:\n\n            if (gamemode == commercial)\n            {\n                // Doom 2\n\n                lumpname = \"HELP\";\n\n                skullx = 330;\n                skully = 165;\n            }\n            else\n            {\n                // Doom 1\n                // HELP2 is the first screen shown in Doom 1\n                \n                lumpname = \"HELP2\";\n\n                skullx = 280;\n                skully = 185;\n            }\n            break;\n\n        case exe_ultimate:\n        case exe_chex:\n\n            // Ultimate Doom always displays \"HELP1\".\n\n            // Chex Quest version also uses \"HELP1\", even though it is based\n            // on Final Doom.\n\n            lumpname = \"HELP1\";\n\n            break;\n\n        case exe_final:\n        case exe_final2:\n\n            // Final Doom always displays \"HELP\".\n\n            lumpname = \"HELP\";\n\n            break;\n\n        default:\n            I_Error(\"Unhandled game version\");\n            break;\n    }\n\n    lumpname = DEH_String(lumpname);\n    \n    V_DrawPatchDirect (0, 0, W_CacheLumpName(lumpname, PU_CACHE));\n\n    ReadDef1.x = skullx;\n    ReadDef1.y = skully;\n}\n\n\n\n//\n// Read This Menus - optional second page.\n//\nvoid M_DrawReadThis2(void)\n{\n    inhelpscreens = true;\n\n    // We only ever draw the second page if this is \n    // gameversion == exe_doom_1_9 and gamemode == registered\n\n    V_DrawPatchDirect(0, 0, W_CacheLumpName(DEH_String(\"HELP1\"), PU_CACHE));\n}\n\n\n//\n// Change Sfx & Music volumes\n//\nvoid M_DrawSound(void)\n{\n    V_DrawPatchDirect (60, 38, W_CacheLumpName(DEH_String(\"M_SVOL\"), PU_CACHE));\n\n    M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1),\n\t\t 16,sfxVolume);\n\n    M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1),\n\t\t 16,musicVolume);\n}\n\nvoid M_Sound(int choice)\n{\n    M_SetupNextMenu(&SoundDef);\n}\n\nvoid M_SfxVol(int choice)\n{\n    switch(choice)\n    {\n      case 0:\n\tif (sfxVolume)\n\t    sfxVolume--;\n\tbreak;\n      case 1:\n\tif (sfxVolume < 15)\n\t    sfxVolume++;\n\tbreak;\n    }\n\t\n    S_SetSfxVolume(sfxVolume * 8);\n}\n\nvoid M_MusicVol(int choice)\n{\n    switch(choice)\n    {\n      case 0:\n\tif (musicVolume)\n\t    musicVolume--;\n\tbreak;\n      case 1:\n\tif (musicVolume < 15)\n\t    musicVolume++;\n\tbreak;\n    }\n\t\n    S_SetMusicVolume(musicVolume * 8);\n}\n\n\n\n\n//\n// M_DrawMainMenu\n//\nvoid M_DrawMainMenu(void)\n{\n    V_DrawPatchDirect(94, 2,\n                      W_CacheLumpName(DEH_String(\"M_DOOM\"), PU_CACHE));\n}\n\n\n\n\n//\n// M_NewGame\n//\nvoid M_DrawNewGame(void)\n{\n    V_DrawPatchDirect(96, 14, W_CacheLumpName(DEH_String(\"M_NEWG\"), PU_CACHE));\n    V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String(\"M_SKILL\"), PU_CACHE));\n}\n\nvoid M_NewGame(int choice)\n{\n    if (netgame && !demoplayback)\n    {\n\tM_StartMessage(DEH_String(NEWGAME),NULL,false);\n\treturn;\n    }\n\t\n    // Chex Quest disabled the episode select screen, as did Doom II.\n\n    if (gamemode == commercial || gameversion == exe_chex)\n\tM_SetupNextMenu(&NewDef);\n    else\n\tM_SetupNextMenu(&EpiDef);\n}\n\n\n//\n//      M_Episode\n//\nint     epi;\n\nvoid M_DrawEpisode(void)\n{\n    V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String(\"M_EPISOD\"), PU_CACHE));\n}\n\nvoid M_VerifyNightmare(int key)\n{\n    if (key != key_menu_confirm)\n\treturn;\n\t\t\n    G_DeferedInitNew(nightmare,epi+1,1);\n    M_ClearMenus ();\n}\n\nvoid M_ChooseSkill(int choice)\n{\n    if (choice == nightmare)\n    {\n\tM_StartMessage(DEH_String(NIGHTMARE),M_VerifyNightmare,true);\n\treturn;\n    }\n\t\n    G_DeferedInitNew(choice,epi+1,1);\n    M_ClearMenus ();\n}\n\nvoid M_Episode(int choice)\n{\n    if ( (gamemode == shareware)\n\t && choice)\n    {\n\tM_StartMessage(DEH_String(SWSTRING),NULL,false);\n\tM_SetupNextMenu(&ReadDef1);\n\treturn;\n    }\n\n    // Yet another hack...\n    if ( (gamemode == registered)\n\t && (choice > 2))\n    {\n      fprintf( stderr,\n\t       \"M_Episode: 4th episode requires UltimateDOOM\\n\");\n      choice = 0;\n    }\n\t \n    epi = choice;\n    M_SetupNextMenu(&NewDef);\n}\n\n\n\n//\n// M_Options\n//\nstatic char *detailNames[2] = {\"M_GDHIGH\",\"M_GDLOW\"};\nstatic char *msgNames[2] = {\"M_MSGOFF\",\"M_MSGON\"};\n\nvoid M_DrawOptions(void)\n{\n    V_DrawPatchDirect(108, 15, W_CacheLumpName(DEH_String(\"M_OPTTTL\"),\n                                               PU_CACHE));\n\t\n    V_DrawPatchDirect(OptionsDef.x + 175, OptionsDef.y + LINEHEIGHT * detail,\n\t\t      W_CacheLumpName(DEH_String(detailNames[detailLevel]),\n\t\t\t              PU_CACHE));\n\n    V_DrawPatchDirect(OptionsDef.x + 120, OptionsDef.y + LINEHEIGHT * messages,\n                      W_CacheLumpName(DEH_String(msgNames[showMessages]),\n                                      PU_CACHE));\n\n    M_DrawThermo(OptionsDef.x, OptionsDef.y + LINEHEIGHT * (mousesens + 1),\n\t\t 10, mouseSensitivity);\n\n    M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1),\n\t\t 9,screenSize);\n}\n\nvoid M_Options(int choice)\n{\n    M_SetupNextMenu(&OptionsDef);\n}\n\n\n\n//\n//      Toggle messages on/off\n//\nvoid M_ChangeMessages(int choice)\n{\n    // warning: unused parameter `int choice'\n    choice = 0;\n    showMessages = 1 - showMessages;\n\t\n    if (!showMessages)\n\tplayers[consoleplayer].message = DEH_String(MSGOFF);\n    else\n\tplayers[consoleplayer].message = DEH_String(MSGON);\n\n    message_dontfuckwithme = true;\n}\n\n\n//\n// M_EndGame\n//\nvoid M_EndGameResponse(int key)\n{\n    if (key != key_menu_confirm)\n\treturn;\n\t\t\n    currentMenu->lastOn = itemOn;\n    M_ClearMenus ();\n    D_StartTitle ();\n}\n\nvoid M_EndGame(int choice)\n{\n    choice = 0;\n    if (!usergame)\n    {\n\tS_StartSound(NULL,sfx_oof);\n\treturn;\n    }\n\t\n    if (netgame)\n    {\n\tM_StartMessage(DEH_String(NETEND),NULL,false);\n\treturn;\n    }\n\t\n    M_StartMessage(DEH_String(ENDGAME),M_EndGameResponse,true);\n}\n\n\n\n\n//\n// M_ReadThis\n//\nvoid M_ReadThis(int choice)\n{\n    choice = 0;\n    M_SetupNextMenu(&ReadDef1);\n}\n\nvoid M_ReadThis2(int choice)\n{\n    // Doom 1.9 had two menus when playing Doom 1\n    // All others had only one\n\n    if (gameversion <= exe_doom_1_9 && gamemode != commercial)\n    {\n        choice = 0;\n        M_SetupNextMenu(&ReadDef2);\n    }\n    else\n    {\n        // Close the menu\n\n        M_FinishReadThis(0);\n    }\n}\n\nvoid M_FinishReadThis(int choice)\n{\n    choice = 0;\n    M_SetupNextMenu(&MainDef);\n}\n\n\n\n\n//\n// M_QuitDOOM\n//\nint     quitsounds[8] =\n{\n    sfx_pldeth,\n    sfx_dmpain,\n    sfx_popain,\n    sfx_slop,\n    sfx_telept,\n    sfx_posit1,\n    sfx_posit3,\n    sfx_sgtatk\n};\n\nint     quitsounds2[8] =\n{\n    sfx_vilact,\n    sfx_getpow,\n    sfx_boscub,\n    sfx_slop,\n    sfx_skeswg,\n    sfx_kntdth,\n    sfx_bspact,\n    sfx_sgtatk\n};\n\n\n\nvoid M_QuitResponse(int key)\n{\n    if (key != key_menu_confirm)\n\treturn;\n    if (!netgame)\n    {\n\tif (gamemode == commercial)\n\t    S_StartSound(NULL,quitsounds2[(gametic>>2)&7]);\n\telse\n\t    S_StartSound(NULL,quitsounds[(gametic>>2)&7]);\n\tI_WaitVBL(105);\n    }\n    I_Quit ();\n    kbd_shutdown();\n}\n\n\nstatic char *M_SelectEndMessage(void)\n{\n    char **endmsg;\n\n    if (logical_gamemission == doom)\n    {\n        // Doom 1\n\n        endmsg = doom1_endmsg;\n    }\n    else\n    {\n        // Doom 2\n        \n        endmsg = doom2_endmsg;\n    }\n\n    return endmsg[gametic % NUM_QUITMESSAGES];\n}\n\n\nvoid M_QuitDOOM(int choice)\n{\n    DEH_snprintf(endstring, sizeof(endstring), \"%s\\n\\n\" DOSY,\n                 DEH_String(M_SelectEndMessage()));\n\n    M_StartMessage(endstring,M_QuitResponse,true);\n}\n\n\n\n\nvoid M_ChangeSensitivity(int choice)\n{\n    switch(choice)\n    {\n      case 0:\n\tif (mouseSensitivity)\n\t    mouseSensitivity--;\n\tbreak;\n      case 1:\n\tif (mouseSensitivity < 9)\n\t    mouseSensitivity++;\n\tbreak;\n    }\n}\n\n\n\n\nvoid M_ChangeDetail(int choice)\n{\n    choice = 0;\n    detailLevel = 1 - detailLevel;\n\n    R_SetViewSize (screenblocks, detailLevel);\n\n    if (!detailLevel)\n\tplayers[consoleplayer].message = DEH_String(DETAILHI);\n    else\n\tplayers[consoleplayer].message = DEH_String(DETAILLO);\n}\n\n\n\n\nvoid M_SizeDisplay(int choice)\n{\n    switch(choice)\n    {\n      case 0:\n\tif (screenSize > 0)\n\t{\n\t    screenblocks--;\n\t    screenSize--;\n\t}\n\tbreak;\n      case 1:\n\tif (screenSize < 8)\n\t{\n\t    screenblocks++;\n\t    screenSize++;\n\t}\n\tbreak;\n    }\n\t\n\n    R_SetViewSize (screenblocks, detailLevel);\n}\n\n\n\n\n//\n//      Menu Functions\n//\nvoid\nM_DrawThermo\n( int\tx,\n  int\ty,\n  int\tthermWidth,\n  int\tthermDot )\n{\n    int\t\txx;\n    int\t\ti;\n\n    xx = x;\n    V_DrawPatchDirect(xx, y, W_CacheLumpName(DEH_String(\"M_THERML\"), PU_CACHE));\n    xx += 8;\n    for (i=0;i<thermWidth;i++)\n    {\n\tV_DrawPatchDirect(xx, y, W_CacheLumpName(DEH_String(\"M_THERMM\"), PU_CACHE));\n\txx += 8;\n    }\n    V_DrawPatchDirect(xx, y, W_CacheLumpName(DEH_String(\"M_THERMR\"), PU_CACHE));\n\n    V_DrawPatchDirect((x + 8) + thermDot * 8, y,\n\t\t      W_CacheLumpName(DEH_String(\"M_THERMO\"), PU_CACHE));\n}\n\n\n\nvoid\nM_DrawEmptyCell\n( menu_t*\tmenu,\n  int\t\titem )\n{\n    V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1, \n                      W_CacheLumpName(DEH_String(\"M_CELL1\"), PU_CACHE));\n}\n\nvoid\nM_DrawSelCell\n( menu_t*\tmenu,\n  int\t\titem )\n{\n    V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1,\n                      W_CacheLumpName(DEH_String(\"M_CELL2\"), PU_CACHE));\n}\n\n\nvoid\nM_StartMessage\n( char*\t\tstring,\n  void*\t\troutine,\n  boolean\tinput )\n{\n    messageLastMenuActive = menuactive;\n    messageToPrint = 1;\n    messageString = string;\n    messageRoutine = routine;\n    messageNeedsInput = input;\n    menuactive = true;\n    return;\n}\n\n\nvoid M_StopMessage(void)\n{\n    menuactive = messageLastMenuActive;\n    messageToPrint = 0;\n}\n\n\n\n//\n// Find string width from hu_font chars\n//\nint M_StringWidth(char* string)\n{\n    size_t             i;\n    int             w = 0;\n    int             c;\n\t\n    for (i = 0;i < strlen(string);i++)\n    {\n\tc = toupper(string[i]) - HU_FONTSTART;\n\tif (c < 0 || c >= HU_FONTSIZE)\n\t    w += 4;\n\telse\n\t    w += SHORT (hu_font[c]->width);\n    }\n\t\t\n    return w;\n}\n\n\n\n//\n//      Find string height from hu_font chars\n//\nint M_StringHeight(char* string)\n{\n    size_t             i;\n    int             h;\n    int             height = SHORT(hu_font[0]->height);\n\t\n    h = height;\n    for (i = 0;i < strlen(string);i++)\n\tif (string[i] == '\\n')\n\t    h += height;\n\t\t\n    return h;\n}\n\n\n//\n//      Write a string using the hu_font\n//\nvoid\nM_WriteText\n( int\t\tx,\n  int\t\ty,\n  char*\t\tstring)\n{\n    int\t\tw;\n    char*\tch;\n    int\t\tc;\n    int\t\tcx;\n    int\t\tcy;\n\t\t\n\n    ch = string;\n    cx = x;\n    cy = y;\n\t\n    while(1)\n    {\n\tc = *ch++;\n\tif (!c)\n\t    break;\n\tif (c == '\\n')\n\t{\n\t    cx = x;\n\t    cy += 12;\n\t    continue;\n\t}\n\t\t\n\tc = toupper(c) - HU_FONTSTART;\n\tif (c < 0 || c>= HU_FONTSIZE)\n\t{\n\t    cx += 4;\n\t    continue;\n\t}\n\t\t\n\tw = SHORT (hu_font[c]->width);\n\tif (cx+w > SCREENWIDTH)\n\t    break;\n\tV_DrawPatchDirect(cx, cy, hu_font[c]);\n\tcx+=w;\n    }\n}\n\n// These keys evaluate to a \"null\" key in Vanilla Doom that allows weird\n// jumping in the menus. Preserve this behavior for accuracy.\n\nstatic boolean IsNullKey(int key)\n{\n    return key == KEY_PAUSE || key == KEY_CAPSLOCK\n        || key == KEY_SCRLCK || key == KEY_NUMLOCK;\n}\n\n//\n// CONTROL PANEL\n//\n\n//\n// M_Responder\n//\nboolean M_Responder (event_t* ev)\n{\n    int             ch;\n    int             key;\n    int             i;\n    static  int     joywait = 0;\n    static  int     mousewait = 0;\n    static  int     mousey = 0;\n    static  int     lasty = 0;\n    static  int     mousex = 0;\n    static  int     lastx = 0;\n\n    // In testcontrols mode, none of the function keys should do anything\n    // - the only key is escape to quit.\n\n    if (testcontrols)\n    {\n        if (ev->type == ev_quit\n         || (ev->type == ev_keydown\n          && (ev->data1 == key_menu_activate || ev->data1 == key_menu_quit)))\n        {\n            I_Quit();\n            return true;\n        }\n\n        return false;\n    }\n\n    // \"close\" button pressed on window?\n    if (ev->type == ev_quit)\n    {\n        // First click on close button = bring up quit confirm message.\n        // Second click on close button = confirm quit\n\n        if (menuactive && messageToPrint && messageRoutine == M_QuitResponse)\n        {\n            M_QuitResponse(key_menu_confirm);\n        }\n        else\n        {\n            S_StartSound(NULL,sfx_swtchn);\n            M_QuitDOOM(0);\n        }\n\n        return true;\n    }\n\n    // key is the key pressed, ch is the actual character typed\n  \n    ch = 0;\n    key = -1;\n\t\n    if (ev->type == ev_joystick && joywait < I_GetTime())\n    {\n\tif (ev->data3 < 0)\n\t{\n\t    key = key_menu_up;\n\t    joywait = I_GetTime() + 5;\n\t}\n\telse if (ev->data3 > 0)\n\t{\n\t    key = key_menu_down;\n\t    joywait = I_GetTime() + 5;\n\t}\n\t\t\n\tif (ev->data2 < 0)\n\t{\n\t    key = key_menu_left;\n\t    joywait = I_GetTime() + 2;\n\t}\n\telse if (ev->data2 > 0)\n\t{\n\t    key = key_menu_right;\n\t    joywait = I_GetTime() + 2;\n\t}\n\t\t\n\tif (ev->data1&1)\n\t{\n\t    key = key_menu_forward;\n\t    joywait = I_GetTime() + 5;\n\t}\n\tif (ev->data1&2)\n\t{\n\t    key = key_menu_back;\n\t    joywait = I_GetTime() + 5;\n\t}\n        if (joybmenu >= 0 && (ev->data1 & (1 << joybmenu)) != 0)\n        {\n            key = key_menu_activate;\n\t    joywait = I_GetTime() + 5;\n        }\n    }\n    else\n    {\n\tif (ev->type == ev_mouse && mousewait < I_GetTime())\n\t{\n\t    mousey += ev->data3;\n\t    if (mousey < lasty-30)\n\t    {\n\t\tkey = key_menu_down;\n\t\tmousewait = I_GetTime() + 5;\n\t\tmousey = lasty -= 30;\n\t    }\n\t    else if (mousey > lasty+30)\n\t    {\n\t\tkey = key_menu_up;\n\t\tmousewait = I_GetTime() + 5;\n\t\tmousey = lasty += 30;\n\t    }\n\t\t\n\t    mousex += ev->data2;\n\t    if (mousex < lastx-30)\n\t    {\n\t\tkey = key_menu_left;\n\t\tmousewait = I_GetTime() + 5;\n\t\tmousex = lastx -= 30;\n\t    }\n\t    else if (mousex > lastx+30)\n\t    {\n\t\tkey = key_menu_right;\n\t\tmousewait = I_GetTime() + 5;\n\t\tmousex = lastx += 30;\n\t    }\n\t\t\n\t    if (ev->data1&1)\n\t    {\n\t\tkey = key_menu_forward;\n\t\tmousewait = I_GetTime() + 15;\n\t    }\n\t\t\t\n\t    if (ev->data1&2)\n\t    {\n\t\tkey = key_menu_back;\n\t\tmousewait = I_GetTime() + 15;\n\t    }\n\t}\n\telse\n\t{\n\t    if (ev->type == ev_keydown)\n\t    {\n\t\tkey = ev->data1;\n\t\tch = ev->data2;\n\t    }\n\t}\n    }\n    \n    if (key == -1)\n\treturn false;\n\n    // Save Game string input\n    if (saveStringEnter)\n    {\n\tswitch(key)\n\t{\n\t  case KEY_BACKSPACE:\n\t    if (saveCharIndex > 0)\n\t    {\n\t\tsaveCharIndex--;\n\t\tsavegamestrings[saveSlot][saveCharIndex] = 0;\n\t    }\n\t    break;\n\n          case KEY_ESCAPE:\n            saveStringEnter = 0;\n            M_StringCopy(savegamestrings[saveSlot], saveOldString,\n                         SAVESTRINGSIZE);\n            break;\n\n\t  case KEY_ENTER:\n\t    saveStringEnter = 0;\n\t    if (savegamestrings[saveSlot][0])\n\t\tM_DoSave(saveSlot);\n\t    break;\n\n\t  default:\n            // This is complicated.\n            // Vanilla has a bug where the shift key is ignored when entering\n            // a savegame name. If vanilla_keyboard_mapping is on, we want\n            // to emulate this bug by using 'data1'. But if it's turned off,\n            // it implies the user doesn't care about Vanilla emulation: just\n            // use the correct 'data2'.\n\n            if (vanilla_keyboard_mapping)\n            {\n                ch = key;\n            }\n\n            ch = toupper(ch);\n\n            if (ch != ' '\n             && (ch - HU_FONTSTART < 0 || ch - HU_FONTSTART >= HU_FONTSIZE))\n            {\n                break;\n            }\n\n\t    if (ch >= 32 && ch <= 127 &&\n\t\tsaveCharIndex < SAVESTRINGSIZE-1 &&\n\t\tM_StringWidth(savegamestrings[saveSlot]) <\n\t\t(SAVESTRINGSIZE-2)*8)\n\t    {\n\t\tsavegamestrings[saveSlot][saveCharIndex++] = ch;\n\t\tsavegamestrings[saveSlot][saveCharIndex] = 0;\n\t    }\n\t    break;\n\t}\n\treturn true;\n    }\n    \n    // Take care of any messages that need input\n    if (messageToPrint)\n    {\n\tif (messageNeedsInput)\n        {\n            if (key != ' ' && key != KEY_ESCAPE\n             && key != key_menu_confirm && key != key_menu_abort)\n            {\n                return false;\n            }\n\t}\n\n\tmenuactive = messageLastMenuActive;\n\tmessageToPrint = 0;\n\tif (messageRoutine)\n\t    messageRoutine(key);\n\n\tmenuactive = false;\n\tS_StartSound(NULL,sfx_swtchx);\n\treturn true;\n    }\n\n    if ((devparm && key == key_menu_help) ||\n        (key != 0 && key == key_menu_screenshot))\n    {\n\tG_ScreenShot ();\n\treturn true;\n    }\n\n    // F-Keys\n    if (!menuactive)\n    {\n\tif (key == key_menu_decscreen)      // Screen size down\n        {\n\t    if (automapactive || chat_on)\n\t\treturn false;\n\t    M_SizeDisplay(0);\n\t    S_StartSound(NULL,sfx_stnmov);\n\t    return true;\n\t}\n        else if (key == key_menu_incscreen) // Screen size up\n        {\n\t    if (automapactive || chat_on)\n\t\treturn false;\n\t    M_SizeDisplay(1);\n\t    S_StartSound(NULL,sfx_stnmov);\n\t    return true;\n\t}\n        else if (key == key_menu_help)     // Help key\n        {\n\t    M_StartControlPanel ();\n\n\t    if ( gamemode == retail )\n\t      currentMenu = &ReadDef2;\n\t    else\n\t      currentMenu = &ReadDef1;\n\n\t    itemOn = 0;\n\t    S_StartSound(NULL,sfx_swtchn);\n\t    return true;\n\t}\n        else if (key == key_menu_save)     // Save\n        {\n\t    M_StartControlPanel();\n\t    S_StartSound(NULL,sfx_swtchn);\n\t    M_SaveGame(0);\n\t    return true;\n        }\n        else if (key == key_menu_load)     // Load\n        {\n\t    M_StartControlPanel();\n\t    S_StartSound(NULL,sfx_swtchn);\n\t    M_LoadGame(0);\n\t    return true;\n        }\n        else if (key == key_menu_volume)   // Sound Volume\n        {\n\t    M_StartControlPanel ();\n\t    currentMenu = &SoundDef;\n\t    itemOn = sfx_vol;\n\t    S_StartSound(NULL,sfx_swtchn);\n\t    return true;\n\t}\n        else if (key == key_menu_detail)   // Detail toggle\n        {\n\t    M_ChangeDetail(0);\n\t    S_StartSound(NULL,sfx_swtchn);\n\t    return true;\n        }\n        else if (key == key_menu_qsave)    // Quicksave\n        {\n\t    S_StartSound(NULL,sfx_swtchn);\n\t    M_QuickSave();\n\t    return true;\n        }\n        else if (key == key_menu_endgame)  // End game\n        {\n\t    S_StartSound(NULL,sfx_swtchn);\n\t    M_EndGame(0);\n\t    return true;\n        }\n        else if (key == key_menu_messages) // Toggle messages\n        {\n\t    M_ChangeMessages(0);\n\t    S_StartSound(NULL,sfx_swtchn);\n\t    return true;\n        }\n        else if (key == key_menu_qload)    // Quickload\n        {\n\t    S_StartSound(NULL,sfx_swtchn);\n\t    M_QuickLoad();\n\t    return true;\n        }\n        else if (key == key_menu_quit)     // Quit DOOM\n        {\n\t    S_StartSound(NULL,sfx_swtchn);\n\t    M_QuitDOOM(0);\n\t    return true;\n        }\n        else if (key == key_menu_gamma)    // gamma toggle\n        {\n\t    usegamma++;\n\t    if (usegamma > 4)\n\t\tusegamma = 0;\n\t    players[consoleplayer].message = DEH_String(gammamsg[usegamma]);\n            I_SetPalette (W_CacheLumpName (DEH_String(\"PLAYPAL\"),PU_CACHE));\n\t    return true;\n\t}\n    }\n\n    // Pop-up menu?\n    if (!menuactive)\n    {\n\tif (key == key_menu_activate)\n\t{\n\t    M_StartControlPanel ();\n\t    S_StartSound(NULL,sfx_swtchn);\n\t    return true;\n\t}\n\treturn false;\n    }\n\n    // Keys usable within menu\n\n    if (key == key_menu_down)\n    {\n        // Move down to next item\n\n        do\n\t{\n\t    if (itemOn+1 > currentMenu->numitems-1)\n\t\titemOn = 0;\n\t    else itemOn++;\n\t    S_StartSound(NULL,sfx_pstop);\n\t} while(currentMenu->menuitems[itemOn].status==-1);\n\n\treturn true;\n    }\n    else if (key == key_menu_up)\n    {\n        // Move back up to previous item\n\n\tdo\n\t{\n\t    if (!itemOn)\n\t\titemOn = currentMenu->numitems-1;\n\t    else itemOn--;\n\t    S_StartSound(NULL,sfx_pstop);\n\t} while(currentMenu->menuitems[itemOn].status==-1);\n\n\treturn true;\n    }\n    else if (key == key_menu_left)\n    {\n        // Slide slider left\n\n\tif (currentMenu->menuitems[itemOn].routine &&\n\t    currentMenu->menuitems[itemOn].status == 2)\n\t{\n\t    S_StartSound(NULL,sfx_stnmov);\n\t    currentMenu->menuitems[itemOn].routine(0);\n\t}\n\treturn true;\n    }\n    else if (key == key_menu_right)\n    {\n        // Slide slider right\n\n\tif (currentMenu->menuitems[itemOn].routine &&\n\t    currentMenu->menuitems[itemOn].status == 2)\n\t{\n\t    S_StartSound(NULL,sfx_stnmov);\n\t    currentMenu->menuitems[itemOn].routine(1);\n\t}\n\treturn true;\n    }\n    else if (key == key_menu_forward)\n    {\n        // Activate menu item\n\n\tif (currentMenu->menuitems[itemOn].routine &&\n\t    currentMenu->menuitems[itemOn].status)\n\t{\n\t    currentMenu->lastOn = itemOn;\n\t    if (currentMenu->menuitems[itemOn].status == 2)\n\t    {\n\t\tcurrentMenu->menuitems[itemOn].routine(1);      // right arrow\n\t\tS_StartSound(NULL,sfx_stnmov);\n\t    }\n\t    else\n\t    {\n\t\tcurrentMenu->menuitems[itemOn].routine(itemOn);\n\t\tS_StartSound(NULL,sfx_pistol);\n\t    }\n\t}\n\treturn true;\n    }\n    else if (key == key_menu_activate)\n    {\n        // Deactivate menu\n\n\tcurrentMenu->lastOn = itemOn;\n\tM_ClearMenus ();\n\tS_StartSound(NULL,sfx_swtchx);\n\treturn true;\n    }\n    else if (key == key_menu_back)\n    {\n        // Go back to previous menu\n\n\tcurrentMenu->lastOn = itemOn;\n\tif (currentMenu->prevMenu)\n\t{\n\t    currentMenu = currentMenu->prevMenu;\n\t    itemOn = currentMenu->lastOn;\n\t    S_StartSound(NULL,sfx_swtchn);\n\t}\n\treturn true;\n    }\n\n    // Keyboard shortcut?\n    // Vanilla Doom has a weird behavior where it jumps to the scroll bars\n    // when the certain keys are pressed, so emulate this.\n\n    else if (ch != 0 || IsNullKey(key))\n    {\n\tfor (i = itemOn+1;i < currentMenu->numitems;i++)\n        {\n\t    if (currentMenu->menuitems[i].alphaKey == ch)\n\t    {\n\t\titemOn = i;\n\t\tS_StartSound(NULL,sfx_pstop);\n\t\treturn true;\n\t    }\n        }\n\n\tfor (i = 0;i <= itemOn;i++)\n        {\n\t    if (currentMenu->menuitems[i].alphaKey == ch)\n\t    {\n\t\titemOn = i;\n\t\tS_StartSound(NULL,sfx_pstop);\n\t\treturn true;\n\t    }\n        }\n    }\n\n    return false;\n}\n\n\n\n//\n// M_StartControlPanel\n//\nvoid M_StartControlPanel (void)\n{\n    // intro might call this repeatedly\n    if (menuactive)\n\treturn;\n    \n    menuactive = 1;\n    currentMenu = &MainDef;         // JDC\n    itemOn = currentMenu->lastOn;   // JDC\n}\n\n// Display OPL debug messages - hack for GENMIDI development.\n\n#if 0\nstatic void M_DrawOPLDev(void)\n{\n    extern void I_OPL_DevMessages(char *, size_t);\n    char debug[1024];\n    char *curr, *p;\n    int line;\n\n    //XXX I_OPL_DevMessages(debug, sizeof(debug));\n    curr = debug;\n    line = 0;\n\n    for (;;)\n    {\n        p = strchr(curr, '\\n');\n\n        if (p != NULL)\n        {\n            *p = '\\0';\n        }\n\n        M_WriteText(0, line * 8, curr);\n        ++line;\n\n        if (p == NULL)\n        {\n            break;\n        }\n\n        curr = p + 1;\n    }\n}\n#endif\n\n//\n// M_Drawer\n// Called after the view has been rendered,\n// but before it has been blitted.\n//\nvoid M_Drawer (void)\n{\n    static short\tx;\n    static short\ty;\n    unsigned int\ti;\n    unsigned int\tmax;\n    char\t\tstring[80];\n    char               *name;\n    int\t\t\tstart;\n\n    inhelpscreens = false;\n    \n    // Horiz. & Vertically center string and print it.\n    if (messageToPrint)\n    {\n\tstart = 0;\n\ty = SCREENHEIGHT/2 - M_StringHeight(messageString) / 2;\n\twhile (messageString[start] != '\\0')\n\t{\n\t    int foundnewline = 0;\n\n            for (i = 0; i < strlen(messageString + start); i++)\n            {\n                if (messageString[start + i] == '\\n')\n                {\n                    M_StringCopy(string, messageString + start,\n                                 sizeof(string));\n                    if (i < sizeof(string))\n                    {\n                        string[i] = '\\0';\n                    }\n\n                    foundnewline = 1;\n                    start += i + 1;\n                    break;\n                }\n            }\n\n            if (!foundnewline)\n            {\n                M_StringCopy(string, messageString + start, sizeof(string));\n                start += strlen(string);\n            }\n\n\t    x = SCREENWIDTH/2 - M_StringWidth(string) / 2;\n\t    M_WriteText(x, y, string);\n\t    y += SHORT(hu_font[0]->height);\n\t}\n\n\treturn;\n    }\n\n    //if (opldev)\n    //{\n    //    M_DrawOPLDev();\n    //}\n\n    if (!menuactive)\n\treturn;\n\n    if (currentMenu->routine)\n\tcurrentMenu->routine();         // call Draw routine\n    \n    // DRAW MENU\n    x = currentMenu->x;\n    y = currentMenu->y;\n    max = currentMenu->numitems;\n\n    for (i=0;i<max;i++)\n    {\n        name = DEH_String(currentMenu->menuitems[i].name);\n\n\tif (name[0])\n\t{\n\t    V_DrawPatchDirect (x, y, W_CacheLumpName(name, PU_CACHE));\n\t}\n\ty += LINEHEIGHT;\n    }\n\n    \n    // DRAW SKULL\n    V_DrawPatchDirect(x + SKULLXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT,\n\t\t      W_CacheLumpName(DEH_String(skullName[whichSkull]),\n\t\t\t\t      PU_CACHE));\n}\n\n\n//\n// M_ClearMenus\n//\nvoid M_ClearMenus (void)\n{\n    menuactive = 0;\n    // if (!netgame && usergame && paused)\n    //       sendpause = true;\n}\n\n\n\n\n//\n// M_SetupNextMenu\n//\nvoid M_SetupNextMenu(menu_t *menudef)\n{\n    currentMenu = menudef;\n    itemOn = currentMenu->lastOn;\n}\n\n\n//\n// M_Ticker\n//\nvoid M_Ticker (void)\n{\n    if (--skullAnimCounter <= 0)\n    {\n\twhichSkull ^= 1;\n\tskullAnimCounter = 8;\n    }\n}\n\n\n//\n// M_Init\n//\nvoid M_Init (void)\n{\n    currentMenu = &MainDef;\n    menuactive = 0;\n    itemOn = currentMenu->lastOn;\n    whichSkull = 0;\n    skullAnimCounter = 10;\n    screenSize = screenblocks - 3;\n    messageToPrint = 0;\n    messageString = NULL;\n    messageLastMenuActive = menuactive;\n    quickSaveSlot = -1;\n\n    // Here we could catch other version dependencies,\n    //  like HELP1/2, and four episodes.\n\n  \n    switch ( gamemode )\n    {\n      case commercial:\n        // Commercial has no \"read this\" entry.\n\tMainMenu[readthis] = MainMenu[quitdoom];\n\tMainDef.numitems--;\n\tMainDef.y += 8;\n\tNewDef.prevMenu = &MainDef;\n\tbreak;\n      case shareware:\n\t// Episode 2 and 3 are handled,\n\t//  branching to an ad screen.\n      case registered:\n\tbreak;\n      case retail:\n\t// We are fine.\n      default:\n\tbreak;\n    }\n\n    // Versions of doom.exe before the Ultimate Doom release only had\n    // three episodes; if we're emulating one of those then don't try\n    // to show episode four. If we are, then do show episode four\n    // (should crash if missing).\n    if (gameversion < exe_ultimate)\n    {\n\tEpiDef.numitems--;\n    }\n\n    //opldev = M_CheckParm(\"-opldev\") > 0;\n}\n\n"
  },
  {
    "path": "fbdoom/m_menu.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//   Menu widget stuff, episode selection and such.\n//    \n\n\n#ifndef __M_MENU__\n#define __M_MENU__\n\n\n\n#include \"d_event.h\"\n\n//\n// MENUS\n//\n// Called by main loop,\n// saves config file and calls I_Quit when user exits.\n// Even when the menu is not displayed,\n// this can resize the view and change game parameters.\n// Does all the real work of the menu interaction.\nboolean M_Responder (event_t *ev);\n\n\n// Called by main loop,\n// only used for menu (skull cursor) animation.\nvoid M_Ticker (void);\n\n// Called by main loop,\n// draws the menus directly into the screen buffer.\nvoid M_Drawer (void);\n\n// Called by D_DoomMain,\n// loads the config file.\nvoid M_Init (void);\n\n// Called by intro code to force menu up upon a keypress,\n// does nothing if menu is already up.\nvoid M_StartControlPanel (void);\n\n\n\nextern int detailLevel;\nextern int screenblocks;\n\n\n\n#endif    \n"
  },
  {
    "path": "fbdoom/m_misc.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 1993-2008 Raven Software\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//      Miscellaneous.\n//\n\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <errno.h>\n\n#ifdef _WIN32\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n#include <io.h>\n#ifdef _MSC_VER\n#include <direct.h>\n#endif\n#else\n#include <sys/stat.h>\n#include <sys/types.h>\n#endif\n\n#include \"doomtype.h\"\n\n#include \"deh_str.h\"\n\n#include \"i_swap.h\"\n#include \"i_system.h\"\n#include \"i_video.h\"\n#include \"m_misc.h\"\n#include \"v_video.h\"\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\n//\n// Create a directory\n//\n\nvoid M_MakeDirectory(char *path)\n{\n#ifdef _WIN32\n    mkdir(path);\n#else\n    mkdir(path, 0755);\n#endif\n}\n\n// Check if a file exists\n\nboolean M_FileExists(char *filename)\n{\n    FILE *fstream;\n\n    fstream = fopen(filename, \"r\");\n\n    if (fstream != NULL)\n    {\n        fclose(fstream);\n        return true;\n    }\n    else\n    {\n        // If we can't open because the file is a directory, the \n        // \"file\" exists at least!\n\n        return errno == EISDIR;\n    }\n}\n\n//\n// Determine the length of an open file.\n//\n\nlong M_FileLength(FILE *handle)\n{ \n    long savedpos;\n    long length;\n\n    // save the current position in the file\n    savedpos = ftell(handle);\n    \n    // jump to the end and find the length\n    fseek(handle, 0, SEEK_END);\n    length = ftell(handle);\n\n    // go back to the old location\n    fseek(handle, savedpos, SEEK_SET);\n\n    return length;\n}\n\n//\n// M_WriteFile\n//\n\nboolean M_WriteFile(char *name, void *source, int length)\n{\n    FILE *handle;\n    int\tcount;\n\t\n    handle = fopen(name, \"wb\");\n\n    if (handle == NULL)\n\treturn false;\n\n    count = fwrite(source, 1, length, handle);\n    fclose(handle);\n\t\n    if (count < length)\n\treturn false;\n\t\t\n    return true;\n}\n\n\n//\n// M_ReadFile\n//\n\nint M_ReadFile(char *name, byte **buffer)\n{\n    FILE *handle;\n    int\tcount, length;\n    byte *buf;\n\t\n    handle = fopen(name, \"rb\");\n    if (handle == NULL)\n\tI_Error (\"Couldn't read file %s\", name);\n\n    // find the size of the file by seeking to the end and\n    // reading the current position\n\n    length = M_FileLength(handle);\n    \n    buf = Z_Malloc (length, PU_STATIC, NULL);\n    count = fread(buf, 1, length, handle);\n    fclose (handle);\n\t\n    if (count < length)\n\tI_Error (\"Couldn't read file %s\", name);\n\t\t\n    *buffer = buf;\n    return length;\n}\n\n// Returns the path to a temporary file of the given name, stored\n// inside the system temporary directory.\n//\n// The returned value must be freed with Z_Free after use.\n\nchar *M_TempFile(char *s)\n{\n    char *tempdir;\n\n#ifdef _WIN32\n\n    // Check the TEMP environment variable to find the location.\n\n    tempdir = getenv(\"TEMP\");\n\n    if (tempdir == NULL)\n    {\n        tempdir = \".\";\n    }\n#else\n    // In Unix, just use /tmp.\n\n    tempdir = \"/tmp\";\n#endif\n\n    return M_StringJoin(tempdir, DIR_SEPARATOR_S, s, NULL);\n}\n\nboolean M_StrToInt(const char *str, int *result)\n{\n    return sscanf(str, \" 0x%x\", result) == 1\n        || sscanf(str, \" 0X%x\", result) == 1\n        || sscanf(str, \" 0%o\", result) == 1\n        || sscanf(str, \" %d\", result) == 1;\n}\n\nvoid M_ExtractFileBase(char *path, char *dest)\n{\n    char *src;\n    char *filename;\n    int length;\n\n    src = path + strlen(path) - 1;\n\n    // back up until a \\ or the start\n    while (src != path && *(src - 1) != DIR_SEPARATOR)\n    {\n\tsrc--;\n    }\n\n    filename = src;\n\n    // Copy up to eight characters\n    // Note: Vanilla Doom exits with an error if a filename is specified\n    // with a base of more than eight characters.  To remove the 8.3\n    // filename limit, instead we simply truncate the name.\n\n    length = 0;\n    memset(dest, 0, 8);\n\n    while (*src != '\\0' && *src != '.')\n    {\n        if (length >= 8)\n        {\n            printf(\"Warning: Truncated '%s' lump name to '%.8s'.\\n\",\n                   filename, dest);\n            break;\n        }\n\n\tdest[length++] = toupper((int)*src++);\n    }\n}\n\n//---------------------------------------------------------------------------\n//\n// PROC M_ForceUppercase\n//\n// Change string to uppercase.\n//\n//---------------------------------------------------------------------------\n\nvoid M_ForceUppercase(char *text)\n{\n    char *p;\n\n    for (p = text; *p != '\\0'; ++p)\n    {\n        *p = toupper(*p);\n    }\n}\n\n//\n// M_StrCaseStr\n//\n// Case-insensitive version of strstr()\n//\n\nchar *M_StrCaseStr(char *haystack, char *needle)\n{\n    unsigned int haystack_len;\n    unsigned int needle_len;\n    unsigned int len;\n    unsigned int i;\n\n    haystack_len = strlen(haystack);\n    needle_len = strlen(needle);\n\n    if (haystack_len < needle_len)\n    {\n        return NULL;\n    }\n\n    len = haystack_len - needle_len;\n\n    for (i = 0; i <= len; ++i)\n    {\n        if (!strncasecmp(haystack + i, needle, needle_len))\n        {\n            return haystack + i;\n        }\n    }\n\n    return NULL;\n}\n\n//\n// Safe version of strdup() that checks the string was successfully\n// allocated.\n//\n\nchar *M_StringDuplicate(const char *orig)\n{\n    char *result;\n\n    result = strdup(orig);\n\n    if (result == NULL)\n    {\n        I_Error(\"Failed to duplicate string (length %i)\\n\",\n                strlen(orig));\n    }\n\n    return result;\n}\n\n//\n// String replace function.\n//\n\nchar *M_StringReplace(const char *haystack, const char *needle,\n                      const char *replacement)\n{\n    char *result, *dst;\n    const char *p;\n    size_t needle_len = strlen(needle);\n    size_t result_len, dst_len;\n\n    // Iterate through occurrences of 'needle' and calculate the size of\n    // the new string.\n    result_len = strlen(haystack) + 1;\n    p = haystack;\n\n    for (;;)\n    {\n        p = strstr(p, needle);\n        if (p == NULL)\n        {\n            break;\n        }\n\n        p += needle_len;\n        result_len += strlen(replacement) - needle_len;\n    }\n\n    // Construct new string.\n\n    result = malloc(result_len);\n    if (result == NULL)\n    {\n        I_Error(\"M_StringReplace: Failed to allocate new string\");\n        return NULL;\n    }\n\n    dst = result; dst_len = result_len;\n    p = haystack;\n\n    while (*p != '\\0')\n    {\n        if (!strncmp(p, needle, needle_len))\n        {\n            M_StringCopy(dst, replacement, dst_len);\n            p += needle_len;\n            dst += strlen(replacement);\n            dst_len -= strlen(replacement);\n        }\n        else\n        {\n            *dst = *p;\n            ++dst; --dst_len;\n            ++p;\n        }\n    }\n\n    *dst = '\\0';\n\n    return result;\n}\n\n// Safe string copy function that works like OpenBSD's strlcpy().\n// Returns true if the string was not truncated.\n\nboolean M_StringCopy(char *dest, const char *src, size_t dest_size)\n{\n    size_t len;\n\n    if (dest_size >= 1)\n    {\n        dest[dest_size - 1] = '\\0';\n        strncpy(dest, src, dest_size - 1);\n    }\n    else\n    {\n        return false;\n    }\n\n    len = strlen(dest);\n    return src[len] == '\\0';\n}\n\n// Safe string concat function that works like OpenBSD's strlcat().\n// Returns true if string not truncated.\n\nboolean M_StringConcat(char *dest, const char *src, size_t dest_size)\n{\n    size_t offset;\n\n    offset = strlen(dest);\n    if (offset > dest_size)\n    {\n        offset = dest_size;\n    }\n\n    return M_StringCopy(dest + offset, src, dest_size - offset);\n}\n\n// Returns true if 's' begins with the specified prefix.\n\nboolean M_StringStartsWith(const char *s, const char *prefix)\n{\n    return strlen(s) > strlen(prefix)\n        && strncmp(s, prefix, strlen(prefix)) == 0;\n}\n\n// Returns true if 's' ends with the specified suffix.\n\nboolean M_StringEndsWith(const char *s, const char *suffix)\n{\n    return strlen(s) >= strlen(suffix)\n        && strcmp(s + strlen(s) - strlen(suffix), suffix) == 0;\n}\n\n// Return a newly-malloced string with all the strings given as arguments\n// concatenated together.\n\nchar *M_StringJoin(const char *s, ...)\n{\n    char *result;\n    const char *v;\n    va_list args;\n    size_t result_len;\n\n    result_len = strlen(s) + 1;\n\n    va_start(args, s);\n    for (;;)\n    {\n        v = va_arg(args, const char *);\n        if (v == NULL)\n        {\n            break;\n        }\n\n        result_len += strlen(v);\n    }\n    va_end(args);\n\n    result = malloc(result_len);\n\n    if (result == NULL)\n    {\n        I_Error(\"M_StringJoin: Failed to allocate new string.\");\n        return NULL;\n    }\n\n    M_StringCopy(result, s, result_len);\n\n    va_start(args, s);\n    for (;;)\n    {\n        v = va_arg(args, const char *);\n        if (v == NULL)\n        {\n            break;\n        }\n\n        M_StringConcat(result, v, result_len);\n    }\n    va_end(args);\n\n    return result;\n}\n\n// On Windows, vsnprintf() is _vsnprintf().\n#ifdef _WIN32\n#if _MSC_VER < 1400 /* not needed for Visual Studio 2008 */\n#define vsnprintf _vsnprintf\n#endif\n#endif\n\n// Safe, portable vsnprintf().\nint M_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args)\n{\n    int result;\n\n    if (buf_len < 1)\n    {\n        return 0;\n    }\n\n    // Windows (and other OSes?) has a vsnprintf() that doesn't always\n    // append a trailing \\0. So we must do it, and write into a buffer\n    // that is one byte shorter; otherwise this function is unsafe.\n    result = vsnprintf(buf, buf_len, s, args);\n\n    // If truncated, change the final char in the buffer to a \\0.\n    // A negative result indicates a truncated buffer on Windows.\n    if (result < 0 || result >= buf_len)\n    {\n        buf[buf_len - 1] = '\\0';\n        result = buf_len - 1;\n    }\n\n    return result;\n}\n\n// Safe, portable snprintf().\nint M_snprintf(char *buf, size_t buf_len, const char *s, ...)\n{\n    va_list args;\n    int result;\n    va_start(args, s);\n    result = M_vsnprintf(buf, buf_len, s, args);\n    va_end(args);\n    return result;\n}\n\n#ifdef _WIN32\n\nchar *M_OEMToUTF8(const char *oem)\n{\n    unsigned int len = strlen(oem) + 1;\n    wchar_t *tmp;\n    char *result;\n\n    tmp = malloc(len * sizeof(wchar_t));\n    MultiByteToWideChar(CP_OEMCP, 0, oem, len, tmp, len);\n    result = malloc(len * 4);\n    WideCharToMultiByte(CP_UTF8, 0, tmp, len, result, len * 4, NULL, NULL);\n    free(tmp);\n\n    return result;\n}\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/m_misc.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//      Miscellaneous.\n//    \n\n\n#ifndef __M_MISC__\n#define __M_MISC__\n\n#include <stdio.h>\n#include <stdarg.h>\n\n#include \"doomtype.h\"\n\nboolean M_WriteFile(char *name, void *source, int length);\nint M_ReadFile(char *name, byte **buffer);\nvoid M_MakeDirectory(char *dir);\nchar *M_TempFile(char *s);\nboolean M_FileExists(char *file);\nlong M_FileLength(FILE *handle);\nboolean M_StrToInt(const char *str, int *result);\nvoid M_ExtractFileBase(char *path, char *dest);\nvoid M_ForceUppercase(char *text);\nchar *M_StrCaseStr(char *haystack, char *needle);\nchar *M_StringDuplicate(const char *orig);\nboolean M_StringCopy(char *dest, const char *src, size_t dest_size);\nboolean M_StringConcat(char *dest, const char *src, size_t dest_size);\nchar *M_StringReplace(const char *haystack, const char *needle,\n                      const char *replacement);\nchar *M_StringJoin(const char *s, ...);\nboolean M_StringStartsWith(const char *s, const char *prefix);\nboolean M_StringEndsWith(const char *s, const char *suffix);\nint M_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args);\nint M_snprintf(char *buf, size_t buf_len, const char *s, ...);\nchar *M_OEMToUTF8(const char *ansi);\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/m_random.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tRandom number LUT.\n//\n\n//\n// M_Random\n// Returns a 0-255 number\n//\n\nstatic const unsigned char rndtable[256] = {\n    0,   8, 109, 220, 222, 241, 149, 107,  75, 248, 254, 140,  16,  66 ,\n    74,  21, 211,  47,  80, 242, 154,  27, 205, 128, 161,  89,  77,  36 ,\n    95, 110,  85,  48, 212, 140, 211, 249,  22,  79, 200,  50,  28, 188 ,\n    52, 140, 202, 120,  68, 145,  62,  70, 184, 190,  91, 197, 152, 224 ,\n    149, 104,  25, 178, 252, 182, 202, 182, 141, 197,   4,  81, 181, 242 ,\n    145,  42,  39, 227, 156, 198, 225, 193, 219,  93, 122, 175, 249,   0 ,\n    175, 143,  70, 239,  46, 246, 163,  53, 163, 109, 168, 135,   2, 235 ,\n    25,  92,  20, 145, 138,  77,  69, 166,  78, 176, 173, 212, 166, 113 ,\n    94, 161,  41,  50, 239,  49, 111, 164,  70,  60,   2,  37, 171,  75 ,\n    136, 156,  11,  56,  42, 146, 138, 229,  73, 146,  77,  61,  98, 196 ,\n    135, 106,  63, 197, 195,  86,  96, 203, 113, 101, 170, 247, 181, 113 ,\n    80, 250, 108,   7, 255, 237, 129, 226,  79, 107, 112, 166, 103, 241 ,\n    24, 223, 239, 120, 198,  58,  60,  82, 128,   3, 184,  66, 143, 224 ,\n    145, 224,  81, 206, 163,  45,  63,  90, 168, 114,  59,  33, 159,  95 ,\n    28, 139, 123,  98, 125, 196,  15,  70, 194, 253,  54,  14, 109, 226 ,\n    71,  17, 161,  93, 186,  87, 244, 138,  20,  52, 123, 251,  26,  36 ,\n    17,  46,  52, 231, 232,  76,  31, 221,  84,  37, 216, 165, 212, 106 ,\n    197, 242,  98,  43,  39, 175, 254, 145, 190,  84, 118, 222, 187, 136 ,\n    120, 163, 236, 249\n};\n\nint\trndindex = 0;\nint\tprndindex = 0;\n\n// Which one is deterministic?\nint P_Random (void)\n{\n    prndindex = (prndindex+1)&0xff;\n    return rndtable[prndindex];\n}\n\nint M_Random (void)\n{\n    rndindex = (rndindex+1)&0xff;\n    return rndtable[rndindex];\n}\n\nvoid M_ClearRandom (void)\n{\n    rndindex = prndindex = 0;\n}\n"
  },
  {
    "path": "fbdoom/m_random.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\n//    \n\n\n#ifndef __M_RANDOM__\n#define __M_RANDOM__\n\n\n#include \"doomtype.h\"\n\n\n\n// Returns a number from 0 to 255,\n// from a lookup table.\nint M_Random (void);\n\n// As M_Random, but used only by the play simulation.\nint P_Random (void);\n\n// Fix randoms for demos.\nvoid M_ClearRandom (void);\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/memio.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// Emulates the IO functions in C stdio.h reading and writing to \n// memory.\n//\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"memio.h\"\n\n#include \"z_zone.h\"\n\ntypedef enum {\n\tMODE_READ,\n\tMODE_WRITE,\n} memfile_mode_t;\n\nstruct _MEMFILE {\n\tunsigned char *buf;\n\tsize_t buflen;\n\tsize_t alloced;\n\tunsigned int position;\n\tmemfile_mode_t mode;\n};\n\n// Open a memory area for reading\n\nMEMFILE *mem_fopen_read(void *buf, size_t buflen)\n{\n\tMEMFILE *file;\n\n\tfile = Z_Malloc(sizeof(MEMFILE), PU_STATIC, 0);\n\n\tfile->buf = (unsigned char *) buf;\n\tfile->buflen = buflen;\n\tfile->position = 0;\n\tfile->mode = MODE_READ;\n\n\treturn file;\n}\n\n// Read bytes\n\nsize_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream)\n{\n\tsize_t items;\n\n\tif (stream->mode != MODE_READ)\n\t{\n\t\tprintf(\"not a read stream\\n\");\n\t\treturn -1;\n\t}\n\n\t// Trying to read more bytes than we have left?\n\t\n\titems = nmemb;\n\n\tif (items * size > stream->buflen - stream->position) \n\t{\n\t\titems = (stream->buflen - stream->position) / size;\n\t}\n\t\n\t// Copy bytes to buffer\n\t\n\tmemcpy(buf, stream->buf + stream->position, items * size);\n\n\t// Update position\n\n\tstream->position += items * size;\n\t\n\treturn items;\n}\n\n// Open a memory area for writing\n\nMEMFILE *mem_fopen_write(void)\n{\n\tMEMFILE *file;\n\n\tfile = Z_Malloc(sizeof(MEMFILE), PU_STATIC, 0);\n\n\tfile->alloced = 1024;\n\tfile->buf = Z_Malloc(file->alloced, PU_STATIC, 0);\n\tfile->buflen = 0;\n\tfile->position = 0;\n\tfile->mode = MODE_WRITE;\n\n\treturn file;\n}\n\n// Write bytes to stream\n\nsize_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, MEMFILE *stream)\n{\n\tsize_t bytes;\n\n\tif (stream->mode != MODE_WRITE)\n\t{\n\t\treturn -1;\n\t}\n\t\n\t// More bytes than can fit in the buffer?\n\t// If so, reallocate bigger.\n\n\tbytes = size * nmemb;\n\t\n\twhile (bytes > stream->alloced - stream->position)\n\t{\n\t\tunsigned char *newbuf;\n\n\t\tnewbuf = Z_Malloc(stream->alloced * 2, PU_STATIC, 0);\n\t\tmemcpy(newbuf, stream->buf, stream->alloced);\n\t\tZ_Free(stream->buf);\n\t\tstream->buf = newbuf;\n\t\tstream->alloced *= 2;\n\t}\n\n\t// Copy into buffer\n\t\n\tmemcpy(stream->buf + stream->position, ptr, bytes);\n\tstream->position += bytes;\n\n\tif (stream->position > stream->buflen)\n\t\tstream->buflen = stream->position;\n\n\treturn nmemb;\n}\n\nvoid mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen)\n{\n\t*buf = stream->buf;\n\t*buflen = stream->buflen;\n}\n\nvoid mem_fclose(MEMFILE *stream)\n{\n\tif (stream->mode == MODE_WRITE)\n\t{\n\t\tZ_Free(stream->buf);\n\t}\n\n\tZ_Free(stream);\n}\n\nlong mem_ftell(MEMFILE *stream)\n{\n\treturn stream->position;\n}\n\nint mem_fseek(MEMFILE *stream, signed long position, mem_rel_t whence)\n{\n\tunsigned int newpos;\n\n\tswitch (whence)\n\t{\n\t\tcase MEM_SEEK_SET:\n\t\t\tnewpos = (int) position;\n\t\t\tbreak;\n\n\t\tcase MEM_SEEK_CUR:\n\t\t\tnewpos = (int) (stream->position + position);\n\t\t\tbreak;\n\t\t\t\n\t\tcase MEM_SEEK_END:\n\t\t\tnewpos = (int) (stream->buflen + position);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn -1;\n\t}\n\n\tif (newpos < stream->buflen)\n\t{\n\t\tstream->position = newpos;\n\t\treturn 0;\n\t}\n\telse\n\t{\n\t\tprintf(\"Error seeking to %i\\n\", newpos);\n\t\treturn -1;\n\t}\n}\n\n\n"
  },
  {
    "path": "fbdoom/memio.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n\n#ifndef MEMIO_H\n#define MEMIO_H\n\ntypedef struct _MEMFILE MEMFILE;\n\ntypedef enum \n{\n\tMEM_SEEK_SET,\n\tMEM_SEEK_CUR,\n\tMEM_SEEK_END,\n} mem_rel_t;\n\nMEMFILE *mem_fopen_read(void *buf, size_t buflen);\nsize_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream);\nMEMFILE *mem_fopen_write(void);\nsize_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, MEMFILE *stream);\nvoid mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen);\nvoid mem_fclose(MEMFILE *stream);\nlong mem_ftell(MEMFILE *stream);\nint mem_fseek(MEMFILE *stream, signed long offset, mem_rel_t whence);\n\n#endif /* #ifndef MEMIO_H */\n\t  \n"
  },
  {
    "path": "fbdoom/net_client.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// Network client code\n//\n\n#ifndef NET_CLIENT_H\n#define NET_CLIENT_H\n\n#include \"doomtype.h\"\n#include \"d_ticcmd.h\"\n#include \"sha1.h\"\n#include \"net_defs.h\"\n\nboolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data);\nvoid NET_CL_Disconnect(void);\nvoid NET_CL_Run(void);\nvoid NET_CL_Init(void);\nvoid NET_CL_LaunchGame(void);\nvoid NET_CL_StartGame(net_gamesettings_t *settings);\nvoid NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic);\nboolean NET_CL_GetSettings(net_gamesettings_t *_settings);\nvoid NET_Init(void);\n\nvoid NET_BindVariables(void);\n\nextern boolean net_client_connected;\nextern boolean net_client_received_wait_data;\nextern net_waitdata_t net_client_wait_data;\nextern boolean net_waiting_for_launch;\nextern char *net_player_name;\n\nextern sha1_digest_t net_server_wad_sha1sum;\nextern sha1_digest_t net_server_deh_sha1sum;\nextern unsigned int net_server_is_freedoom;\nextern sha1_digest_t net_local_wad_sha1sum;\nextern sha1_digest_t net_local_deh_sha1sum;\nextern unsigned int net_local_is_freedoom;\n\nextern boolean drone;\n\n#endif /* #ifndef NET_CLIENT_H */\n"
  },
  {
    "path": "fbdoom/net_dedicated.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n//\n// Dedicated server code.\n// \n\n#ifndef NET_DEDICATED_H\n#define NET_DEDICATED_H\n\nvoid NET_DedicatedServer(void);\n\n#endif /* #ifndef NET_DEDICATED_H */\n\n\n"
  },
  {
    "path": "fbdoom/net_defs.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     Definitions for use in networking code.\n//\n\n#ifndef NET_DEFS_H\n#define NET_DEFS_H \n\n#include <stdio.h>\n\n#include \"doomtype.h\"\n#include \"d_ticcmd.h\"\n#include \"sha1.h\"\n\n// Absolute maximum number of \"nodes\" in the game.  This is different to\n// NET_MAXPLAYERS, as there may be observers that are not participating\n// (eg. left/right monitors)\n\n#define MAXNETNODES 16\n\n// The maximum number of players, multiplayer/networking.\n// This is the maximum supported by the networking code; individual games\n// have their own values for MAXPLAYERS that can be smaller.\n\n#define NET_MAXPLAYERS 8\n\n// Maximum length of a player's name.\n\n#define MAXPLAYERNAME 30\n\n// Networking and tick handling related.\n\n#define BACKUPTICS 128\n\ntypedef struct _net_module_s net_module_t;\ntypedef struct _net_packet_s net_packet_t;\ntypedef struct _net_addr_s net_addr_t;\ntypedef struct _net_context_s net_context_t;\n\nstruct _net_packet_s\n{\n    byte *data;\n    size_t len;\n    size_t alloced;\n    unsigned int pos;\n};\n\nstruct _net_module_s\n{\n    // Initialize this module for use as a client\n\n    boolean (*InitClient)(void);\n\n    // Initialize this module for use as a server\n\n    boolean (*InitServer)(void);\n\n    // Send a packet\n\n    void (*SendPacket)(net_addr_t *addr, net_packet_t *packet);\n\n    // Check for new packets to receive\n    //\n    // Returns true if packet received\n\n    boolean (*RecvPacket)(net_addr_t **addr, net_packet_t **packet);\n\n    // Converts an address to a string\n\n    void (*AddrToString)(net_addr_t *addr, char *buffer, int buffer_len);\n\n    // Free back an address when no longer in use\n\n    void (*FreeAddress)(net_addr_t *addr);\n\n    // Try to resolve a name to an address\n\n    net_addr_t *(*ResolveAddress)(char *addr);\n};\n\n// net_addr_t\n\nstruct _net_addr_s\n{\n    net_module_t *module;\n    void *handle;\n};\n\n// magic number sent when connecting to check this is a valid client\n\n#define NET_MAGIC_NUMBER 3436803284U\n\n// header field value indicating that the packet is a reliable packet\n\n#define NET_RELIABLE_PACKET (1 << 15)\n\n// packet types\n\ntypedef enum\n{\n    NET_PACKET_TYPE_SYN,\n    NET_PACKET_TYPE_ACK,\n    NET_PACKET_TYPE_REJECTED,\n    NET_PACKET_TYPE_KEEPALIVE,\n    NET_PACKET_TYPE_WAITING_DATA,\n    NET_PACKET_TYPE_GAMESTART,\n    NET_PACKET_TYPE_GAMEDATA,\n    NET_PACKET_TYPE_GAMEDATA_ACK,\n    NET_PACKET_TYPE_DISCONNECT,\n    NET_PACKET_TYPE_DISCONNECT_ACK,\n    NET_PACKET_TYPE_RELIABLE_ACK,\n    NET_PACKET_TYPE_GAMEDATA_RESEND,\n    NET_PACKET_TYPE_CONSOLE_MESSAGE,\n    NET_PACKET_TYPE_QUERY,\n    NET_PACKET_TYPE_QUERY_RESPONSE,\n    NET_PACKET_TYPE_LAUNCH,\n} net_packet_type_t;\n\ntypedef enum\n{\n    NET_MASTER_PACKET_TYPE_ADD,\n    NET_MASTER_PACKET_TYPE_ADD_RESPONSE,\n    NET_MASTER_PACKET_TYPE_QUERY,\n    NET_MASTER_PACKET_TYPE_QUERY_RESPONSE,\n    NET_MASTER_PACKET_TYPE_GET_METADATA,\n    NET_MASTER_PACKET_TYPE_GET_METADATA_RESPONSE,\n    NET_MASTER_PACKET_TYPE_SIGN_START,\n    NET_MASTER_PACKET_TYPE_SIGN_START_RESPONSE,\n    NET_MASTER_PACKET_TYPE_SIGN_END,\n    NET_MASTER_PACKET_TYPE_SIGN_END_RESPONSE,\n} net_master_packet_type_t;\n\n// Settings specified when the client connects to the server.\n\ntypedef struct\n{\n    int gamemode;\n    int gamemission;\n    int lowres_turn;\n    int drone;\n    int max_players;\n    int is_freedoom;\n    sha1_digest_t wad_sha1sum;\n    sha1_digest_t deh_sha1sum;\n    int player_class;\n} net_connect_data_t;\n\n// Game settings sent by client to server when initiating game start,\n// and received from the server by clients when the game starts.\n\ntypedef struct\n{\n    int ticdup;\n    int extratics;\n    int deathmatch;\n    int episode;\n    int nomonsters;\n    int fast_monsters;\n    int respawn_monsters;\n    int map;\n    int skill;\n    int gameversion;\n    int lowres_turn;\n    int new_sync;\n    int timelimit;\n    int loadgame;\n    int random;  // [Strife only]\n\n    // These fields are only used by the server when sending a game\n    // start message:\n\n    int num_players;\n    int consoleplayer;\n\n    // Hexen player classes:\n\n    int player_classes[NET_MAXPLAYERS];\n\n} net_gamesettings_t;\n\n#define NET_TICDIFF_FORWARD      (1 << 0)\n#define NET_TICDIFF_SIDE         (1 << 1)\n#define NET_TICDIFF_TURN         (1 << 2)\n#define NET_TICDIFF_BUTTONS      (1 << 3)\n#define NET_TICDIFF_CONSISTANCY  (1 << 4)\n#define NET_TICDIFF_CHATCHAR     (1 << 5)\n#define NET_TICDIFF_RAVEN        (1 << 6)\n#define NET_TICDIFF_STRIFE       (1 << 7)\n\ntypedef struct\n{\n    unsigned int diff;\n    ticcmd_t cmd;\n} net_ticdiff_t;\n\n// Complete set of ticcmds from all players\n\ntypedef struct \n{\n    signed int latency;\n    unsigned int seq;\n    boolean playeringame[NET_MAXPLAYERS];\n    net_ticdiff_t cmds[NET_MAXPLAYERS];\n} net_full_ticcmd_t;\n\n// Data sent in response to server queries\n\ntypedef struct\n{\n    char *version;\n    int server_state;\n    int num_players;\n    int max_players;\n    int gamemode;\n    int gamemission;\n    char *description;\n} net_querydata_t;\n\n// Data sent by the server while waiting for the game to start.\n\ntypedef struct\n{\n    int num_players;\n    int num_drones;\n    int ready_players;\n    int max_players;\n    int is_controller;\n    int consoleplayer;\n    char player_names[NET_MAXPLAYERS][MAXPLAYERNAME];\n    char player_addrs[NET_MAXPLAYERS][MAXPLAYERNAME];\n    sha1_digest_t wad_sha1sum;\n    sha1_digest_t deh_sha1sum;\n    int is_freedoom;\n} net_waitdata_t;\n\n#endif /* #ifndef NET_DEFS_H */\n"
  },
  {
    "path": "fbdoom/net_gui.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// Graphical stuff related to the networking code:\n//\n//  * The client waiting screen when we are waiting for the server to\n//    start the game.\n//\n\n\n#ifndef NET_GUI_H\n#define NET_GUI_H\n\n#include \"doomtype.h\"\n\nextern void NET_WaitForLaunch(void);\n\n#endif /* #ifndef NET_GUI_H */\n\n"
  },
  {
    "path": "fbdoom/net_io.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//      Network packet manipulation (net_packet_t)\n//\n\n#ifndef NET_IO_H\n#define NET_IO_H\n\n#include \"net_defs.h\"\n\nextern net_addr_t net_broadcast_addr;\n\nnet_context_t *NET_NewContext(void);\nvoid NET_AddModule(net_context_t *context, net_module_t *module);\nvoid NET_SendPacket(net_addr_t *addr, net_packet_t *packet);\nvoid NET_SendBroadcast(net_context_t *context, net_packet_t *packet);\nboolean NET_RecvPacket(net_context_t *context, net_addr_t **addr, \n                       net_packet_t **packet);\nchar *NET_AddrToString(net_addr_t *addr);\nvoid NET_FreeAddress(net_addr_t *addr);\nnet_addr_t *NET_ResolveAddress(net_context_t *context, char *address);\n\n#endif  /* #ifndef NET_IO_H */\n\n"
  },
  {
    "path": "fbdoom/net_loop.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//      Loopback network module for server compiled into the client\n//\n\n#ifndef NET_LOOP_H\n#define NET_LOOP_H\n\n#include \"net_defs.h\"\n\nextern net_module_t net_loop_client_module;\nextern net_module_t net_loop_server_module;\n\n#endif /* #ifndef NET_LOOP_H */\n\n"
  },
  {
    "path": "fbdoom/net_packet.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     Definitions for use in networking code.\n//\n\n#ifndef NET_PACKET_H\n#define NET_PACKET_H\n\n#include \"net_defs.h\"\n\nnet_packet_t *NET_NewPacket(int initial_size);\nnet_packet_t *NET_PacketDup(net_packet_t *packet);\nvoid NET_FreePacket(net_packet_t *packet);\n\nboolean NET_ReadInt8(net_packet_t *packet, unsigned int *data);\nboolean NET_ReadInt16(net_packet_t *packet, unsigned int *data);\nboolean NET_ReadInt32(net_packet_t *packet, unsigned int *data);\n\nboolean NET_ReadSInt8(net_packet_t *packet, signed int *data);\nboolean NET_ReadSInt16(net_packet_t *packet, signed int *data);\nboolean NET_ReadSInt32(net_packet_t *packet, signed int *data);\n\nchar *NET_ReadString(net_packet_t *packet);\n\nvoid NET_WriteInt8(net_packet_t *packet, unsigned int i);\nvoid NET_WriteInt16(net_packet_t *packet, unsigned int i);\nvoid NET_WriteInt32(net_packet_t *packet, unsigned int i);\n\nvoid NET_WriteString(net_packet_t *packet, char *string);\n\n#endif /* #ifndef NET_PACKET_H */\n\n"
  },
  {
    "path": "fbdoom/net_query.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     Querying servers to find their current status.\n//\n\n#ifndef NET_QUERY_H\n#define NET_QUERY_H\n\n#include \"net_defs.h\"\n\ntypedef void (*net_query_callback_t)(net_addr_t *addr,\n                                     net_querydata_t *querydata,\n                                     unsigned int ping_time,\n                                     void *user_data);\n\nextern int NET_StartLANQuery(void);\nextern int NET_StartMasterQuery(void);\n\nextern void NET_LANQuery(void);\nextern void NET_MasterQuery(void);\nextern void NET_QueryAddress(char *addr);\nextern net_addr_t *NET_FindLANServer(void);\n\nextern int NET_Query_Poll(net_query_callback_t callback, void *user_data);\n\nextern net_addr_t *NET_Query_ResolveMaster(net_context_t *context);\nextern void NET_Query_AddToMaster(net_addr_t *master_addr);\nextern boolean NET_Query_CheckAddedToMaster(boolean *result);\nextern void NET_Query_MasterResponse(net_packet_t *packet);\n\n#endif /* #ifndef NET_QUERY_H */\n\n"
  },
  {
    "path": "fbdoom/net_sdl.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     Networking module which uses SDL_net\n//\n\n#ifndef NET_SDL_H\n#define NET_SDL_H\n\n#include \"net_defs.h\"\n\nextern net_module_t net_sdl_module;\n\n#endif /* #ifndef NET_SDL_H */\n\n"
  },
  {
    "path": "fbdoom/net_server.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// Network server code\n//\n\n#ifndef NET_SERVER_H\n#define NET_SERVER_H\n\n// initialize server and wait for connections\n\nvoid NET_SV_Init(void);\n\n// run server: check for new packets received etc.\n\nvoid NET_SV_Run(void);\n\n// Shut down the server\n// Blocks until all clients disconnect, or until a 5 second timeout\n\nvoid NET_SV_Shutdown(void);\n\n// Add a network module to the context used by the server\n\nvoid NET_SV_AddModule(net_module_t *module);\n\n// Register server with master server.\n\nvoid NET_SV_RegisterWithMaster(void);\n\n#endif /* #ifndef NET_SERVER_H */\n\n"
  },
  {
    "path": "fbdoom/p_ceilng.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:  Ceiling aninmation (lowering, crushing, raising)\n//\n\n\n\n#include \"z_zone.h\"\n#include \"doomdef.h\"\n#include \"p_local.h\"\n\n#include \"s_sound.h\"\n\n// State.\n#include \"doomstat.h\"\n#include \"r_state.h\"\n\n// Data.\n#include \"sounds.h\"\n\n//\n// CEILINGS\n//\n\n\nceiling_t*\tactiveceilings[MAXCEILINGS];\n\n\n//\n// T_MoveCeiling\n//\n\nvoid T_MoveCeiling (ceiling_t* ceiling)\n{\n    result_e\tres;\n\t\n    switch(ceiling->direction)\n    {\n      case 0:\n\t// IN STASIS\n\tbreak;\n      case 1:\n\t// UP\n\tres = T_MovePlane(ceiling->sector,\n\t\t\t  ceiling->speed,\n\t\t\t  ceiling->topheight,\n\t\t\t  false,1,ceiling->direction);\n\t\n\tif (!(leveltime&7))\n\t{\n\t    switch(ceiling->type)\n\t    {\n\t      case silentCrushAndRaise:\n\t\tbreak;\n\t      default:\n\t\tS_StartSound(&ceiling->sector->soundorg, sfx_stnmov);\n\t\t// ?\n\t\tbreak;\n\t    }\n\t}\n\t\n\tif (res == pastdest)\n\t{\n\t    switch(ceiling->type)\n\t    {\n\t      case raiseToHighest:\n\t\tP_RemoveActiveCeiling(ceiling);\n\t\tbreak;\n\t\t\n\t      case silentCrushAndRaise:\n\t\tS_StartSound(&ceiling->sector->soundorg, sfx_pstop);\n\t      case fastCrushAndRaise:\n\t      case crushAndRaise:\n\t\tceiling->direction = -1;\n\t\tbreak;\n\t\t\n\t      default:\n\t\tbreak;\n\t    }\n\t    \n\t}\n\tbreak;\n\t\n      case -1:\n\t// DOWN\n\tres = T_MovePlane(ceiling->sector,\n\t\t\t  ceiling->speed,\n\t\t\t  ceiling->bottomheight,\n\t\t\t  ceiling->crush,1,ceiling->direction);\n\t\n\tif (!(leveltime&7))\n\t{\n\t    switch(ceiling->type)\n\t    {\n\t      case silentCrushAndRaise: break;\n\t      default:\n\t\tS_StartSound(&ceiling->sector->soundorg, sfx_stnmov);\n\t    }\n\t}\n\t\n\tif (res == pastdest)\n\t{\n\t    switch(ceiling->type)\n\t    {\n\t      case silentCrushAndRaise:\n\t\tS_StartSound(&ceiling->sector->soundorg, sfx_pstop);\n\t      case crushAndRaise:\n\t\tceiling->speed = CEILSPEED;\n\t      case fastCrushAndRaise:\n\t\tceiling->direction = 1;\n\t\tbreak;\n\n\t      case lowerAndCrush:\n\t      case lowerToFloor:\n\t\tP_RemoveActiveCeiling(ceiling);\n\t\tbreak;\n\n\t      default:\n\t\tbreak;\n\t    }\n\t}\n\telse // ( res != pastdest )\n\t{\n\t    if (res == crushed)\n\t    {\n\t\tswitch(ceiling->type)\n\t\t{\n\t\t  case silentCrushAndRaise:\n\t\t  case crushAndRaise:\n\t\t  case lowerAndCrush:\n\t\t    ceiling->speed = CEILSPEED / 8;\n\t\t    break;\n\n\t\t  default:\n\t\t    break;\n\t\t}\n\t    }\n\t}\n\tbreak;\n    }\n}\n\n\n//\n// EV_DoCeiling\n// Move a ceiling up/down and all around!\n//\nint\nEV_DoCeiling\n( line_t*\tline,\n  ceiling_e\ttype )\n{\n    int\t\tsecnum;\n    int\t\trtn;\n    sector_t*\tsec;\n    ceiling_t*\tceiling;\n\t\n    secnum = -1;\n    rtn = 0;\n    \n    //\tReactivate in-stasis ceilings...for certain types.\n    switch(type)\n    {\n      case fastCrushAndRaise:\n      case silentCrushAndRaise:\n      case crushAndRaise:\n\tP_ActivateInStasisCeiling(line);\n      default:\n\tbreak;\n    }\n\t\n    while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)\n    {\n\tsec = &sectors[secnum];\n\tif (sec->specialdata)\n\t    continue;\n\t\n\t// new door thinker\n\trtn = 1;\n\tceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0);\n\tP_AddThinker (&ceiling->thinker);\n\tsec->specialdata = ceiling;\n\tceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling;\n\tceiling->sector = sec;\n\tceiling->crush = false;\n\t\n\tswitch(type)\n\t{\n\t  case fastCrushAndRaise:\n\t    ceiling->crush = true;\n\t    ceiling->topheight = sec->ceilingheight;\n\t    ceiling->bottomheight = sec->floorheight + (8*FRACUNIT);\n\t    ceiling->direction = -1;\n\t    ceiling->speed = CEILSPEED * 2;\n\t    break;\n\n\t  case silentCrushAndRaise:\n\t  case crushAndRaise:\n\t    ceiling->crush = true;\n\t    ceiling->topheight = sec->ceilingheight;\n\t  case lowerAndCrush:\n\t  case lowerToFloor:\n\t    ceiling->bottomheight = sec->floorheight;\n\t    if (type != lowerToFloor)\n\t\tceiling->bottomheight += 8*FRACUNIT;\n\t    ceiling->direction = -1;\n\t    ceiling->speed = CEILSPEED;\n\t    break;\n\n\t  case raiseToHighest:\n\t    ceiling->topheight = P_FindHighestCeilingSurrounding(sec);\n\t    ceiling->direction = 1;\n\t    ceiling->speed = CEILSPEED;\n\t    break;\n\t}\n\t\t\n\tceiling->tag = sec->tag;\n\tceiling->type = type;\n\tP_AddActiveCeiling(ceiling);\n    }\n    return rtn;\n}\n\n\n//\n// Add an active ceiling\n//\nvoid P_AddActiveCeiling(ceiling_t* c)\n{\n    int\t\ti;\n    \n    for (i = 0; i < MAXCEILINGS;i++)\n    {\n\tif (activeceilings[i] == NULL)\n\t{\n\t    activeceilings[i] = c;\n\t    return;\n\t}\n    }\n}\n\n\n\n//\n// Remove a ceiling's thinker\n//\nvoid P_RemoveActiveCeiling(ceiling_t* c)\n{\n    int\t\ti;\n\t\n    for (i = 0;i < MAXCEILINGS;i++)\n    {\n\tif (activeceilings[i] == c)\n\t{\n\t    activeceilings[i]->sector->specialdata = NULL;\n\t    P_RemoveThinker (&activeceilings[i]->thinker);\n\t    activeceilings[i] = NULL;\n\t    break;\n\t}\n    }\n}\n\n\n\n//\n// Restart a ceiling that's in-stasis\n//\nvoid P_ActivateInStasisCeiling(line_t* line)\n{\n    int\t\ti;\n\t\n    for (i = 0;i < MAXCEILINGS;i++)\n    {\n\tif (activeceilings[i]\n\t    && (activeceilings[i]->tag == line->tag)\n\t    && (activeceilings[i]->direction == 0))\n\t{\n\t    activeceilings[i]->direction = activeceilings[i]->olddirection;\n\t    activeceilings[i]->thinker.function.acp1\n\t      = (actionf_p1)T_MoveCeiling;\n\t}\n    }\n}\n\n\n\n//\n// EV_CeilingCrushStop\n// Stop a ceiling from crushing!\n//\nint\tEV_CeilingCrushStop(line_t\t*line)\n{\n    int\t\ti;\n    int\t\trtn;\n\t\n    rtn = 0;\n    for (i = 0;i < MAXCEILINGS;i++)\n    {\n\tif (activeceilings[i]\n\t    && (activeceilings[i]->tag == line->tag)\n\t    && (activeceilings[i]->direction != 0))\n\t{\n\t    activeceilings[i]->olddirection = activeceilings[i]->direction;\n\t    activeceilings[i]->thinker.function.acv = (actionf_v)NULL;\n\t    activeceilings[i]->direction = 0;\t\t// in-stasis\n\t    rtn = 1;\n\t}\n    }\n    \n\n    return rtn;\n}\n"
  },
  {
    "path": "fbdoom/p_doors.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION: Door animation code (opening/closing)\n//\n\n\n\n#include \"z_zone.h\"\n#include \"doomdef.h\"\n#include \"deh_main.h\"\n#include \"p_local.h\"\n\n#include \"s_sound.h\"\n\n\n// State.\n#include \"doomstat.h\"\n#include \"r_state.h\"\n\n// Data.\n#include \"dstrings.h\"\n#include \"sounds.h\"\n\n#if 0\n//\n// Sliding door frame information\n//\nslidename_t\tslideFrameNames[MAXSLIDEDOORS] =\n{\n    {\"GDOORF1\",\"GDOORF2\",\"GDOORF3\",\"GDOORF4\",\t// front\n     \"GDOORB1\",\"GDOORB2\",\"GDOORB3\",\"GDOORB4\"},\t// back\n\t \n    {\"\\0\",\"\\0\",\"\\0\",\"\\0\"}\n};\n#endif\n\n\n//\n// VERTICAL DOORS\n//\n\n//\n// T_VerticalDoor\n//\nvoid T_VerticalDoor (vldoor_t* door)\n{\n    result_e\tres;\n\t\n    switch(door->direction)\n    {\n      case 0:\n\t// WAITING\n\tif (!--door->topcountdown)\n\t{\n\t    switch(door->type)\n\t    {\n\t      case vld_blazeRaise:\n\t\tdoor->direction = -1; // time to go back down\n\t\tS_StartSound(&door->sector->soundorg, sfx_bdcls);\n\t\tbreak;\n\t\t\n\t      case vld_normal:\n\t\tdoor->direction = -1; // time to go back down\n\t\tS_StartSound(&door->sector->soundorg, sfx_dorcls);\n\t\tbreak;\n\t\t\n\t      case vld_close30ThenOpen:\n\t\tdoor->direction = 1;\n\t\tS_StartSound(&door->sector->soundorg, sfx_doropn);\n\t\tbreak;\n\t\t\n\t      default:\n\t\tbreak;\n\t    }\n\t}\n\tbreak;\n\t\n      case 2:\n\t//  INITIAL WAIT\n\tif (!--door->topcountdown)\n\t{\n\t    switch(door->type)\n\t    {\n\t      case vld_raiseIn5Mins:\n\t\tdoor->direction = 1;\n\t\tdoor->type = vld_normal;\n\t\tS_StartSound(&door->sector->soundorg, sfx_doropn);\n\t\tbreak;\n\t\t\n\t      default:\n\t\tbreak;\n\t    }\n\t}\n\tbreak;\n\t\n      case -1:\n\t// DOWN\n\tres = T_MovePlane(door->sector,\n\t\t\t  door->speed,\n\t\t\t  door->sector->floorheight,\n\t\t\t  false,1,door->direction);\n\tif (res == pastdest)\n\t{\n\t    switch(door->type)\n\t    {\n\t      case vld_blazeRaise:\n\t      case vld_blazeClose:\n\t\tdoor->sector->specialdata = NULL;\n\t\tP_RemoveThinker (&door->thinker);  // unlink and free\n\t\tS_StartSound(&door->sector->soundorg, sfx_bdcls);\n\t\tbreak;\n\t\t\n\t      case vld_normal:\n\t      case vld_close:\n\t\tdoor->sector->specialdata = NULL;\n\t\tP_RemoveThinker (&door->thinker);  // unlink and free\n\t\tbreak;\n\t\t\n\t      case vld_close30ThenOpen:\n\t\tdoor->direction = 0;\n\t\tdoor->topcountdown = TICRATE*30;\n\t\tbreak;\n\t\t\n\t      default:\n\t\tbreak;\n\t    }\n\t}\n\telse if (res == crushed)\n\t{\n\t    switch(door->type)\n\t    {\n\t      case vld_blazeClose:\n\t      case vld_close:\t\t// DO NOT GO BACK UP!\n\t\tbreak;\n\t\t\n\t      default:\n\t\tdoor->direction = 1;\n\t\tS_StartSound(&door->sector->soundorg, sfx_doropn);\n\t\tbreak;\n\t    }\n\t}\n\tbreak;\n\t\n      case 1:\n\t// UP\n\tres = T_MovePlane(door->sector,\n\t\t\t  door->speed,\n\t\t\t  door->topheight,\n\t\t\t  false,1,door->direction);\n\t\n\tif (res == pastdest)\n\t{\n\t    switch(door->type)\n\t    {\n\t      case vld_blazeRaise:\n\t      case vld_normal:\n\t\tdoor->direction = 0; // wait at top\n\t\tdoor->topcountdown = door->topwait;\n\t\tbreak;\n\t\t\n\t      case vld_close30ThenOpen:\n\t      case vld_blazeOpen:\n\t      case vld_open:\n\t\tdoor->sector->specialdata = NULL;\n\t\tP_RemoveThinker (&door->thinker);  // unlink and free\n\t\tbreak;\n\t\t\n\t      default:\n\t\tbreak;\n\t    }\n\t}\n\tbreak;\n    }\n}\n\n\n//\n// EV_DoLockedDoor\n// Move a locked door up/down\n//\n\nint\nEV_DoLockedDoor\n( line_t*\tline,\n  vldoor_e\ttype,\n  mobj_t*\tthing )\n{\n    player_t*\tp;\n\t\n    p = thing->player;\n\t\n    if (!p)\n\treturn 0;\n\t\t\n    switch(line->special)\n    {\n      case 99:\t// Blue Lock\n      case 133:\n\tif ( !p )\n\t    return 0;\n\tif (!p->cards[it_bluecard] && !p->cards[it_blueskull])\n\t{\n\t    p->message = DEH_String(PD_BLUEO);\n\t    S_StartSound(NULL,sfx_oof);\n\t    return 0;\n\t}\n\tbreak;\n\t\n      case 134: // Red Lock\n      case 135:\n\tif ( !p )\n\t    return 0;\n\tif (!p->cards[it_redcard] && !p->cards[it_redskull])\n\t{\n\t    p->message = DEH_String(PD_REDO);\n\t    S_StartSound(NULL,sfx_oof);\n\t    return 0;\n\t}\n\tbreak;\n\t\n      case 136:\t// Yellow Lock\n      case 137:\n\tif ( !p )\n\t    return 0;\n\tif (!p->cards[it_yellowcard] &&\n\t    !p->cards[it_yellowskull])\n\t{\n\t    p->message = DEH_String(PD_YELLOWO);\n\t    S_StartSound(NULL,sfx_oof);\n\t    return 0;\n\t}\n\tbreak;\t\n    }\n\n    return EV_DoDoor(line,type);\n}\n\n\nint\nEV_DoDoor\n( line_t*\tline,\n  vldoor_e\ttype )\n{\n    int\t\tsecnum,rtn;\n    sector_t*\tsec;\n    vldoor_t*\tdoor;\n\t\n    secnum = -1;\n    rtn = 0;\n    \n    while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)\n    {\n\tsec = &sectors[secnum];\n\tif (sec->specialdata)\n\t    continue;\n\t\t\n\t\n\t// new door thinker\n\trtn = 1;\n\tdoor = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0);\n\tP_AddThinker (&door->thinker);\n\tsec->specialdata = door;\n\n\tdoor->thinker.function.acp1 = (actionf_p1) T_VerticalDoor;\n\tdoor->sector = sec;\n\tdoor->type = type;\n\tdoor->topwait = VDOORWAIT;\n\tdoor->speed = VDOORSPEED;\n\t\t\n\tswitch(type)\n\t{\n\t  case vld_blazeClose:\n\t    door->topheight = P_FindLowestCeilingSurrounding(sec);\n\t    door->topheight -= 4*FRACUNIT;\n\t    door->direction = -1;\n\t    door->speed = VDOORSPEED * 4;\n\t    S_StartSound(&door->sector->soundorg, sfx_bdcls);\n\t    break;\n\t    \n\t  case vld_close:\n\t    door->topheight = P_FindLowestCeilingSurrounding(sec);\n\t    door->topheight -= 4*FRACUNIT;\n\t    door->direction = -1;\n\t    S_StartSound(&door->sector->soundorg, sfx_dorcls);\n\t    break;\n\t    \n\t  case vld_close30ThenOpen:\n\t    door->topheight = sec->ceilingheight;\n\t    door->direction = -1;\n\t    S_StartSound(&door->sector->soundorg, sfx_dorcls);\n\t    break;\n\t    \n\t  case vld_blazeRaise:\n\t  case vld_blazeOpen:\n\t    door->direction = 1;\n\t    door->topheight = P_FindLowestCeilingSurrounding(sec);\n\t    door->topheight -= 4*FRACUNIT;\n\t    door->speed = VDOORSPEED * 4;\n\t    if (door->topheight != sec->ceilingheight)\n\t\tS_StartSound(&door->sector->soundorg, sfx_bdopn);\n\t    break;\n\t    \n\t  case vld_normal:\n\t  case vld_open:\n\t    door->direction = 1;\n\t    door->topheight = P_FindLowestCeilingSurrounding(sec);\n\t    door->topheight -= 4*FRACUNIT;\n\t    if (door->topheight != sec->ceilingheight)\n\t\tS_StartSound(&door->sector->soundorg, sfx_doropn);\n\t    break;\n\t    \n\t  default:\n\t    break;\n\t}\n\t\t\n    }\n    return rtn;\n}\n\n\n//\n// EV_VerticalDoor : open a door manually, no tag value\n//\nvoid\nEV_VerticalDoor\n( line_t*\tline,\n  mobj_t*\tthing )\n{\n    player_t*\tplayer;\n    sector_t*\tsec;\n    vldoor_t*\tdoor;\n    int\t\tside;\n\t\n    side = 0;\t// only front sides can be used\n\n    //\tCheck for locks\n    player = thing->player;\n\t\t\n    switch(line->special)\n    {\n      case 26: // Blue Lock\n      case 32:\n\tif ( !player )\n\t    return;\n\t\n\tif (!player->cards[it_bluecard] && !player->cards[it_blueskull])\n\t{\n\t    player->message = DEH_String(PD_BLUEK);\n\t    S_StartSound(NULL,sfx_oof);\n\t    return;\n\t}\n\tbreak;\n\t\n      case 27: // Yellow Lock\n      case 34:\n\tif ( !player )\n\t    return;\n\t\n\tif (!player->cards[it_yellowcard] &&\n\t    !player->cards[it_yellowskull])\n\t{\n\t    player->message = DEH_String(PD_YELLOWK);\n\t    S_StartSound(NULL,sfx_oof);\n\t    return;\n\t}\n\tbreak;\n\t\n      case 28: // Red Lock\n      case 33:\n\tif ( !player )\n\t    return;\n\t\n\tif (!player->cards[it_redcard] && !player->cards[it_redskull])\n\t{\n\t    player->message = DEH_String(PD_REDK);\n\t    S_StartSound(NULL,sfx_oof);\n\t    return;\n\t}\n\tbreak;\n    }\n\t\n    // if the sector has an active thinker, use it\n    sec = sides[ line->sidenum[side^1]] .sector;\n\n    if (sec->specialdata)\n    {\n\tdoor = sec->specialdata;\n\tswitch(line->special)\n\t{\n\t  case\t1: // ONLY FOR \"RAISE\" DOORS, NOT \"OPEN\"s\n\t  case\t26:\n\t  case\t27:\n\t  case\t28:\n\t  case\t117:\n\t    if (door->direction == -1)\n\t\tdoor->direction = 1;\t// go back up\n\t    else\n\t    {\n\t\tif (!thing->player)\n\t\t    return;\t\t// JDC: bad guys never close doors\n\n                // When is a door not a door?\n                // In Vanilla, door->direction is set, even though\n                // \"specialdata\" might not actually point at a door.\n\n                if (door->thinker.function.acp1 == (actionf_p1) T_VerticalDoor)\n                {\n                    door->direction = -1;\t// start going down immediately\n                }\n                else if (door->thinker.function.acp1 == (actionf_p1) T_PlatRaise)\n                {\n                    // Erm, this is a plat, not a door.\n                    // This notably causes a problem in ep1-0500.lmp where\n                    // a plat and a door are cross-referenced; the door\n                    // doesn't open on 64-bit.\n                    // The direction field in vldoor_t corresponds to the wait\n                    // field in plat_t.  Let's set that to -1 instead.\n\n                    plat_t *plat;\n\n                    plat = (plat_t *) door;\n                    plat->wait = -1;\n                }\n                else\n                {\n                    // This isn't a door OR a plat.  Now we're in trouble.\n\n                    fprintf(stderr, \"EV_VerticalDoor: Tried to close \"\n                                    \"something that wasn't a door.\\n\");\n\n                    // Try closing it anyway. At least it will work on 32-bit\n                    // machines.\n\n                    door->direction = -1;\n                }\n\t    }\n\t    return;\n\t}\n    }\n\t\n    // for proper sound\n    switch(line->special)\n    {\n      case 117:\t// BLAZING DOOR RAISE\n      case 118:\t// BLAZING DOOR OPEN\n\tS_StartSound(&sec->soundorg,sfx_bdopn);\n\tbreak;\n\t\n      case 1:\t// NORMAL DOOR SOUND\n      case 31:\n\tS_StartSound(&sec->soundorg,sfx_doropn);\n\tbreak;\n\t\n      default:\t// LOCKED DOOR SOUND\n\tS_StartSound(&sec->soundorg,sfx_doropn);\n\tbreak;\n    }\n\t\n    \n    // new door thinker\n    door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0);\n    P_AddThinker (&door->thinker);\n    sec->specialdata = door;\n    door->thinker.function.acp1 = (actionf_p1) T_VerticalDoor;\n    door->sector = sec;\n    door->direction = 1;\n    door->speed = VDOORSPEED;\n    door->topwait = VDOORWAIT;\n\n    switch(line->special)\n    {\n      case 1:\n      case 26:\n      case 27:\n      case 28:\n\tdoor->type = vld_normal;\n\tbreak;\n\t\n      case 31:\n      case 32:\n      case 33:\n      case 34:\n\tdoor->type = vld_open;\n\tline->special = 0;\n\tbreak;\n\t\n      case 117:\t// blazing door raise\n\tdoor->type = vld_blazeRaise;\n\tdoor->speed = VDOORSPEED*4;\n\tbreak;\n      case 118:\t// blazing door open\n\tdoor->type = vld_blazeOpen;\n\tline->special = 0;\n\tdoor->speed = VDOORSPEED*4;\n\tbreak;\n    }\n    \n    // find the top and bottom of the movement range\n    door->topheight = P_FindLowestCeilingSurrounding(sec);\n    door->topheight -= 4*FRACUNIT;\n}\n\n\n//\n// Spawn a door that closes after 30 seconds\n//\nvoid P_SpawnDoorCloseIn30 (sector_t* sec)\n{\n    vldoor_t*\tdoor;\n\t\n    door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0);\n\n    P_AddThinker (&door->thinker);\n\n    sec->specialdata = door;\n    sec->special = 0;\n\n    door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor;\n    door->sector = sec;\n    door->direction = 0;\n    door->type = vld_normal;\n    door->speed = VDOORSPEED;\n    door->topcountdown = 30 * TICRATE;\n}\n\n//\n// Spawn a door that opens after 5 minutes\n//\nvoid\nP_SpawnDoorRaiseIn5Mins\n( sector_t*\tsec,\n  int\t\tsecnum )\n{\n    vldoor_t*\tdoor;\n\t\n    door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0);\n    \n    P_AddThinker (&door->thinker);\n\n    sec->specialdata = door;\n    sec->special = 0;\n\n    door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor;\n    door->sector = sec;\n    door->direction = 2;\n    door->type = vld_raiseIn5Mins;\n    door->speed = VDOORSPEED;\n    door->topheight = P_FindLowestCeilingSurrounding(sec);\n    door->topheight -= 4*FRACUNIT;\n    door->topwait = VDOORWAIT;\n    door->topcountdown = 5 * 60 * TICRATE;\n}\n\n\n\n// UNUSED\n// Separate into p_slidoor.c?\n\n#if 0\t\t// ABANDONED TO THE MISTS OF TIME!!!\n//\n// EV_SlidingDoor : slide a door horizontally\n// (animate midtexture, then set noblocking line)\n//\n\n\nslideframe_t slideFrames[MAXSLIDEDOORS];\n\nvoid P_InitSlidingDoorFrames(void)\n{\n    int\t\ti;\n    int\t\tf1;\n    int\t\tf2;\n    int\t\tf3;\n    int\t\tf4;\n\t\n    // DOOM II ONLY...\n    if ( gamemode != commercial)\n\treturn;\n\t\n    for (i = 0;i < MAXSLIDEDOORS; i++)\n    {\n\tif (!slideFrameNames[i].frontFrame1[0])\n\t    break;\n\t\t\t\n\tf1 = R_TextureNumForName(slideFrameNames[i].frontFrame1);\n\tf2 = R_TextureNumForName(slideFrameNames[i].frontFrame2);\n\tf3 = R_TextureNumForName(slideFrameNames[i].frontFrame3);\n\tf4 = R_TextureNumForName(slideFrameNames[i].frontFrame4);\n\n\tslideFrames[i].frontFrames[0] = f1;\n\tslideFrames[i].frontFrames[1] = f2;\n\tslideFrames[i].frontFrames[2] = f3;\n\tslideFrames[i].frontFrames[3] = f4;\n\t\t\n\tf1 = R_TextureNumForName(slideFrameNames[i].backFrame1);\n\tf2 = R_TextureNumForName(slideFrameNames[i].backFrame2);\n\tf3 = R_TextureNumForName(slideFrameNames[i].backFrame3);\n\tf4 = R_TextureNumForName(slideFrameNames[i].backFrame4);\n\n\tslideFrames[i].backFrames[0] = f1;\n\tslideFrames[i].backFrames[1] = f2;\n\tslideFrames[i].backFrames[2] = f3;\n\tslideFrames[i].backFrames[3] = f4;\n    }\n}\n\n\n//\n// Return index into \"slideFrames\" array\n// for which door type to use\n//\nint P_FindSlidingDoorType(line_t*\tline)\n{\n    int\t\ti;\n    int\t\tval;\n\t\n    for (i = 0;i < MAXSLIDEDOORS;i++)\n    {\n\tval = sides[line->sidenum[0]].midtexture;\n\tif (val == slideFrames[i].frontFrames[0])\n\t    return i;\n    }\n\t\n    return -1;\n}\n\nvoid T_SlidingDoor (slidedoor_t*\tdoor)\n{\n    switch(door->status)\n    {\n      case sd_opening:\n\tif (!door->timer--)\n\t{\n\t    if (++door->frame == SNUMFRAMES)\n\t    {\n\t\t// IF DOOR IS DONE OPENING...\n\t\tsides[door->line->sidenum[0]].midtexture = 0;\n\t\tsides[door->line->sidenum[1]].midtexture = 0;\n\t\tdoor->line->flags &= ML_BLOCKING^0xff;\n\t\t\t\t\t\n\t\tif (door->type == sdt_openOnly)\n\t\t{\n\t\t    door->frontsector->specialdata = NULL;\n\t\t    P_RemoveThinker (&door->thinker);\n\t\t    break;\n\t\t}\n\t\t\t\t\t\n\t\tdoor->timer = SDOORWAIT;\n\t\tdoor->status = sd_waiting;\n\t    }\n\t    else\n\t    {\n\t\t// IF DOOR NEEDS TO ANIMATE TO NEXT FRAME...\n\t\tdoor->timer = SWAITTICS;\n\t\t\t\t\t\n\t\tsides[door->line->sidenum[0]].midtexture =\n\t\t    slideFrames[door->whichDoorIndex].\n\t\t    frontFrames[door->frame];\n\t\tsides[door->line->sidenum[1]].midtexture =\n\t\t    slideFrames[door->whichDoorIndex].\n\t\t    backFrames[door->frame];\n\t    }\n\t}\n\tbreak;\n\t\t\t\n      case sd_waiting:\n\t// IF DOOR IS DONE WAITING...\n\tif (!door->timer--)\n\t{\n\t    // CAN DOOR CLOSE?\n\t    if (door->frontsector->thinglist != NULL ||\n\t\tdoor->backsector->thinglist != NULL)\n\t    {\n\t\tdoor->timer = SDOORWAIT;\n\t\tbreak;\n\t    }\n\n\t    //door->frame = SNUMFRAMES-1;\n\t    door->status = sd_closing;\n\t    door->timer = SWAITTICS;\n\t}\n\tbreak;\n\t\t\t\n      case sd_closing:\n\tif (!door->timer--)\n\t{\n\t    if (--door->frame < 0)\n\t    {\n\t\t// IF DOOR IS DONE CLOSING...\n\t\tdoor->line->flags |= ML_BLOCKING;\n\t\tdoor->frontsector->specialdata = NULL;\n\t\tP_RemoveThinker (&door->thinker);\n\t\tbreak;\n\t    }\n\t    else\n\t    {\n\t\t// IF DOOR NEEDS TO ANIMATE TO NEXT FRAME...\n\t\tdoor->timer = SWAITTICS;\n\t\t\t\t\t\n\t\tsides[door->line->sidenum[0]].midtexture =\n\t\t    slideFrames[door->whichDoorIndex].\n\t\t    frontFrames[door->frame];\n\t\tsides[door->line->sidenum[1]].midtexture =\n\t\t    slideFrames[door->whichDoorIndex].\n\t\t    backFrames[door->frame];\n\t    }\n\t}\n\tbreak;\n    }\n}\n\n\n\nvoid\nEV_SlidingDoor\n( line_t*\tline,\n  mobj_t*\tthing )\n{\n    sector_t*\t\tsec;\n    slidedoor_t*\tdoor;\n\t\n    // DOOM II ONLY...\n    if (gamemode != commercial)\n\treturn;\n    \n    // Make sure door isn't already being animated\n    sec = line->frontsector;\n    door = NULL;\n    if (sec->specialdata)\n    {\n\tif (!thing->player)\n\t    return;\n\t\t\t\n\tdoor = sec->specialdata;\n\tif (door->type == sdt_openAndClose)\n\t{\n\t    if (door->status == sd_waiting)\n\t\tdoor->status = sd_closing;\n\t}\n\telse\n\t    return;\n    }\n    \n    // Init sliding door vars\n    if (!door)\n    {\n\tdoor = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0);\n\tP_AddThinker (&door->thinker);\n\tsec->specialdata = door;\n\t\t\n\tdoor->type = sdt_openAndClose;\n\tdoor->status = sd_opening;\n\tdoor->whichDoorIndex = P_FindSlidingDoorType(line);\n\n\tif (door->whichDoorIndex < 0)\n\t    I_Error(\"EV_SlidingDoor: Can't use texture for sliding door!\");\n\t\t\t\n\tdoor->frontsector = sec;\n\tdoor->backsector = line->backsector;\n\tdoor->thinker.function = T_SlidingDoor;\n\tdoor->timer = SWAITTICS;\n\tdoor->frame = 0;\n\tdoor->line = line;\n    }\n}\n#endif\n"
  },
  {
    "path": "fbdoom/p_enemy.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tEnemy thinking, AI.\n//\tAction Pointer Functions\n//\tthat are associated with states/frames. \n//\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"m_random.h\"\n#include \"i_system.h\"\n\n#include \"doomdef.h\"\n#include \"p_local.h\"\n\n#include \"s_sound.h\"\n\n#include \"g_game.h\"\n\n// State.\n#include \"doomstat.h\"\n#include \"r_state.h\"\n\n// Data.\n#include \"sounds.h\"\n\n\n\n\ntypedef enum\n{\n    DI_EAST,\n    DI_NORTHEAST,\n    DI_NORTH,\n    DI_NORTHWEST,\n    DI_WEST,\n    DI_SOUTHWEST,\n    DI_SOUTH,\n    DI_SOUTHEAST,\n    DI_NODIR,\n    NUMDIRS\n    \n} dirtype_t;\n\n\n//\n// P_NewChaseDir related LUT.\n//\ndirtype_t opposite[] =\n{\n  DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST,\n  DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR\n};\n\ndirtype_t diags[] =\n{\n    DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST\n};\n\n\n\n\n\nvoid A_Fall (mobj_t *actor);\n\n\n//\n// ENEMY THINKING\n// Enemies are allways spawned\n// with targetplayer = -1, threshold = 0\n// Most monsters are spawned unaware of all players,\n// but some can be made preaware\n//\n\n\n//\n// Called by P_NoiseAlert.\n// Recursively traverse adjacent sectors,\n// sound blocking lines cut off traversal.\n//\n\nmobj_t*\t\tsoundtarget;\n\nvoid\nP_RecursiveSound\n( sector_t*\tsec,\n  int\t\tsoundblocks )\n{\n    int\t\ti;\n    line_t*\tcheck;\n    sector_t*\tother;\n\t\n    // wake up all monsters in this sector\n    if (sec->validcount == validcount\n\t&& sec->soundtraversed <= soundblocks+1)\n    {\n\treturn;\t\t// already flooded\n    }\n    \n    sec->validcount = validcount;\n    sec->soundtraversed = soundblocks+1;\n    sec->soundtarget = soundtarget;\n\t\n    for (i=0 ;i<sec->linecount ; i++)\n    {\n\tcheck = sec->lines[i];\n\tif (! (check->flags & ML_TWOSIDED) )\n\t    continue;\n\t\n\tP_LineOpening (check);\n\n\tif (openrange <= 0)\n\t    continue;\t// closed door\n\t\n\tif ( sides[ check->sidenum[0] ].sector == sec)\n\t    other = sides[ check->sidenum[1] ] .sector;\n\telse\n\t    other = sides[ check->sidenum[0] ].sector;\n\t\n\tif (check->flags & ML_SOUNDBLOCK)\n\t{\n\t    if (!soundblocks)\n\t\tP_RecursiveSound (other, 1);\n\t}\n\telse\n\t    P_RecursiveSound (other, soundblocks);\n    }\n}\n\n\n\n//\n// P_NoiseAlert\n// If a monster yells at a player,\n// it will alert other monsters to the player.\n//\nvoid\nP_NoiseAlert\n( mobj_t*\ttarget,\n  mobj_t*\temmiter )\n{\n    soundtarget = target;\n    validcount++;\n    P_RecursiveSound (emmiter->subsector->sector, 0);\n}\n\n\n\n\n//\n// P_CheckMeleeRange\n//\nboolean P_CheckMeleeRange (mobj_t*\tactor)\n{\n    mobj_t*\tpl;\n    fixed_t\tdist;\n\t\n    if (!actor->target)\n\treturn false;\n\t\t\n    pl = actor->target;\n    dist = P_AproxDistance (pl->x-actor->x, pl->y-actor->y);\n\n    if (dist >= MELEERANGE-20*FRACUNIT+pl->info->radius)\n\treturn false;\n\t\n    if (! P_CheckSight (actor, actor->target) )\n\treturn false;\n\t\t\t\t\t\t\t\n    return true;\t\t\n}\n\n//\n// P_CheckMissileRange\n//\nboolean P_CheckMissileRange (mobj_t* actor)\n{\n    fixed_t\tdist;\n\t\n    if (! P_CheckSight (actor, actor->target) )\n\treturn false;\n\t\n    if ( actor->flags & MF_JUSTHIT )\n    {\n\t// the target just hit the enemy,\n\t// so fight back!\n\tactor->flags &= ~MF_JUSTHIT;\n\treturn true;\n    }\n\t\n    if (actor->reactiontime)\n\treturn false;\t// do not attack yet\n\t\t\n    // OPTIMIZE: get this from a global checksight\n    dist = P_AproxDistance ( actor->x-actor->target->x,\n\t\t\t     actor->y-actor->target->y) - 64*FRACUNIT;\n    \n    if (!actor->info->meleestate)\n\tdist -= 128*FRACUNIT;\t// no melee attack, so fire more\n\n    dist >>= 16;\n\n    if (actor->type == MT_VILE)\n    {\n\tif (dist > 14*64)\t\n\t    return false;\t// too far away\n    }\n\t\n\n    if (actor->type == MT_UNDEAD)\n    {\n\tif (dist < 196)\t\n\t    return false;\t// close for fist attack\n\tdist >>= 1;\n    }\n\t\n\n    if (actor->type == MT_CYBORG\n\t|| actor->type == MT_SPIDER\n\t|| actor->type == MT_SKULL)\n    {\n\tdist >>= 1;\n    }\n    \n    if (dist > 200)\n\tdist = 200;\n\t\t\n    if (actor->type == MT_CYBORG && dist > 160)\n\tdist = 160;\n\t\t\n    if (P_Random () < dist)\n\treturn false;\n\t\t\n    return true;\n}\n\n\n//\n// P_Move\n// Move in the current direction,\n// returns false if the move is blocked.\n//\nfixed_t\txspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000};\nfixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000};\n\nboolean P_Move (mobj_t*\tactor)\n{\n    fixed_t\ttryx;\n    fixed_t\ttryy;\n    \n    line_t*\tld;\n    \n    // warning: 'catch', 'throw', and 'try'\n    // are all C++ reserved words\n    boolean\ttry_ok;\n    boolean\tgood;\n\t\t\n    if (actor->movedir == DI_NODIR)\n\treturn false;\n\t\t\n    if ((unsigned)actor->movedir >= 8)\n\tI_Error (\"Weird actor->movedir!\");\n\t\t\n    tryx = actor->x + actor->info->speed*xspeed[actor->movedir];\n    tryy = actor->y + actor->info->speed*yspeed[actor->movedir];\n\n    try_ok = P_TryMove (actor, tryx, tryy);\n\n    if (!try_ok)\n    {\n\t// open any specials\n\tif (actor->flags & MF_FLOAT && floatok)\n\t{\n\t    // must adjust height\n\t    if (actor->z < tmfloorz)\n\t\tactor->z += FLOATSPEED;\n\t    else\n\t\tactor->z -= FLOATSPEED;\n\n\t    actor->flags |= MF_INFLOAT;\n\t    return true;\n\t}\n\t\t\n\tif (!numspechit)\n\t    return false;\n\t\t\t\n\tactor->movedir = DI_NODIR;\n\tgood = false;\n\twhile (numspechit--)\n\t{\n\t    ld = spechit[numspechit];\n\t    // if the special is not a door\n\t    // that can be opened,\n\t    // return false\n\t    if (P_UseSpecialLine (actor, ld,0))\n\t\tgood = true;\n\t}\n\treturn good;\n    }\n    else\n    {\n\tactor->flags &= ~MF_INFLOAT;\n    }\n\t\n\t\n    if (! (actor->flags & MF_FLOAT) )\t\n\tactor->z = actor->floorz;\n    return true; \n}\n\n\n//\n// TryWalk\n// Attempts to move actor on\n// in its current (ob->moveangle) direction.\n// If blocked by either a wall or an actor\n// returns FALSE\n// If move is either clear or blocked only by a door,\n// returns TRUE and sets...\n// If a door is in the way,\n// an OpenDoor call is made to start it opening.\n//\nboolean P_TryWalk (mobj_t* actor)\n{\t\n    if (!P_Move (actor))\n    {\n\treturn false;\n    }\n\n    actor->movecount = P_Random()&15;\n    return true;\n}\n\n\n\n\nvoid P_NewChaseDir (mobj_t*\tactor)\n{\n    fixed_t\tdeltax;\n    fixed_t\tdeltay;\n    \n    dirtype_t\td[3];\n    \n    int\t\ttdir;\n    dirtype_t\tolddir;\n    \n    dirtype_t\tturnaround;\n\n    if (!actor->target)\n\tI_Error (\"P_NewChaseDir: called with no target\");\n\t\t\n    olddir = actor->movedir;\n    turnaround=opposite[olddir];\n\n    deltax = actor->target->x - actor->x;\n    deltay = actor->target->y - actor->y;\n\n    if (deltax>10*FRACUNIT)\n\td[1]= DI_EAST;\n    else if (deltax<-10*FRACUNIT)\n\td[1]= DI_WEST;\n    else\n\td[1]=DI_NODIR;\n\n    if (deltay<-10*FRACUNIT)\n\td[2]= DI_SOUTH;\n    else if (deltay>10*FRACUNIT)\n\td[2]= DI_NORTH;\n    else\n\td[2]=DI_NODIR;\n\n    // try direct route\n    if (d[1] != DI_NODIR\n\t&& d[2] != DI_NODIR)\n    {\n\tactor->movedir = diags[((deltay<0)<<1)+(deltax>0)];\n\tif (actor->movedir != (int) turnaround && P_TryWalk(actor))\n\t    return;\n    }\n\n    // try other directions\n    if (P_Random() > 200\n\t||  abs(deltay)>abs(deltax))\n    {\n\ttdir=d[1];\n\td[1]=d[2];\n\td[2]=tdir;\n    }\n\n    if (d[1]==turnaround)\n\td[1]=DI_NODIR;\n    if (d[2]==turnaround)\n\td[2]=DI_NODIR;\n\t\n    if (d[1]!=DI_NODIR)\n    {\n\tactor->movedir = d[1];\n\tif (P_TryWalk(actor))\n\t{\n\t    // either moved forward or attacked\n\t    return;\n\t}\n    }\n\n    if (d[2]!=DI_NODIR)\n    {\n\tactor->movedir =d[2];\n\n\tif (P_TryWalk(actor))\n\t    return;\n    }\n\n    // there is no direct path to the player,\n    // so pick another direction.\n    if (olddir!=DI_NODIR)\n    {\n\tactor->movedir =olddir;\n\n\tif (P_TryWalk(actor))\n\t    return;\n    }\n\n    // randomly determine direction of search\n    if (P_Random()&1) \t\n    {\n\tfor ( tdir=DI_EAST;\n\t      tdir<=DI_SOUTHEAST;\n\t      tdir++ )\n\t{\n\t    if (tdir != (int) turnaround)\n\t    {\n\t\tactor->movedir =tdir;\n\t\t\n\t\tif ( P_TryWalk(actor) )\n\t\t    return;\n\t    }\n\t}\n    }\n    else\n    {\n\tfor ( tdir=DI_SOUTHEAST;\n\t      tdir != (DI_EAST-1);\n\t      tdir-- )\n\t{\n\t    if (tdir != (int) turnaround)\n\t    {\n\t\tactor->movedir = tdir;\n\t\t\n\t\tif ( P_TryWalk(actor) )\n\t\t    return;\n\t    }\n\t}\n    }\n\n    if (turnaround !=  DI_NODIR)\n    {\n\tactor->movedir =turnaround;\n\tif ( P_TryWalk(actor) )\n\t    return;\n    }\n\n    actor->movedir = DI_NODIR;\t// can not move\n}\n\n\n\n//\n// P_LookForPlayers\n// If allaround is false, only look 180 degrees in front.\n// Returns true if a player is targeted.\n//\nboolean\nP_LookForPlayers\n( mobj_t*\tactor,\n  boolean\tallaround )\n{\n    int\t\tc;\n    int\t\tstop;\n    player_t*\tplayer;\n    angle_t\tan;\n    fixed_t\tdist;\n\n    c = 0;\n    stop = (actor->lastlook-1)&3;\n\t\n    for ( ; ; actor->lastlook = (actor->lastlook+1)&3 )\n    {\n\tif (!playeringame[actor->lastlook])\n\t    continue;\n\t\t\t\n\tif (c++ == 2\n\t    || actor->lastlook == stop)\n\t{\n\t    // done looking\n\t    return false;\t\n\t}\n\t\n\tplayer = &players[actor->lastlook];\n\n\tif (player->health <= 0)\n\t    continue;\t\t// dead\n\n\tif (!P_CheckSight (actor, player->mo))\n\t    continue;\t\t// out of sight\n\t\t\t\n\tif (!allaround)\n\t{\n\t    an = R_PointToAngle2 (actor->x,\n\t\t\t\t  actor->y, \n\t\t\t\t  player->mo->x,\n\t\t\t\t  player->mo->y)\n\t\t- actor->angle;\n\t    \n\t    if (an > ANG90 && an < ANG270)\n\t    {\n\t\tdist = P_AproxDistance (player->mo->x - actor->x,\n\t\t\t\t\tplayer->mo->y - actor->y);\n\t\t// if real close, react anyway\n\t\tif (dist > MELEERANGE)\n\t\t    continue;\t// behind back\n\t    }\n\t}\n\t\t\n\tactor->target = player->mo;\n\treturn true;\n    }\n\n    return false;\n}\n\n\n//\n// A_KeenDie\n// DOOM II special, map 32.\n// Uses special tag 666.\n//\nvoid A_KeenDie (mobj_t* mo)\n{\n    thinker_t*\tth;\n    mobj_t*\tmo2;\n    line_t\tjunk;\n\n    A_Fall (mo);\n    \n    // scan the remaining thinkers\n    // to see if all Keens are dead\n    for (th = thinkercap.next ; th != &thinkercap ; th=th->next)\n    {\n\tif (th->function.acp1 != (actionf_p1)P_MobjThinker)\n\t    continue;\n\n\tmo2 = (mobj_t *)th;\n\tif (mo2 != mo\n\t    && mo2->type == mo->type\n\t    && mo2->health > 0)\n\t{\n\t    // other Keen not dead\n\t    return;\t\t\n\t}\n    }\n\n    junk.tag = 666;\n    EV_DoDoor(&junk, vld_open);\n}\n\n\n//\n// ACTION ROUTINES\n//\n\n//\n// A_Look\n// Stay in state until a player is sighted.\n//\nvoid A_Look (mobj_t* actor)\n{\n    mobj_t*\ttarg;\n\t\n    actor->threshold = 0;\t// any shot will wake up\n    targ = actor->subsector->sector->soundtarget;\n\n    if (targ\n\t&& (targ->flags & MF_SHOOTABLE) )\n    {\n\tactor->target = targ;\n\n\tif ( actor->flags & MF_AMBUSH )\n\t{\n\t    if (P_CheckSight (actor, actor->target))\n\t\tgoto seeyou;\n\t}\n\telse\n\t    goto seeyou;\n    }\n\t\n\t\n    if (!P_LookForPlayers (actor, false) )\n\treturn;\n\t\t\n    // go into chase state\n  seeyou:\n    if (actor->info->seesound)\n    {\n\tint\t\tsound;\n\t\t\n\tswitch (actor->info->seesound)\n\t{\n\t  case sfx_posit1:\n\t  case sfx_posit2:\n\t  case sfx_posit3:\n\t    sound = sfx_posit1+P_Random()%3;\n\t    break;\n\n\t  case sfx_bgsit1:\n\t  case sfx_bgsit2:\n\t    sound = sfx_bgsit1+P_Random()%2;\n\t    break;\n\n\t  default:\n\t    sound = actor->info->seesound;\n\t    break;\n\t}\n\n\tif (actor->type==MT_SPIDER\n\t    || actor->type == MT_CYBORG)\n\t{\n\t    // full volume\n\t    S_StartSound (NULL, sound);\n\t}\n\telse\n\t    S_StartSound (actor, sound);\n    }\n\n    P_SetMobjState (actor, actor->info->seestate);\n}\n\n\n//\n// A_Chase\n// Actor has a melee attack,\n// so it tries to close as fast as possible\n//\nvoid A_Chase (mobj_t*\tactor)\n{\n    int\t\tdelta;\n\n    if (actor->reactiontime)\n\tactor->reactiontime--;\n\t\t\t\t\n\n    // modify target threshold\n    if  (actor->threshold)\n    {\n\tif (!actor->target\n\t    || actor->target->health <= 0)\n\t{\n\t    actor->threshold = 0;\n\t}\n\telse\n\t    actor->threshold--;\n    }\n    \n    // turn towards movement direction if not there yet\n    if (actor->movedir < 8)\n    {\n\tactor->angle &= (7<<29);\n\tdelta = actor->angle - (actor->movedir << 29);\n\t\n\tif (delta > 0)\n\t    actor->angle -= ANG90/2;\n\telse if (delta < 0)\n\t    actor->angle += ANG90/2;\n    }\n\n    if (!actor->target\n\t|| !(actor->target->flags&MF_SHOOTABLE))\n    {\n\t// look for a new target\n\tif (P_LookForPlayers(actor,true))\n\t    return; \t// got a new target\n\t\n\tP_SetMobjState (actor, actor->info->spawnstate);\n\treturn;\n    }\n    \n    // do not attack twice in a row\n    if (actor->flags & MF_JUSTATTACKED)\n    {\n\tactor->flags &= ~MF_JUSTATTACKED;\n\tif (gameskill != sk_nightmare && !fastparm)\n\t    P_NewChaseDir (actor);\n\treturn;\n    }\n    \n    // check for melee attack\n    if (actor->info->meleestate\n\t&& P_CheckMeleeRange (actor))\n    {\n\tif (actor->info->attacksound)\n\t    S_StartSound (actor, actor->info->attacksound);\n\n\tP_SetMobjState (actor, actor->info->meleestate);\n\treturn;\n    }\n    \n    // check for missile attack\n    if (actor->info->missilestate)\n    {\n\tif (gameskill < sk_nightmare\n\t    && !fastparm && actor->movecount)\n\t{\n\t    goto nomissile;\n\t}\n\t\n\tif (!P_CheckMissileRange (actor))\n\t    goto nomissile;\n\t\n\tP_SetMobjState (actor, actor->info->missilestate);\n\tactor->flags |= MF_JUSTATTACKED;\n\treturn;\n    }\n\n    // ?\n  nomissile:\n    // possibly choose another target\n    if (netgame\n\t&& !actor->threshold\n\t&& !P_CheckSight (actor, actor->target) )\n    {\n\tif (P_LookForPlayers(actor,true))\n\t    return;\t// got a new target\n    }\n    \n    // chase towards player\n    if (--actor->movecount<0\n\t|| !P_Move (actor))\n    {\n\tP_NewChaseDir (actor);\n    }\n    \n    // make active sound\n    if (actor->info->activesound\n\t&& P_Random () < 3)\n    {\n\tS_StartSound (actor, actor->info->activesound);\n    }\n}\n\n\n//\n// A_FaceTarget\n//\nvoid A_FaceTarget (mobj_t* actor)\n{\t\n    if (!actor->target)\n\treturn;\n    \n    actor->flags &= ~MF_AMBUSH;\n\t\n    actor->angle = R_PointToAngle2 (actor->x,\n\t\t\t\t    actor->y,\n\t\t\t\t    actor->target->x,\n\t\t\t\t    actor->target->y);\n    \n    if (actor->target->flags & MF_SHADOW)\n\tactor->angle += (P_Random()-P_Random())<<21;\n}\n\n\n//\n// A_PosAttack\n//\nvoid A_PosAttack (mobj_t* actor)\n{\n    int\t\tangle;\n    int\t\tdamage;\n    int\t\tslope;\n\t\n    if (!actor->target)\n\treturn;\n\t\t\n    A_FaceTarget (actor);\n    angle = actor->angle;\n    slope = P_AimLineAttack (actor, angle, MISSILERANGE);\n\n    S_StartSound (actor, sfx_pistol);\n    angle += (P_Random()-P_Random())<<20;\n    damage = ((P_Random()%5)+1)*3;\n    P_LineAttack (actor, angle, MISSILERANGE, slope, damage);\n}\n\nvoid A_SPosAttack (mobj_t* actor)\n{\n    int\t\ti;\n    int\t\tangle;\n    int\t\tbangle;\n    int\t\tdamage;\n    int\t\tslope;\n\t\n    if (!actor->target)\n\treturn;\n\n    S_StartSound (actor, sfx_shotgn);\n    A_FaceTarget (actor);\n    bangle = actor->angle;\n    slope = P_AimLineAttack (actor, bangle, MISSILERANGE);\n\n    for (i=0 ; i<3 ; i++)\n    {\n\tangle = bangle + ((P_Random()-P_Random())<<20);\n\tdamage = ((P_Random()%5)+1)*3;\n\tP_LineAttack (actor, angle, MISSILERANGE, slope, damage);\n    }\n}\n\nvoid A_CPosAttack (mobj_t* actor)\n{\n    int\t\tangle;\n    int\t\tbangle;\n    int\t\tdamage;\n    int\t\tslope;\n\t\n    if (!actor->target)\n\treturn;\n\n    S_StartSound (actor, sfx_shotgn);\n    A_FaceTarget (actor);\n    bangle = actor->angle;\n    slope = P_AimLineAttack (actor, bangle, MISSILERANGE);\n\n    angle = bangle + ((P_Random()-P_Random())<<20);\n    damage = ((P_Random()%5)+1)*3;\n    P_LineAttack (actor, angle, MISSILERANGE, slope, damage);\n}\n\nvoid A_CPosRefire (mobj_t* actor)\n{\t\n    // keep firing unless target got out of sight\n    A_FaceTarget (actor);\n\n    if (P_Random () < 40)\n\treturn;\n\n    if (!actor->target\n\t|| actor->target->health <= 0\n\t|| !P_CheckSight (actor, actor->target) )\n    {\n\tP_SetMobjState (actor, actor->info->seestate);\n    }\n}\n\n\nvoid A_SpidRefire (mobj_t* actor)\n{\t\n    // keep firing unless target got out of sight\n    A_FaceTarget (actor);\n\n    if (P_Random () < 10)\n\treturn;\n\n    if (!actor->target\n\t|| actor->target->health <= 0\n\t|| !P_CheckSight (actor, actor->target) )\n    {\n\tP_SetMobjState (actor, actor->info->seestate);\n    }\n}\n\nvoid A_BspiAttack (mobj_t *actor)\n{\t\n    if (!actor->target)\n\treturn;\n\t\t\n    A_FaceTarget (actor);\n\n    // launch a missile\n    P_SpawnMissile (actor, actor->target, MT_ARACHPLAZ);\n}\n\n\n//\n// A_TroopAttack\n//\nvoid A_TroopAttack (mobj_t* actor)\n{\n    int\t\tdamage;\n\t\n    if (!actor->target)\n\treturn;\n\t\t\n    A_FaceTarget (actor);\n    if (P_CheckMeleeRange (actor))\n    {\n\tS_StartSound (actor, sfx_claw);\n\tdamage = (P_Random()%8+1)*3;\n\tP_DamageMobj (actor->target, actor, actor, damage);\n\treturn;\n    }\n\n    \n    // launch a missile\n    P_SpawnMissile (actor, actor->target, MT_TROOPSHOT);\n}\n\n\nvoid A_SargAttack (mobj_t* actor)\n{\n    int\t\tdamage;\n\n    if (!actor->target)\n\treturn;\n\t\t\n    A_FaceTarget (actor);\n    if (P_CheckMeleeRange (actor))\n    {\n\tdamage = ((P_Random()%10)+1)*4;\n\tP_DamageMobj (actor->target, actor, actor, damage);\n    }\n}\n\nvoid A_HeadAttack (mobj_t* actor)\n{\n    int\t\tdamage;\n\t\n    if (!actor->target)\n\treturn;\n\t\t\n    A_FaceTarget (actor);\n    if (P_CheckMeleeRange (actor))\n    {\n\tdamage = (P_Random()%6+1)*10;\n\tP_DamageMobj (actor->target, actor, actor, damage);\n\treturn;\n    }\n    \n    // launch a missile\n    P_SpawnMissile (actor, actor->target, MT_HEADSHOT);\n}\n\nvoid A_CyberAttack (mobj_t* actor)\n{\t\n    if (!actor->target)\n\treturn;\n\t\t\n    A_FaceTarget (actor);\n    P_SpawnMissile (actor, actor->target, MT_ROCKET);\n}\n\n\nvoid A_BruisAttack (mobj_t* actor)\n{\n    int\t\tdamage;\n\t\n    if (!actor->target)\n\treturn;\n\t\t\n    if (P_CheckMeleeRange (actor))\n    {\n\tS_StartSound (actor, sfx_claw);\n\tdamage = (P_Random()%8+1)*10;\n\tP_DamageMobj (actor->target, actor, actor, damage);\n\treturn;\n    }\n    \n    // launch a missile\n    P_SpawnMissile (actor, actor->target, MT_BRUISERSHOT);\n}\n\n\n//\n// A_SkelMissile\n//\nvoid A_SkelMissile (mobj_t* actor)\n{\t\n    mobj_t*\tmo;\n\t\n    if (!actor->target)\n\treturn;\n\t\t\n    A_FaceTarget (actor);\n    actor->z += 16*FRACUNIT;\t// so missile spawns higher\n    mo = P_SpawnMissile (actor, actor->target, MT_TRACER);\n    actor->z -= 16*FRACUNIT;\t// back to normal\n\n    mo->x += mo->momx;\n    mo->y += mo->momy;\n    mo->tracer = actor->target;\n}\n\nint\tTRACEANGLE = 0xc000000;\n\nvoid A_Tracer (mobj_t* actor)\n{\n    angle_t\texact;\n    fixed_t\tdist;\n    fixed_t\tslope;\n    mobj_t*\tdest;\n    mobj_t*\tth;\n\t\t\n    if (gametic & 3)\n\treturn;\n    \n    // spawn a puff of smoke behind the rocket\t\t\n    P_SpawnPuff (actor->x, actor->y, actor->z);\n\t\n    th = P_SpawnMobj (actor->x-actor->momx,\n\t\t      actor->y-actor->momy,\n\t\t      actor->z, MT_SMOKE);\n    \n    th->momz = FRACUNIT;\n    th->tics -= P_Random()&3;\n    if (th->tics < 1)\n\tth->tics = 1;\n    \n    // adjust direction\n    dest = actor->tracer;\n\t\n    if (!dest || dest->health <= 0)\n\treturn;\n    \n    // change angle\t\n    exact = R_PointToAngle2 (actor->x,\n\t\t\t     actor->y,\n\t\t\t     dest->x,\n\t\t\t     dest->y);\n\n    if (exact != actor->angle)\n    {\n\tif (exact - actor->angle > 0x80000000)\n\t{\n\t    actor->angle -= TRACEANGLE;\n\t    if (exact - actor->angle < 0x80000000)\n\t\tactor->angle = exact;\n\t}\n\telse\n\t{\n\t    actor->angle += TRACEANGLE;\n\t    if (exact - actor->angle > 0x80000000)\n\t\tactor->angle = exact;\n\t}\n    }\n\t\n    exact = actor->angle>>ANGLETOFINESHIFT;\n    actor->momx = FixedMul (actor->info->speed, finecosine[exact]);\n    actor->momy = FixedMul (actor->info->speed, finesine[exact]);\n    \n    // change slope\n    dist = P_AproxDistance (dest->x - actor->x,\n\t\t\t    dest->y - actor->y);\n    \n    dist = dist / actor->info->speed;\n\n    if (dist < 1)\n\tdist = 1;\n    slope = (dest->z+40*FRACUNIT - actor->z) / dist;\n\n    if (slope < actor->momz)\n\tactor->momz -= FRACUNIT/8;\n    else\n\tactor->momz += FRACUNIT/8;\n}\n\n\nvoid A_SkelWhoosh (mobj_t*\tactor)\n{\n    if (!actor->target)\n\treturn;\n    A_FaceTarget (actor);\n    S_StartSound (actor,sfx_skeswg);\n}\n\nvoid A_SkelFist (mobj_t*\tactor)\n{\n    int\t\tdamage;\n\n    if (!actor->target)\n\treturn;\n\t\t\n    A_FaceTarget (actor);\n\t\n    if (P_CheckMeleeRange (actor))\n    {\n\tdamage = ((P_Random()%10)+1)*6;\n\tS_StartSound (actor, sfx_skepch);\n\tP_DamageMobj (actor->target, actor, actor, damage);\n    }\n}\n\n\n\n//\n// PIT_VileCheck\n// Detect a corpse that could be raised.\n//\nmobj_t*\t\tcorpsehit;\nmobj_t*\t\tvileobj;\nfixed_t\t\tviletryx;\nfixed_t\t\tviletryy;\n\nboolean PIT_VileCheck (mobj_t*\tthing)\n{\n    int\t\tmaxdist;\n    boolean\tcheck;\n\t\n    if (!(thing->flags & MF_CORPSE) )\n\treturn true;\t// not a monster\n    \n    if (thing->tics != -1)\n\treturn true;\t// not lying still yet\n    \n    if (thing->info->raisestate == S_NULL)\n\treturn true;\t// monster doesn't have a raise state\n    \n    maxdist = thing->info->radius + mobjinfo[MT_VILE].radius;\n\t\n    if ( abs(thing->x - viletryx) > maxdist\n\t || abs(thing->y - viletryy) > maxdist )\n\treturn true;\t\t// not actually touching\n\t\t\n    corpsehit = thing;\n    corpsehit->momx = corpsehit->momy = 0;\n    corpsehit->height <<= 2;\n    check = P_CheckPosition (corpsehit, corpsehit->x, corpsehit->y);\n    corpsehit->height >>= 2;\n\n    if (!check)\n\treturn true;\t\t// doesn't fit here\n\t\t\n    return false;\t\t// got one, so stop checking\n}\n\n\n\n//\n// A_VileChase\n// Check for ressurecting a body\n//\nvoid A_VileChase (mobj_t* actor)\n{\n    int\t\t\txl;\n    int\t\t\txh;\n    int\t\t\tyl;\n    int\t\t\tyh;\n    \n    int\t\t\tbx;\n    int\t\t\tby;\n\n    mobjinfo_t*\t\tinfo;\n    mobj_t*\t\ttemp;\n\t\n    if (actor->movedir != DI_NODIR)\n    {\n\t// check for corpses to raise\n\tviletryx =\n\t    actor->x + actor->info->speed*xspeed[actor->movedir];\n\tviletryy =\n\t    actor->y + actor->info->speed*yspeed[actor->movedir];\n\n\txl = (viletryx - bmaporgx - MAXRADIUS*2)>>MAPBLOCKSHIFT;\n\txh = (viletryx - bmaporgx + MAXRADIUS*2)>>MAPBLOCKSHIFT;\n\tyl = (viletryy - bmaporgy - MAXRADIUS*2)>>MAPBLOCKSHIFT;\n\tyh = (viletryy - bmaporgy + MAXRADIUS*2)>>MAPBLOCKSHIFT;\n\t\n\tvileobj = actor;\n\tfor (bx=xl ; bx<=xh ; bx++)\n\t{\n\t    for (by=yl ; by<=yh ; by++)\n\t    {\n\t\t// Call PIT_VileCheck to check\n\t\t// whether object is a corpse\n\t\t// that canbe raised.\n\t\tif (!P_BlockThingsIterator(bx,by,PIT_VileCheck))\n\t\t{\n\t\t    // got one!\n\t\t    temp = actor->target;\n\t\t    actor->target = corpsehit;\n\t\t    A_FaceTarget (actor);\n\t\t    actor->target = temp;\n\t\t\t\t\t\n\t\t    P_SetMobjState (actor, S_VILE_HEAL1);\n\t\t    S_StartSound (corpsehit, sfx_slop);\n\t\t    info = corpsehit->info;\n\t\t    \n\t\t    P_SetMobjState (corpsehit,info->raisestate);\n\t\t    corpsehit->height <<= 2;\n\t\t    corpsehit->flags = info->flags;\n\t\t    corpsehit->health = info->spawnhealth;\n\t\t    corpsehit->target = NULL;\n\n\t\t    return;\n\t\t}\n\t    }\n\t}\n    }\n\n    // Return to normal attack.\n    A_Chase (actor);\n}\n\n\n//\n// A_VileStart\n//\nvoid A_VileStart (mobj_t* actor)\n{\n    S_StartSound (actor, sfx_vilatk);\n}\n\n\n//\n// A_Fire\n// Keep fire in front of player unless out of sight\n//\nvoid A_Fire (mobj_t* actor);\n\nvoid A_StartFire (mobj_t* actor)\n{\n    S_StartSound(actor,sfx_flamst);\n    A_Fire(actor);\n}\n\nvoid A_FireCrackle (mobj_t* actor)\n{\n    S_StartSound(actor,sfx_flame);\n    A_Fire(actor);\n}\n\nvoid A_Fire (mobj_t* actor)\n{\n    mobj_t*\tdest;\n    mobj_t*     target;\n    unsigned\tan;\n\t\t\n    dest = actor->tracer;\n    if (!dest)\n\treturn;\n\n    target = P_SubstNullMobj(actor->target);\n\t\t\n    // don't move it if the vile lost sight\n    if (!P_CheckSight (target, dest) )\n\treturn;\n\n    an = dest->angle >> ANGLETOFINESHIFT;\n\n    P_UnsetThingPosition (actor);\n    actor->x = dest->x + FixedMul (24*FRACUNIT, finecosine[an]);\n    actor->y = dest->y + FixedMul (24*FRACUNIT, finesine[an]);\n    actor->z = dest->z;\n    P_SetThingPosition (actor);\n}\n\n\n\n//\n// A_VileTarget\n// Spawn the hellfire\n//\nvoid A_VileTarget (mobj_t*\tactor)\n{\n    mobj_t*\tfog;\n\t\n    if (!actor->target)\n\treturn;\n\n    A_FaceTarget (actor);\n\n    fog = P_SpawnMobj (actor->target->x,\n\t\t       actor->target->x,\n\t\t       actor->target->z, MT_FIRE);\n    \n    actor->tracer = fog;\n    fog->target = actor;\n    fog->tracer = actor->target;\n    A_Fire (fog);\n}\n\n\n\n\n//\n// A_VileAttack\n//\nvoid A_VileAttack (mobj_t* actor)\n{\t\n    mobj_t*\tfire;\n    int\t\tan;\n\t\n    if (!actor->target)\n\treturn;\n    \n    A_FaceTarget (actor);\n\n    if (!P_CheckSight (actor, actor->target) )\n\treturn;\n\n    S_StartSound (actor, sfx_barexp);\n    P_DamageMobj (actor->target, actor, actor, 20);\n    actor->target->momz = 1000*FRACUNIT/actor->target->info->mass;\n\t\n    an = actor->angle >> ANGLETOFINESHIFT;\n\n    fire = actor->tracer;\n\n    if (!fire)\n\treturn;\n\t\t\n    // move the fire between the vile and the player\n    fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);\n    fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);\t\n    P_RadiusAttack (fire, actor, 70 );\n}\n\n\n\n\n//\n// Mancubus attack,\n// firing three missiles (bruisers)\n// in three different directions?\n// Doesn't look like it. \n//\n#define\tFATSPREAD\t(ANG90/8)\n\nvoid A_FatRaise (mobj_t *actor)\n{\n    A_FaceTarget (actor);\n    S_StartSound (actor, sfx_manatk);\n}\n\n\nvoid A_FatAttack1 (mobj_t* actor)\n{\n    mobj_t*\tmo;\n    mobj_t*     target;\n    int\t\tan;\n\n    A_FaceTarget (actor);\n\n    // Change direction  to ...\n    actor->angle += FATSPREAD;\n    target = P_SubstNullMobj(actor->target);\n    P_SpawnMissile (actor, target, MT_FATSHOT);\n\n    mo = P_SpawnMissile (actor, target, MT_FATSHOT);\n    mo->angle += FATSPREAD;\n    an = mo->angle >> ANGLETOFINESHIFT;\n    mo->momx = FixedMul (mo->info->speed, finecosine[an]);\n    mo->momy = FixedMul (mo->info->speed, finesine[an]);\n}\n\nvoid A_FatAttack2 (mobj_t* actor)\n{\n    mobj_t*\tmo;\n    mobj_t*     target;\n    int\t\tan;\n\n    A_FaceTarget (actor);\n    // Now here choose opposite deviation.\n    actor->angle -= FATSPREAD;\n    target = P_SubstNullMobj(actor->target);\n    P_SpawnMissile (actor, target, MT_FATSHOT);\n\n    mo = P_SpawnMissile (actor, target, MT_FATSHOT);\n    mo->angle -= FATSPREAD*2;\n    an = mo->angle >> ANGLETOFINESHIFT;\n    mo->momx = FixedMul (mo->info->speed, finecosine[an]);\n    mo->momy = FixedMul (mo->info->speed, finesine[an]);\n}\n\nvoid A_FatAttack3 (mobj_t*\tactor)\n{\n    mobj_t*\tmo;\n    mobj_t*     target;\n    int\t\tan;\n\n    A_FaceTarget (actor);\n\n    target = P_SubstNullMobj(actor->target);\n    \n    mo = P_SpawnMissile (actor, target, MT_FATSHOT);\n    mo->angle -= FATSPREAD/2;\n    an = mo->angle >> ANGLETOFINESHIFT;\n    mo->momx = FixedMul (mo->info->speed, finecosine[an]);\n    mo->momy = FixedMul (mo->info->speed, finesine[an]);\n\n    mo = P_SpawnMissile (actor, target, MT_FATSHOT);\n    mo->angle += FATSPREAD/2;\n    an = mo->angle >> ANGLETOFINESHIFT;\n    mo->momx = FixedMul (mo->info->speed, finecosine[an]);\n    mo->momy = FixedMul (mo->info->speed, finesine[an]);\n}\n\n\n//\n// SkullAttack\n// Fly at the player like a missile.\n//\n#define\tSKULLSPEED\t\t(20*FRACUNIT)\n\nvoid A_SkullAttack (mobj_t* actor)\n{\n    mobj_t*\t\tdest;\n    angle_t\t\tan;\n    int\t\t\tdist;\n\n    if (!actor->target)\n\treturn;\n\t\t\n    dest = actor->target;\t\n    actor->flags |= MF_SKULLFLY;\n\n    S_StartSound (actor, actor->info->attacksound);\n    A_FaceTarget (actor);\n    an = actor->angle >> ANGLETOFINESHIFT;\n    actor->momx = FixedMul (SKULLSPEED, finecosine[an]);\n    actor->momy = FixedMul (SKULLSPEED, finesine[an]);\n    dist = P_AproxDistance (dest->x - actor->x, dest->y - actor->y);\n    dist = dist / SKULLSPEED;\n    \n    if (dist < 1)\n\tdist = 1;\n    actor->momz = (dest->z+(dest->height>>1) - actor->z) / dist;\n}\n\n\n//\n// A_PainShootSkull\n// Spawn a lost soul and launch it at the target\n//\nvoid\nA_PainShootSkull\n( mobj_t*\tactor,\n  angle_t\tangle )\n{\n    fixed_t\tx;\n    fixed_t\ty;\n    fixed_t\tz;\n    \n    mobj_t*\tnewmobj;\n    angle_t\tan;\n    int\t\tprestep;\n    int\t\tcount;\n    thinker_t*\tcurrentthinker;\n\n    // count total number of skull currently on the level\n    count = 0;\n\n    currentthinker = thinkercap.next;\n    while (currentthinker != &thinkercap)\n    {\n\tif (   (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker)\n\t    && ((mobj_t *)currentthinker)->type == MT_SKULL)\n\t    count++;\n\tcurrentthinker = currentthinker->next;\n    }\n\n    // if there are allready 20 skulls on the level,\n    // don't spit another one\n    if (count > 20)\n\treturn;\n\n\n    // okay, there's playe for another one\n    an = angle >> ANGLETOFINESHIFT;\n    \n    prestep =\n\t4*FRACUNIT\n\t+ 3*(actor->info->radius + mobjinfo[MT_SKULL].radius)/2;\n    \n    x = actor->x + FixedMul (prestep, finecosine[an]);\n    y = actor->y + FixedMul (prestep, finesine[an]);\n    z = actor->z + 8*FRACUNIT;\n\t\t\n    newmobj = P_SpawnMobj (x , y, z, MT_SKULL);\n\n    // Check for movements.\n    if (!P_TryMove (newmobj, newmobj->x, newmobj->y))\n    {\n\t// kill it immediately\n\tP_DamageMobj (newmobj,actor,actor,10000);\t\n\treturn;\n    }\n\t\t\n    newmobj->target = actor->target;\n    A_SkullAttack (newmobj);\n}\n\n\n//\n// A_PainAttack\n// Spawn a lost soul and launch it at the target\n// \nvoid A_PainAttack (mobj_t* actor)\n{\n    if (!actor->target)\n\treturn;\n\n    A_FaceTarget (actor);\n    A_PainShootSkull (actor, actor->angle);\n}\n\n\nvoid A_PainDie (mobj_t* actor)\n{\n    A_Fall (actor);\n    A_PainShootSkull (actor, actor->angle+ANG90);\n    A_PainShootSkull (actor, actor->angle+ANG180);\n    A_PainShootSkull (actor, actor->angle+ANG270);\n}\n\n\n\n\n\n\nvoid A_Scream (mobj_t* actor)\n{\n    int\t\tsound;\n\t\n    switch (actor->info->deathsound)\n    {\n      case 0:\n\treturn;\n\t\t\n      case sfx_podth1:\n      case sfx_podth2:\n      case sfx_podth3:\n\tsound = sfx_podth1 + P_Random ()%3;\n\tbreak;\n\t\t\n      case sfx_bgdth1:\n      case sfx_bgdth2:\n\tsound = sfx_bgdth1 + P_Random ()%2;\n\tbreak;\n\t\n      default:\n\tsound = actor->info->deathsound;\n\tbreak;\n    }\n\n    // Check for bosses.\n    if (actor->type==MT_SPIDER\n\t|| actor->type == MT_CYBORG)\n    {\n\t// full volume\n\tS_StartSound (NULL, sound);\n    }\n    else\n\tS_StartSound (actor, sound);\n}\n\n\nvoid A_XScream (mobj_t* actor)\n{\n    S_StartSound (actor, sfx_slop);\t\n}\n\nvoid A_Pain (mobj_t* actor)\n{\n    if (actor->info->painsound)\n\tS_StartSound (actor, actor->info->painsound);\t\n}\n\n\n\nvoid A_Fall (mobj_t *actor)\n{\n    // actor is on ground, it can be walked over\n    actor->flags &= ~MF_SOLID;\n\n    // So change this if corpse objects\n    // are meant to be obstacles.\n}\n\n\n//\n// A_Explode\n//\nvoid A_Explode (mobj_t* thingy)\n{\n    P_RadiusAttack(thingy, thingy->target, 128);\n}\n\n// Check whether the death of the specified monster type is allowed\n// to trigger the end of episode special action.\n//\n// This behavior changed in v1.9, the most notable effect of which\n// was to break uac_dead.wad\n\nstatic boolean CheckBossEnd(mobjtype_t motype)\n{\n    if (gameversion < exe_ultimate)\n    {\n        if (gamemap != 8)\n        {\n            return false;\n        }\n\n        // Baron death on later episodes is nothing special.\n\n        if (motype == MT_BRUISER && gameepisode != 1)\n        {\n            return false;\n        }\n\n        return true;\n    }\n    else\n    {\n        // New logic that appeared in Ultimate Doom.\n        // Looks like the logic was overhauled while adding in the\n        // episode 4 support.  Now bosses only trigger on their\n        // specific episode.\n\n\tswitch(gameepisode)\n\t{\n            case 1:\n                return gamemap == 8 && motype == MT_BRUISER;\n\n            case 2:\n                return gamemap == 8 && motype == MT_CYBORG;\n\n            case 3:\n                return gamemap == 8 && motype == MT_SPIDER;\n\n\t    case 4:\n                return (gamemap == 6 && motype == MT_CYBORG)\n                    || (gamemap == 8 && motype == MT_SPIDER);\n\n            default:\n                return gamemap == 8;\n\t}\n    }\n}\n\n//\n// A_BossDeath\n// Possibly trigger special effects\n// if on first boss level\n//\nvoid A_BossDeath (mobj_t* mo)\n{\n    thinker_t*\tth;\n    mobj_t*\tmo2;\n    line_t\tjunk;\n    int\t\ti;\n\t\t\n    if ( gamemode == commercial)\n    {\n\tif (gamemap != 7)\n\t    return;\n\t\t\n\tif ((mo->type != MT_FATSO)\n\t    && (mo->type != MT_BABY))\n\t    return;\n    }\n    else\n    {\n        if (!CheckBossEnd(mo->type))\n        {\n            return;\n        }\n    }\n\n    // make sure there is a player alive for victory\n    for (i=0 ; i<MAXPLAYERS ; i++)\n\tif (playeringame[i] && players[i].health > 0)\n\t    break;\n    \n    if (i==MAXPLAYERS)\n\treturn;\t// no one left alive, so do not end game\n    \n    // scan the remaining thinkers to see\n    // if all bosses are dead\n    for (th = thinkercap.next ; th != &thinkercap ; th=th->next)\n    {\n\tif (th->function.acp1 != (actionf_p1)P_MobjThinker)\n\t    continue;\n\t\n\tmo2 = (mobj_t *)th;\n\tif (mo2 != mo\n\t    && mo2->type == mo->type\n\t    && mo2->health > 0)\n\t{\n\t    // other boss not dead\n\t    return;\n\t}\n    }\n\t\n    // victory!\n    if ( gamemode == commercial)\n    {\n\tif (gamemap == 7)\n\t{\n\t    if (mo->type == MT_FATSO)\n\t    {\n\t\tjunk.tag = 666;\n\t\tEV_DoFloor(&junk,lowerFloorToLowest);\n\t\treturn;\n\t    }\n\t    \n\t    if (mo->type == MT_BABY)\n\t    {\n\t\tjunk.tag = 667;\n\t\tEV_DoFloor(&junk,raiseToTexture);\n\t\treturn;\n\t    }\n\t}\n    }\n    else\n    {\n\tswitch(gameepisode)\n\t{\n\t  case 1:\n\t    junk.tag = 666;\n\t    EV_DoFloor (&junk, lowerFloorToLowest);\n\t    return;\n\t    break;\n\t    \n\t  case 4:\n\t    switch(gamemap)\n\t    {\n\t      case 6:\n\t\tjunk.tag = 666;\n\t\tEV_DoDoor (&junk, vld_blazeOpen);\n\t\treturn;\n\t\tbreak;\n\t\t\n\t      case 8:\n\t\tjunk.tag = 666;\n\t\tEV_DoFloor (&junk, lowerFloorToLowest);\n\t\treturn;\n\t\tbreak;\n\t    }\n\t}\n    }\n\t\n    G_ExitLevel ();\n}\n\n\nvoid A_Hoof (mobj_t* mo)\n{\n    S_StartSound (mo, sfx_hoof);\n    A_Chase (mo);\n}\n\nvoid A_Metal (mobj_t* mo)\n{\n    S_StartSound (mo, sfx_metal);\n    A_Chase (mo);\n}\n\nvoid A_BabyMetal (mobj_t* mo)\n{\n    S_StartSound (mo, sfx_bspwlk);\n    A_Chase (mo);\n}\n\nvoid\nA_OpenShotgun2\n( player_t*\tplayer,\n  pspdef_t*\tpsp )\n{\n    S_StartSound (player->mo, sfx_dbopn);\n}\n\nvoid\nA_LoadShotgun2\n( player_t*\tplayer,\n  pspdef_t*\tpsp )\n{\n    S_StartSound (player->mo, sfx_dbload);\n}\n\nvoid\nA_ReFire\n( player_t*\tplayer,\n  pspdef_t*\tpsp );\n\nvoid\nA_CloseShotgun2\n( player_t*\tplayer,\n  pspdef_t*\tpsp )\n{\n    S_StartSound (player->mo, sfx_dbcls);\n    A_ReFire(player,psp);\n}\n\n\n\nmobj_t*\t\tbraintargets[32];\nint\t\tnumbraintargets;\nint\t\tbraintargeton = 0;\n\nvoid A_BrainAwake (mobj_t* mo)\n{\n    thinker_t*\tthinker;\n    mobj_t*\tm;\n\t\n    // find all the target spots\n    numbraintargets = 0;\n    braintargeton = 0;\n\t\n    thinker = thinkercap.next;\n    for (thinker = thinkercap.next ;\n\t thinker != &thinkercap ;\n\t thinker = thinker->next)\n    {\n\tif (thinker->function.acp1 != (actionf_p1)P_MobjThinker)\n\t    continue;\t// not a mobj\n\n\tm = (mobj_t *)thinker;\n\n\tif (m->type == MT_BOSSTARGET )\n\t{\n\t    braintargets[numbraintargets] = m;\n\t    numbraintargets++;\n\t}\n    }\n\t\n    S_StartSound (NULL,sfx_bossit);\n}\n\n\nvoid A_BrainPain (mobj_t*\tmo)\n{\n    S_StartSound (NULL,sfx_bospn);\n}\n\n\nvoid A_BrainScream (mobj_t*\tmo)\n{\n    int\t\tx;\n    int\t\ty;\n    int\t\tz;\n    mobj_t*\tth;\n\t\n    for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8)\n    {\n\ty = mo->y - 320*FRACUNIT;\n\tz = 128 + P_Random()*2*FRACUNIT;\n\tth = P_SpawnMobj (x,y,z, MT_ROCKET);\n\tth->momz = P_Random()*512;\n\n\tP_SetMobjState (th, S_BRAINEXPLODE1);\n\n\tth->tics -= P_Random()&7;\n\tif (th->tics < 1)\n\t    th->tics = 1;\n    }\n\t\n    S_StartSound (NULL,sfx_bosdth);\n}\n\n\n\nvoid A_BrainExplode (mobj_t* mo)\n{\n    int\t\tx;\n    int\t\ty;\n    int\t\tz;\n    mobj_t*\tth;\n\t\n    x = mo->x + (P_Random () - P_Random ())*2048;\n    y = mo->y;\n    z = 128 + P_Random()*2*FRACUNIT;\n    th = P_SpawnMobj (x,y,z, MT_ROCKET);\n    th->momz = P_Random()*512;\n\n    P_SetMobjState (th, S_BRAINEXPLODE1);\n\n    th->tics -= P_Random()&7;\n    if (th->tics < 1)\n\tth->tics = 1;\n}\n\n\nvoid A_BrainDie (mobj_t*\tmo)\n{\n    G_ExitLevel ();\n}\n\nvoid A_BrainSpit (mobj_t*\tmo)\n{\n    mobj_t*\ttarg;\n    mobj_t*\tnewmobj;\n    \n    static int\teasy = 0;\n\t\n    easy ^= 1;\n    if (gameskill <= sk_easy && (!easy))\n\treturn;\n\t\t\n    // shoot a cube at current target\n    targ = braintargets[braintargeton];\n    braintargeton = (braintargeton+1)%numbraintargets;\n\n    // spawn brain missile\n    newmobj = P_SpawnMissile (mo, targ, MT_SPAWNSHOT);\n    newmobj->target = targ;\n    newmobj->reactiontime =\n\t((targ->y - mo->y)/newmobj->momy) / newmobj->state->tics;\n\n    S_StartSound(NULL, sfx_bospit);\n}\n\n\n\nvoid A_SpawnFly (mobj_t* mo);\n\n// travelling cube sound\nvoid A_SpawnSound (mobj_t* mo)\t\n{\n    S_StartSound (mo,sfx_boscub);\n    A_SpawnFly(mo);\n}\n\nvoid A_SpawnFly (mobj_t* mo)\n{\n    mobj_t*\tnewmobj;\n    mobj_t*\tfog;\n    mobj_t*\ttarg;\n    int\t\tr;\n    mobjtype_t\ttype;\n\t\n    if (--mo->reactiontime)\n\treturn;\t// still flying\n\t\n    targ = P_SubstNullMobj(mo->target);\n\n    // First spawn teleport fog.\n    fog = P_SpawnMobj (targ->x, targ->y, targ->z, MT_SPAWNFIRE);\n    S_StartSound (fog, sfx_telept);\n\n    // Randomly select monster to spawn.\n    r = P_Random ();\n\n    // Probability distribution (kind of :),\n    // decreasing likelihood.\n    if ( r<50 )\n\ttype = MT_TROOP;\n    else if (r<90)\n\ttype = MT_SERGEANT;\n    else if (r<120)\n\ttype = MT_SHADOWS;\n    else if (r<130)\n\ttype = MT_PAIN;\n    else if (r<160)\n\ttype = MT_HEAD;\n    else if (r<162)\n\ttype = MT_VILE;\n    else if (r<172)\n\ttype = MT_UNDEAD;\n    else if (r<192)\n\ttype = MT_BABY;\n    else if (r<222)\n\ttype = MT_FATSO;\n    else if (r<246)\n\ttype = MT_KNIGHT;\n    else\n\ttype = MT_BRUISER;\t\t\n\n    newmobj\t= P_SpawnMobj (targ->x, targ->y, targ->z, type);\n    if (P_LookForPlayers (newmobj, true) )\n\tP_SetMobjState (newmobj, newmobj->info->seestate);\n\t\n    // telefrag anything in this spot\n    P_TeleportMove (newmobj, newmobj->x, newmobj->y);\n\n    // remove self (i.e., cube).\n    P_RemoveMobj (mo);\n}\n\n\n\nvoid A_PlayerScream (mobj_t* mo)\n{\n    // Default death sound.\n    int\t\tsound = sfx_pldeth;\n\t\n    if ( (gamemode == commercial)\n\t&& \t(mo->health < -50))\n    {\n\t// IF THE PLAYER DIES\n\t// LESS THAN -50% WITHOUT GIBBING\n\tsound = sfx_pdiehi;\n    }\n    \n    S_StartSound (mo, sound);\n}\n"
  },
  {
    "path": "fbdoom/p_floor.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tFloor animation: raising stairs.\n//\n\n\n\n#include \"z_zone.h\"\n#include \"doomdef.h\"\n#include \"p_local.h\"\n\n#include \"s_sound.h\"\n\n// State.\n#include \"doomstat.h\"\n#include \"r_state.h\"\n// Data.\n#include \"sounds.h\"\n\n\n//\n// FLOORS\n//\n\n//\n// Move a plane (floor or ceiling) and check for crushing\n//\nresult_e\nT_MovePlane\n( sector_t*\tsector,\n  fixed_t\tspeed,\n  fixed_t\tdest,\n  boolean\tcrush,\n  int\t\tfloorOrCeiling,\n  int\t\tdirection )\n{\n    boolean\tflag;\n    fixed_t\tlastpos;\n\t\n    switch(floorOrCeiling)\n    {\n      case 0:\n\t// FLOOR\n\tswitch(direction)\n\t{\n\t  case -1:\n\t    // DOWN\n\t    if (sector->floorheight - speed < dest)\n\t    {\n\t\tlastpos = sector->floorheight;\n\t\tsector->floorheight = dest;\n\t\tflag = P_ChangeSector(sector,crush);\n\t\tif (flag == true)\n\t\t{\n\t\t    sector->floorheight =lastpos;\n\t\t    P_ChangeSector(sector,crush);\n\t\t    //return crushed;\n\t\t}\n\t\treturn pastdest;\n\t    }\n\t    else\n\t    {\n\t\tlastpos = sector->floorheight;\n\t\tsector->floorheight -= speed;\n\t\tflag = P_ChangeSector(sector,crush);\n\t\tif (flag == true)\n\t\t{\n\t\t    sector->floorheight = lastpos;\n\t\t    P_ChangeSector(sector,crush);\n\t\t    return crushed;\n\t\t}\n\t    }\n\t    break;\n\t\t\t\t\t\t\n\t  case 1:\n\t    // UP\n\t    if (sector->floorheight + speed > dest)\n\t    {\n\t\tlastpos = sector->floorheight;\n\t\tsector->floorheight = dest;\n\t\tflag = P_ChangeSector(sector,crush);\n\t\tif (flag == true)\n\t\t{\n\t\t    sector->floorheight = lastpos;\n\t\t    P_ChangeSector(sector,crush);\n\t\t    //return crushed;\n\t\t}\n\t\treturn pastdest;\n\t    }\n\t    else\n\t    {\n\t\t// COULD GET CRUSHED\n\t\tlastpos = sector->floorheight;\n\t\tsector->floorheight += speed;\n\t\tflag = P_ChangeSector(sector,crush);\n\t\tif (flag == true)\n\t\t{\n\t\t    if (crush == true)\n\t\t\treturn crushed;\n\t\t    sector->floorheight = lastpos;\n\t\t    P_ChangeSector(sector,crush);\n\t\t    return crushed;\n\t\t}\n\t    }\n\t    break;\n\t}\n\tbreak;\n\t\t\t\t\t\t\t\t\t\n      case 1:\n\t// CEILING\n\tswitch(direction)\n\t{\n\t  case -1:\n\t    // DOWN\n\t    if (sector->ceilingheight - speed < dest)\n\t    {\n\t\tlastpos = sector->ceilingheight;\n\t\tsector->ceilingheight = dest;\n\t\tflag = P_ChangeSector(sector,crush);\n\n\t\tif (flag == true)\n\t\t{\n\t\t    sector->ceilingheight = lastpos;\n\t\t    P_ChangeSector(sector,crush);\n\t\t    //return crushed;\n\t\t}\n\t\treturn pastdest;\n\t    }\n\t    else\n\t    {\n\t\t// COULD GET CRUSHED\n\t\tlastpos = sector->ceilingheight;\n\t\tsector->ceilingheight -= speed;\n\t\tflag = P_ChangeSector(sector,crush);\n\n\t\tif (flag == true)\n\t\t{\n\t\t    if (crush == true)\n\t\t\treturn crushed;\n\t\t    sector->ceilingheight = lastpos;\n\t\t    P_ChangeSector(sector,crush);\n\t\t    return crushed;\n\t\t}\n\t    }\n\t    break;\n\t\t\t\t\t\t\n\t  case 1:\n\t    // UP\n\t    if (sector->ceilingheight + speed > dest)\n\t    {\n\t\tlastpos = sector->ceilingheight;\n\t\tsector->ceilingheight = dest;\n\t\tflag = P_ChangeSector(sector,crush);\n\t\tif (flag == true)\n\t\t{\n\t\t    sector->ceilingheight = lastpos;\n\t\t    P_ChangeSector(sector,crush);\n\t\t    //return crushed;\n\t\t}\n\t\treturn pastdest;\n\t    }\n\t    else\n\t    {\n\t\tlastpos = sector->ceilingheight;\n\t\tsector->ceilingheight += speed;\n\t\tflag = P_ChangeSector(sector,crush);\n// UNUSED\n#if 0\n\t\tif (flag == true)\n\t\t{\n\t\t    sector->ceilingheight = lastpos;\n\t\t    P_ChangeSector(sector,crush);\n\t\t    return crushed;\n\t\t}\n#endif\n\t    }\n\t    break;\n\t}\n\tbreak;\n\t\t\n    }\n    return ok;\n}\n\n\n//\n// MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN)\n//\nvoid T_MoveFloor(floormove_t* floor)\n{\n    result_e\tres;\n\t\n    res = T_MovePlane(floor->sector,\n\t\t      floor->speed,\n\t\t      floor->floordestheight,\n\t\t      floor->crush,0,floor->direction);\n    \n    if (!(leveltime&7))\n\tS_StartSound(&floor->sector->soundorg, sfx_stnmov);\n    \n    if (res == pastdest)\n    {\n\tfloor->sector->specialdata = NULL;\n\n\tif (floor->direction == 1)\n\t{\n\t    switch(floor->type)\n\t    {\n\t      case donutRaise:\n\t\tfloor->sector->special = floor->newspecial;\n\t\tfloor->sector->floorpic = floor->texture;\n\t      default:\n\t\tbreak;\n\t    }\n\t}\n\telse if (floor->direction == -1)\n\t{\n\t    switch(floor->type)\n\t    {\n\t      case lowerAndChange:\n\t\tfloor->sector->special = floor->newspecial;\n\t\tfloor->sector->floorpic = floor->texture;\n\t      default:\n\t\tbreak;\n\t    }\n\t}\n\tP_RemoveThinker(&floor->thinker);\n\n\tS_StartSound(&floor->sector->soundorg, sfx_pstop);\n    }\n\n}\n\n//\n// HANDLE FLOOR TYPES\n//\nint\nEV_DoFloor\n( line_t*\tline,\n  floor_e\tfloortype )\n{\n    int\t\t\tsecnum;\n    int\t\t\trtn;\n    int\t\t\ti;\n    sector_t*\t\tsec;\n    floormove_t*\tfloor;\n\n    secnum = -1;\n    rtn = 0;\n    while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)\n    {\n\tsec = &sectors[secnum];\n\t\t\n\t// ALREADY MOVING?  IF SO, KEEP GOING...\n\tif (sec->specialdata)\n\t    continue;\n\t\n\t// new floor thinker\n\trtn = 1;\n\tfloor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);\n\tP_AddThinker (&floor->thinker);\n\tsec->specialdata = floor;\n\tfloor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;\n\tfloor->type = floortype;\n\tfloor->crush = false;\n\n\tswitch(floortype)\n\t{\n\t  case lowerFloor:\n\t    floor->direction = -1;\n\t    floor->sector = sec;\n\t    floor->speed = FLOORSPEED;\n\t    floor->floordestheight = \n\t\tP_FindHighestFloorSurrounding(sec);\n\t    break;\n\n\t  case lowerFloorToLowest:\n\t    floor->direction = -1;\n\t    floor->sector = sec;\n\t    floor->speed = FLOORSPEED;\n\t    floor->floordestheight = \n\t\tP_FindLowestFloorSurrounding(sec);\n\t    break;\n\n\t  case turboLower:\n\t    floor->direction = -1;\n\t    floor->sector = sec;\n\t    floor->speed = FLOORSPEED * 4;\n\t    floor->floordestheight = \n\t\tP_FindHighestFloorSurrounding(sec);\n\t    if (floor->floordestheight != sec->floorheight)\n\t\tfloor->floordestheight += 8*FRACUNIT;\n\t    break;\n\n\t  case raiseFloorCrush:\n\t    floor->crush = true;\n\t  case raiseFloor:\n\t    floor->direction = 1;\n\t    floor->sector = sec;\n\t    floor->speed = FLOORSPEED;\n\t    floor->floordestheight = \n\t\tP_FindLowestCeilingSurrounding(sec);\n\t    if (floor->floordestheight > sec->ceilingheight)\n\t\tfloor->floordestheight = sec->ceilingheight;\n\t    floor->floordestheight -= (8*FRACUNIT)*\n\t\t(floortype == raiseFloorCrush);\n\t    break;\n\n\t  case raiseFloorTurbo:\n\t    floor->direction = 1;\n\t    floor->sector = sec;\n\t    floor->speed = FLOORSPEED*4;\n\t    floor->floordestheight = \n\t\tP_FindNextHighestFloor(sec,sec->floorheight);\n\t    break;\n\n\t  case raiseFloorToNearest:\n\t    floor->direction = 1;\n\t    floor->sector = sec;\n\t    floor->speed = FLOORSPEED;\n\t    floor->floordestheight = \n\t\tP_FindNextHighestFloor(sec,sec->floorheight);\n\t    break;\n\n\t  case raiseFloor24:\n\t    floor->direction = 1;\n\t    floor->sector = sec;\n\t    floor->speed = FLOORSPEED;\n\t    floor->floordestheight = floor->sector->floorheight +\n\t\t24 * FRACUNIT;\n\t    break;\n\t  case raiseFloor512:\n\t    floor->direction = 1;\n\t    floor->sector = sec;\n\t    floor->speed = FLOORSPEED;\n\t    floor->floordestheight = floor->sector->floorheight +\n\t\t512 * FRACUNIT;\n\t    break;\n\n\t  case raiseFloor24AndChange:\n\t    floor->direction = 1;\n\t    floor->sector = sec;\n\t    floor->speed = FLOORSPEED;\n\t    floor->floordestheight = floor->sector->floorheight +\n\t\t24 * FRACUNIT;\n\t    sec->floorpic = line->frontsector->floorpic;\n\t    sec->special = line->frontsector->special;\n\t    break;\n\n\t  case raiseToTexture:\n\t  {\n\t      int\tminsize = INT_MAX;\n\t      side_t*\tside;\n\t\t\t\t\n\t      floor->direction = 1;\n\t      floor->sector = sec;\n\t      floor->speed = FLOORSPEED;\n\t      for (i = 0; i < sec->linecount; i++)\n\t      {\n\t\t  if (twoSided (secnum, i) )\n\t\t  {\n\t\t      side = getSide(secnum,i,0);\n\t\t      if (side->bottomtexture >= 0)\n\t\t\t  if (textureheight[side->bottomtexture] < \n\t\t\t      minsize)\n\t\t\t      minsize = \n\t\t\t\t  textureheight[side->bottomtexture];\n\t\t      side = getSide(secnum,i,1);\n\t\t      if (side->bottomtexture >= 0)\n\t\t\t  if (textureheight[side->bottomtexture] < \n\t\t\t      minsize)\n\t\t\t      minsize = \n\t\t\t\t  textureheight[side->bottomtexture];\n\t\t  }\n\t      }\n\t      floor->floordestheight =\n\t\t  floor->sector->floorheight + minsize;\n\t  }\n\t  break;\n\t  \n\t  case lowerAndChange:\n\t    floor->direction = -1;\n\t    floor->sector = sec;\n\t    floor->speed = FLOORSPEED;\n\t    floor->floordestheight = \n\t\tP_FindLowestFloorSurrounding(sec);\n\t    floor->texture = sec->floorpic;\n\n\t    for (i = 0; i < sec->linecount; i++)\n\t    {\n\t\tif ( twoSided(secnum, i) )\n\t\t{\n\t\t    if (getSide(secnum,i,0)->sector-sectors == secnum)\n\t\t    {\n\t\t\tsec = getSector(secnum,i,1);\n\n\t\t\tif (sec->floorheight == floor->floordestheight)\n\t\t\t{\n\t\t\t    floor->texture = sec->floorpic;\n\t\t\t    floor->newspecial = sec->special;\n\t\t\t    break;\n\t\t\t}\n\t\t    }\n\t\t    else\n\t\t    {\n\t\t\tsec = getSector(secnum,i,0);\n\n\t\t\tif (sec->floorheight == floor->floordestheight)\n\t\t\t{\n\t\t\t    floor->texture = sec->floorpic;\n\t\t\t    floor->newspecial = sec->special;\n\t\t\t    break;\n\t\t\t}\n\t\t    }\n\t\t}\n\t    }\n\t  default:\n\t    break;\n\t}\n    }\n    return rtn;\n}\n\n\n\n\n//\n// BUILD A STAIRCASE!\n//\nint\nEV_BuildStairs\n( line_t*\tline,\n  stair_e\ttype )\n{\n    int\t\t\tsecnum;\n    int\t\t\theight;\n    int\t\t\ti;\n    int\t\t\tnewsecnum;\n    int\t\t\ttexture;\n    int\t\t\tok;\n    int\t\t\trtn;\n    \n    sector_t*\t\tsec;\n    sector_t*\t\ttsec;\n\n    floormove_t*\tfloor;\n    \n    fixed_t\t\tstairsize = 0;\n    fixed_t\t\tspeed = 0;\n\n    secnum = -1;\n    rtn = 0;\n    while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)\n    {\n\tsec = &sectors[secnum];\n\t\t\n\t// ALREADY MOVING?  IF SO, KEEP GOING...\n\tif (sec->specialdata)\n\t    continue;\n\t\n\t// new floor thinker\n\trtn = 1;\n\tfloor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);\n\tP_AddThinker (&floor->thinker);\n\tsec->specialdata = floor;\n\tfloor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;\n\tfloor->direction = 1;\n\tfloor->sector = sec;\n\tswitch(type)\n\t{\n\t  case build8:\n\t    speed = FLOORSPEED/4;\n\t    stairsize = 8*FRACUNIT;\n\t    break;\n\t  case turbo16:\n\t    speed = FLOORSPEED*4;\n\t    stairsize = 16*FRACUNIT;\n\t    break;\n\t}\n\tfloor->speed = speed;\n\theight = sec->floorheight + stairsize;\n\tfloor->floordestheight = height;\n\t\t\n\ttexture = sec->floorpic;\n\t\n\t// Find next sector to raise\n\t// 1.\tFind 2-sided line with same sector side[0]\n\t// 2.\tOther side is the next sector to raise\n\tdo\n\t{\n\t    ok = 0;\n\t    for (i = 0;i < sec->linecount;i++)\n\t    {\n\t\tif ( !((sec->lines[i])->flags & ML_TWOSIDED) )\n\t\t    continue;\n\t\t\t\t\t\n\t\ttsec = (sec->lines[i])->frontsector;\n\t\tnewsecnum = tsec-sectors;\n\t\t\n\t\tif (secnum != newsecnum)\n\t\t    continue;\n\n\t\ttsec = (sec->lines[i])->backsector;\n\t\tnewsecnum = tsec - sectors;\n\n\t\tif (tsec->floorpic != texture)\n\t\t    continue;\n\t\t\t\t\t\n\t\theight += stairsize;\n\n\t\tif (tsec->specialdata)\n\t\t    continue;\n\t\t\t\t\t\n\t\tsec = tsec;\n\t\tsecnum = newsecnum;\n\t\tfloor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);\n\n\t\tP_AddThinker (&floor->thinker);\n\n\t\tsec->specialdata = floor;\n\t\tfloor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;\n\t\tfloor->direction = 1;\n\t\tfloor->sector = sec;\n\t\tfloor->speed = speed;\n\t\tfloor->floordestheight = height;\n\t\tok = 1;\n\t\tbreak;\n\t    }\n\t} while(ok);\n    }\n    return rtn;\n}\n\n"
  },
  {
    "path": "fbdoom/p_inter.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tHandling interactions (i.e., collisions).\n//\n\n\n\n\n// Data.\n#include \"doomdef.h\"\n#include \"dstrings.h\"\n#include \"sounds.h\"\n\n#include \"deh_main.h\"\n#include \"deh_misc.h\"\n#include \"doomstat.h\"\n\n#include \"m_random.h\"\n#include \"i_system.h\"\n\n#include \"am_map.h\"\n\n#include \"p_local.h\"\n\n#include \"s_sound.h\"\n\n#include \"p_inter.h\"\n\n\n#define BONUSADD\t6\n\n\n\n\n// a weapon is found with two clip loads,\n// a big item has five clip loads\nint\tmaxammo[NUMAMMO] = {200, 50, 300, 50};\nint\tclipammo[NUMAMMO] = {10, 4, 20, 1};\n\n\n//\n// GET STUFF\n//\n\n//\n// P_GiveAmmo\n// Num is the number of clip loads,\n// not the individual count (0= 1/2 clip).\n// Returns false if the ammo can't be picked up at all\n//\n\nboolean\nP_GiveAmmo\n( player_t*\tplayer,\n  ammotype_t\tammo,\n  int\t\tnum )\n{\n    int\t\toldammo;\n\t\n    if (ammo == am_noammo)\n\treturn false;\n\t\t\n    if (ammo > NUMAMMO)\n\tI_Error (\"P_GiveAmmo: bad type %i\", ammo);\n\t\t\n    if ( player->ammo[ammo] == player->maxammo[ammo]  )\n\treturn false;\n\t\t\n    if (num)\n\tnum *= clipammo[ammo];\n    else\n\tnum = clipammo[ammo]/2;\n    \n    if (gameskill == sk_baby\n\t|| gameskill == sk_nightmare)\n    {\n\t// give double ammo in trainer mode,\n\t// you'll need in nightmare\n\tnum <<= 1;\n    }\n    \n\t\t\n    oldammo = player->ammo[ammo];\n    player->ammo[ammo] += num;\n\n    if (player->ammo[ammo] > player->maxammo[ammo])\n\tplayer->ammo[ammo] = player->maxammo[ammo];\n\n    // If non zero ammo, \n    // don't change up weapons,\n    // player was lower on purpose.\n    if (oldammo)\n\treturn true;\t\n\n    // We were down to zero,\n    // so select a new weapon.\n    // Preferences are not user selectable.\n    switch (ammo)\n    {\n      case am_clip:\n\tif (player->readyweapon == wp_fist)\n\t{\n\t    if (player->weaponowned[wp_chaingun])\n\t\tplayer->pendingweapon = wp_chaingun;\n\t    else\n\t\tplayer->pendingweapon = wp_pistol;\n\t}\n\tbreak;\n\t\n      case am_shell:\n\tif (player->readyweapon == wp_fist\n\t    || player->readyweapon == wp_pistol)\n\t{\n\t    if (player->weaponowned[wp_shotgun])\n\t\tplayer->pendingweapon = wp_shotgun;\n\t}\n\tbreak;\n\t\n      case am_cell:\n\tif (player->readyweapon == wp_fist\n\t    || player->readyweapon == wp_pistol)\n\t{\n\t    if (player->weaponowned[wp_plasma])\n\t\tplayer->pendingweapon = wp_plasma;\n\t}\n\tbreak;\n\t\n      case am_misl:\n\tif (player->readyweapon == wp_fist)\n\t{\n\t    if (player->weaponowned[wp_missile])\n\t\tplayer->pendingweapon = wp_missile;\n\t}\n      default:\n\tbreak;\n    }\n\t\n    return true;\n}\n\n\n//\n// P_GiveWeapon\n// The weapon name may have a MF_DROPPED flag ored in.\n//\nboolean\nP_GiveWeapon\n( player_t*\tplayer,\n  weapontype_t\tweapon,\n  boolean\tdropped )\n{\n    boolean\tgaveammo;\n    boolean\tgaveweapon;\n\n    if (netgame && (deathmatch!=2) && !dropped )\n\t{\n\t\t// leave placed weapons forever on net games\n\t\tif (player->weaponowned[weapon])\n\t\t\treturn false;\n\n\t\tplayer->bonuscount += BONUSADD;\n\t\tplayer->weaponowned[weapon] = true;\n\t\n\t\tif (deathmatch)\n\t\t\tP_GiveAmmo (player, weaponinfo[weapon].ammo, 5);\n\t\telse\n\t\t\tP_GiveAmmo (player, weaponinfo[weapon].ammo, 2);\n\t\tplayer->pendingweapon = weapon;\n\n\t\tif (player == &players[consoleplayer])\n\t\t\tS_StartSound (NULL, sfx_wpnup);\n\t\treturn false;\n    }\n\t\n    if (weaponinfo[weapon].ammo != am_noammo)\n    {\n\t\t// give one clip with a dropped weapon,\n\t\t// two clips with a found weapon\n\t\tif (dropped)\n\t\t\tgaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 1);\n\t\telse\n\t\t\tgaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 2);\n    }\n    else\n    {\n    \tgaveammo = false;\n    }\n\t\n    if (player->weaponowned[weapon])\n    {\n    \tgaveweapon = false;\n    }\n    else\n    {\n\t\tgaveweapon = true;\n\t\tplayer->weaponowned[weapon] = true;\n\t\tplayer->pendingweapon = weapon;\n    }\n\t\n    return (gaveweapon || gaveammo);\n}\n\n \n\n//\n// P_GiveBody\n// Returns false if the body isn't needed at all\n//\nboolean\nP_GiveBody\n( player_t*\tplayer,\n  int\t\tnum )\n{\n    if (player->health >= MAXHEALTH)\n\treturn false;\n\t\t\n    player->health += num;\n    if (player->health > MAXHEALTH)\n\tplayer->health = MAXHEALTH;\n    player->mo->health = player->health;\n\t\n    return true;\n}\n\n\n\n//\n// P_GiveArmor\n// Returns false if the armor is worse\n// than the current armor.\n//\nboolean\nP_GiveArmor\n( player_t*\tplayer,\n  int\t\tarmortype )\n{\n    int\t\thits;\n\t\n    hits = armortype*100;\n    if (player->armorpoints >= hits)\n\treturn false;\t// don't pick up\n\t\t\n    player->armortype = armortype;\n    player->armorpoints = hits;\n\t\n    return true;\n}\n\n\n\n//\n// P_GiveCard\n//\nvoid\nP_GiveCard\n( player_t*\tplayer,\n  card_t\tcard )\n{\n    if (player->cards[card])\n\treturn;\n    \n    player->bonuscount = BONUSADD;\n    player->cards[card] = 1;\n}\n\n\n//\n// P_GivePower\n//\nboolean\nP_GivePower\n( player_t*\tplayer,\n  int /*powertype_t*/\tpower )\n{\n    if (power == pw_invulnerability)\n    {\n\tplayer->powers[power] = INVULNTICS;\n\treturn true;\n    }\n    \n    if (power == pw_invisibility)\n    {\n\tplayer->powers[power] = INVISTICS;\n\tplayer->mo->flags |= MF_SHADOW;\n\treturn true;\n    }\n    \n    if (power == pw_infrared)\n    {\n\tplayer->powers[power] = INFRATICS;\n\treturn true;\n    }\n    \n    if (power == pw_ironfeet)\n    {\n\tplayer->powers[power] = IRONTICS;\n\treturn true;\n    }\n    \n    if (power == pw_strength)\n    {\n\tP_GiveBody (player, 100);\n\tplayer->powers[power] = 1;\n\treturn true;\n    }\n\t\n    if (player->powers[power])\n\treturn false;\t// already got it\n\t\t\n    player->powers[power] = 1;\n    return true;\n}\n\n\n\n//\n// P_TouchSpecialThing\n//\nvoid\nP_TouchSpecialThing\n( mobj_t*\tspecial,\n  mobj_t*\ttoucher )\n{\n    player_t*\tplayer;\n    int\t\ti;\n    fixed_t\tdelta;\n    int\t\tsound;\n\t\t\n    delta = special->z - toucher->z;\n\n    if (delta > toucher->height\n\t|| delta < -8*FRACUNIT)\n    {\n\t// out of reach\n\treturn;\n    }\n    \n\t\n    sound = sfx_itemup;\t\n    player = toucher->player;\n\n    // Dead thing touching.\n    // Can happen with a sliding player corpse.\n    if (toucher->health <= 0)\n\treturn;\n\n    // Identify by sprite.\n    switch (special->sprite)\n    {\n\t// armor\n      case SPR_ARM1:\n\tif (!P_GiveArmor (player, deh_green_armor_class))\n\t    return;\n\tplayer->message = DEH_String(GOTARMOR);\n\tbreak;\n\t\t\n      case SPR_ARM2:\n\tif (!P_GiveArmor (player, deh_blue_armor_class))\n\t    return;\n\tplayer->message = DEH_String(GOTMEGA);\n\tbreak;\n\t\n\t// bonus items\n      case SPR_BON1:\n\tplayer->health++;\t\t// can go over 100%\n\tif (player->health > deh_max_health)\n\t    player->health = deh_max_health;\n\tplayer->mo->health = player->health;\n\tplayer->message = DEH_String(GOTHTHBONUS);\n\tbreak;\n\t\n      case SPR_BON2:\n\tplayer->armorpoints++;\t\t// can go over 100%\n\tif (player->armorpoints > deh_max_armor)\n\t    player->armorpoints = deh_max_armor;\n        // deh_green_armor_class only applies to the green armor shirt;\n        // for the armor helmets, armortype 1 is always used.\n\tif (!player->armortype)\n\t    player->armortype = 1;\n\tplayer->message = DEH_String(GOTARMBONUS);\n\tbreak;\n\t\n      case SPR_SOUL:\n\tplayer->health += deh_soulsphere_health;\n\tif (player->health > deh_max_soulsphere)\n\t    player->health = deh_max_soulsphere;\n\tplayer->mo->health = player->health;\n\tplayer->message = DEH_String(GOTSUPER);\n\tsound = sfx_getpow;\n\tbreak;\n\t\n      case SPR_MEGA:\n\tif (gamemode != commercial)\n\t    return;\n\tplayer->health = deh_megasphere_health;\n\tplayer->mo->health = player->health;\n        // We always give armor type 2 for the megasphere; dehacked only \n        // affects the MegaArmor.\n\tP_GiveArmor (player, 2);\n\tplayer->message = DEH_String(GOTMSPHERE);\n\tsound = sfx_getpow;\n\tbreak;\n\t\n\t// cards\n\t// leave cards for everyone\n      case SPR_BKEY:\n\tif (!player->cards[it_bluecard])\n\t    player->message = DEH_String(GOTBLUECARD);\n\tP_GiveCard (player, it_bluecard);\n\tif (!netgame)\n\t    break;\n\treturn;\n\t\n      case SPR_YKEY:\n\tif (!player->cards[it_yellowcard])\n\t    player->message = DEH_String(GOTYELWCARD);\n\tP_GiveCard (player, it_yellowcard);\n\tif (!netgame)\n\t    break;\n\treturn;\n\t\n      case SPR_RKEY:\n\tif (!player->cards[it_redcard])\n\t    player->message = DEH_String(GOTREDCARD);\n\tP_GiveCard (player, it_redcard);\n\tif (!netgame)\n\t    break;\n\treturn;\n\t\n      case SPR_BSKU:\n\tif (!player->cards[it_blueskull])\n\t    player->message = DEH_String(GOTBLUESKUL);\n\tP_GiveCard (player, it_blueskull);\n\tif (!netgame)\n\t    break;\n\treturn;\n\t\n      case SPR_YSKU:\n\tif (!player->cards[it_yellowskull])\n\t    player->message = DEH_String(GOTYELWSKUL);\n\tP_GiveCard (player, it_yellowskull);\n\tif (!netgame)\n\t    break;\n\treturn;\n\t\n      case SPR_RSKU:\n\tif (!player->cards[it_redskull])\n\t    player->message = DEH_String(GOTREDSKULL);\n\tP_GiveCard (player, it_redskull);\n\tif (!netgame)\n\t    break;\n\treturn;\n\t\n\t// medikits, heals\n      case SPR_STIM:\n\tif (!P_GiveBody (player, 10))\n\t    return;\n\tplayer->message = DEH_String(GOTSTIM);\n\tbreak;\n\t\n      case SPR_MEDI:\n\tif (!P_GiveBody (player, 25))\n\t    return;\n\n\tif (player->health < 25)\n\t    player->message = DEH_String(GOTMEDINEED);\n\telse\n\t    player->message = DEH_String(GOTMEDIKIT);\n\tbreak;\n\n\t\n\t// power ups\n      case SPR_PINV:\n\tif (!P_GivePower (player, pw_invulnerability))\n\t    return;\n\tplayer->message = DEH_String(GOTINVUL);\n\tsound = sfx_getpow;\n\tbreak;\n\t\n      case SPR_PSTR:\n\tif (!P_GivePower (player, pw_strength))\n\t    return;\n\tplayer->message = DEH_String(GOTBERSERK);\n\tif (player->readyweapon != wp_fist)\n\t    player->pendingweapon = wp_fist;\n\tsound = sfx_getpow;\n\tbreak;\n\t\n      case SPR_PINS:\n\tif (!P_GivePower (player, pw_invisibility))\n\t    return;\n\tplayer->message = DEH_String(GOTINVIS);\n\tsound = sfx_getpow;\n\tbreak;\n\t\n      case SPR_SUIT:\n\tif (!P_GivePower (player, pw_ironfeet))\n\t    return;\n\tplayer->message = DEH_String(GOTSUIT);\n\tsound = sfx_getpow;\n\tbreak;\n\t\n      case SPR_PMAP:\n\tif (!P_GivePower (player, pw_allmap))\n\t    return;\n\tplayer->message = DEH_String(GOTMAP);\n\tsound = sfx_getpow;\n\tbreak;\n\t\n      case SPR_PVIS:\n\tif (!P_GivePower (player, pw_infrared))\n\t    return;\n\tplayer->message = DEH_String(GOTVISOR);\n\tsound = sfx_getpow;\n\tbreak;\n\t\n\t// ammo\n      case SPR_CLIP:\n\tif (special->flags & MF_DROPPED)\n\t{\n\t    if (!P_GiveAmmo (player,am_clip,0))\n\t\treturn;\n\t}\n\telse\n\t{\n\t    if (!P_GiveAmmo (player,am_clip,1))\n\t\treturn;\n\t}\n\tplayer->message = DEH_String(GOTCLIP);\n\tbreak;\n\t\n      case SPR_AMMO:\n\tif (!P_GiveAmmo (player, am_clip,5))\n\t    return;\n\tplayer->message = DEH_String(GOTCLIPBOX);\n\tbreak;\n\t\n      case SPR_ROCK:\n\tif (!P_GiveAmmo (player, am_misl,1))\n\t    return;\n\tplayer->message = DEH_String(GOTROCKET);\n\tbreak;\n\t\n      case SPR_BROK:\n\tif (!P_GiveAmmo (player, am_misl,5))\n\t    return;\n\tplayer->message = DEH_String(GOTROCKBOX);\n\tbreak;\n\t\n      case SPR_CELL:\n\tif (!P_GiveAmmo (player, am_cell,1))\n\t    return;\n\tplayer->message = DEH_String(GOTCELL);\n\tbreak;\n\t\n      case SPR_CELP:\n\tif (!P_GiveAmmo (player, am_cell,5))\n\t    return;\n\tplayer->message = DEH_String(GOTCELLBOX);\n\tbreak;\n\t\n      case SPR_SHEL:\n\tif (!P_GiveAmmo (player, am_shell,1))\n\t    return;\n\tplayer->message = DEH_String(GOTSHELLS);\n\tbreak;\n\t\n      case SPR_SBOX:\n\tif (!P_GiveAmmo (player, am_shell,5))\n\t    return;\n\tplayer->message = DEH_String(GOTSHELLBOX);\n\tbreak;\n\t\n      case SPR_BPAK:\n\tif (!player->backpack)\n\t{\n\t    for (i=0 ; i<NUMAMMO ; i++)\n\t\tplayer->maxammo[i] *= 2;\n\t    player->backpack = true;\n\t}\n\tfor (i=0 ; i<NUMAMMO ; i++)\n\t    P_GiveAmmo (player, i, 1);\n\tplayer->message = DEH_String(GOTBACKPACK);\n\tbreak;\n\t\n\t// weapons\n      case SPR_BFUG:\n\tif (!P_GiveWeapon (player, wp_bfg, false) )\n\t    return;\n\tplayer->message = DEH_String(GOTBFG9000);\n\tsound = sfx_wpnup;\t\n\tbreak;\n\t\n      case SPR_MGUN:\n\tif (!P_GiveWeapon (player, wp_chaingun, (special->flags&MF_DROPPED) != 0) )\n\t    return;\n\tplayer->message = DEH_String(GOTCHAINGUN);\n\tsound = sfx_wpnup;\t\n\tbreak;\n\t\n      case SPR_CSAW:\n\tif (!P_GiveWeapon (player, wp_chainsaw, false) )\n\t    return;\n\tplayer->message = DEH_String(GOTCHAINSAW);\n\tsound = sfx_wpnup;\t\n\tbreak;\n\t\n      case SPR_LAUN:\n\tif (!P_GiveWeapon (player, wp_missile, false) )\n\t    return;\n\tplayer->message = DEH_String(GOTLAUNCHER);\n\tsound = sfx_wpnup;\t\n\tbreak;\n\t\n      case SPR_PLAS:\n\tif (!P_GiveWeapon (player, wp_plasma, false) )\n\t    return;\n\tplayer->message = DEH_String(GOTPLASMA);\n\tsound = sfx_wpnup;\t\n\tbreak;\n\t\n      case SPR_SHOT:\n\tif (!P_GiveWeapon (player, wp_shotgun, (special->flags&MF_DROPPED) != 0 ) )\n\t    return;\n\tplayer->message = DEH_String(GOTSHOTGUN);\n\tsound = sfx_wpnup;\t\n\tbreak;\n\t\t\n      case SPR_SGN2:\n\tif (!P_GiveWeapon (player, wp_supershotgun, (special->flags&MF_DROPPED) != 0 ) )\n\t    return;\n\tplayer->message = DEH_String(GOTSHOTGUN2);\n\tsound = sfx_wpnup;\t\n\tbreak;\n\t\t\n      default:\n\tI_Error (\"P_SpecialThing: Unknown gettable thing\");\n    }\n\t\n    if (special->flags & MF_COUNTITEM)\n\tplayer->itemcount++;\n    P_RemoveMobj (special);\n    player->bonuscount += BONUSADD;\n    if (player == &players[consoleplayer])\n\tS_StartSound (NULL, sound);\n}\n\n\n//\n// KillMobj\n//\nvoid\nP_KillMobj\n( mobj_t*\tsource,\n  mobj_t*\ttarget )\n{\n    mobjtype_t\titem;\n    mobj_t*\tmo;\n\t\n    target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY);\n\n    if (target->type != MT_SKULL)\n\ttarget->flags &= ~MF_NOGRAVITY;\n\n    target->flags |= MF_CORPSE|MF_DROPOFF;\n    target->height >>= 2;\n\n    if (source && source->player)\n    {\n\t// count for intermission\n\tif (target->flags & MF_COUNTKILL)\n\t    source->player->killcount++;\t\n\n\tif (target->player)\n\t    source->player->frags[target->player-players]++;\n    }\n    else if (!netgame && (target->flags & MF_COUNTKILL) )\n    {\n\t// count all monster deaths,\n\t// even those caused by other monsters\n\tplayers[0].killcount++;\n    }\n    \n    if (target->player)\n    {\n\t// count environment kills against you\n\tif (!source)\t\n\t    target->player->frags[target->player-players]++;\n\t\t\t\n\ttarget->flags &= ~MF_SOLID;\n\ttarget->player->playerstate = PST_DEAD;\n\tP_DropWeapon (target->player);\n\n\tif (target->player == &players[consoleplayer]\n\t    && automapactive)\n\t{\n\t    // don't die in auto map,\n\t    // switch view prior to dying\n\t    AM_Stop ();\n\t}\n\t\n    }\n\n    if (target->health < -target->info->spawnhealth \n\t&& target->info->xdeathstate)\n    {\n\tP_SetMobjState (target, target->info->xdeathstate);\n    }\n    else\n\tP_SetMobjState (target, target->info->deathstate);\n    target->tics -= P_Random()&3;\n\n    if (target->tics < 1)\n\ttarget->tics = 1;\n\t\t\n    //\tI_StartSound (&actor->r, actor->info->deathsound);\n\n    // In Chex Quest, monsters don't drop items.\n\n    if (gameversion == exe_chex)\n    {\n        return;\n    }\n\n    // Drop stuff.\n    // This determines the kind of object spawned\n    // during the death frame of a thing.\n    switch (target->type)\n    {\n      case MT_WOLFSS:\n      case MT_POSSESSED:\n\titem = MT_CLIP;\n\tbreak;\n\t\n      case MT_SHOTGUY:\n\titem = MT_SHOTGUN;\n\tbreak;\n\t\n      case MT_CHAINGUY:\n\titem = MT_CHAINGUN;\n\tbreak;\n\t\n      default:\n\treturn;\n    }\n\n    mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item);\n    mo->flags |= MF_DROPPED;\t// special versions of items\n}\n\n\n\n\n//\n// P_DamageMobj\n// Damages both enemies and players\n// \"inflictor\" is the thing that caused the damage\n//  creature or missile, can be NULL (slime, etc)\n// \"source\" is the thing to target after taking damage\n//  creature or NULL\n// Source and inflictor are the same for melee attacks.\n// Source can be NULL for slime, barrel explosions\n// and other environmental stuff.\n//\nvoid\nP_DamageMobj\n( mobj_t*\ttarget,\n  mobj_t*\tinflictor,\n  mobj_t*\tsource,\n  int \t\tdamage )\n{\n    unsigned\tang;\n    int\t\tsaved;\n    player_t*\tplayer;\n    fixed_t\tthrust;\n    int\t\ttemp;\n\t\n    if ( !(target->flags & MF_SHOOTABLE) )\n\treturn;\t// shouldn't happen...\n\t\t\n    if (target->health <= 0)\n\treturn;\n\n    if ( target->flags & MF_SKULLFLY )\n    {\n\ttarget->momx = target->momy = target->momz = 0;\n    }\n\t\n    player = target->player;\n    if (player && gameskill == sk_baby)\n\tdamage >>= 1; \t// take half damage in trainer mode\n\t\t\n\n    // Some close combat weapons should not\n    // inflict thrust and push the victim out of reach,\n    // thus kick away unless using the chainsaw.\n    if (inflictor\n\t&& !(target->flags & MF_NOCLIP)\n\t&& (!source\n\t    || !source->player\n\t    || source->player->readyweapon != wp_chainsaw))\n    {\n\tang = R_PointToAngle2 ( inflictor->x,\n\t\t\t\tinflictor->y,\n\t\t\t\ttarget->x,\n\t\t\t\ttarget->y);\n\t\t\n\tthrust = damage*(FRACUNIT>>3)*100/target->info->mass;\n\n\t// make fall forwards sometimes\n\tif ( damage < 40\n\t     && damage > target->health\n\t     && target->z - inflictor->z > 64*FRACUNIT\n\t     && (P_Random ()&1) )\n\t{\n\t    ang += ANG180;\n\t    thrust *= 4;\n\t}\n\t\t\n\tang >>= ANGLETOFINESHIFT;\n\ttarget->momx += FixedMul (thrust, finecosine[ang]);\n\ttarget->momy += FixedMul (thrust, finesine[ang]);\n    }\n    \n    // player specific\n    if (player)\n    {\n\t// end of game hell hack\n\tif (target->subsector->sector->special == 11\n\t    && damage >= target->health)\n\t{\n\t    damage = target->health - 1;\n\t}\n\t\n\n\t// Below certain threshold,\n\t// ignore damage in GOD mode, or with INVUL power.\n\tif ( damage < 1000\n\t     && ( (player->cheats&CF_GODMODE)\n\t\t  || player->powers[pw_invulnerability] ) )\n\t{\n\t    return;\n\t}\n\t\n\tif (player->armortype)\n\t{\n\t    if (player->armortype == 1)\n\t\tsaved = damage/3;\n\t    else\n\t\tsaved = damage/2;\n\t    \n\t    if (player->armorpoints <= saved)\n\t    {\n\t\t// armor is used up\n\t\tsaved = player->armorpoints;\n\t\tplayer->armortype = 0;\n\t    }\n\t    player->armorpoints -= saved;\n\t    damage -= saved;\n\t}\n\tplayer->health -= damage; \t// mirror mobj health here for Dave\n\tif (player->health < 0)\n\t    player->health = 0;\n\t\n\tplayer->attacker = source;\n\tplayer->damagecount += damage;\t// add damage after armor / invuln\n\n\tif (player->damagecount > 100)\n\t    player->damagecount = 100;\t// teleport stomp does 10k points...\n\t\n\ttemp = damage < 100 ? damage : 100;\n\n\tif (player == &players[consoleplayer])\n\t    I_Tactile (40,10,40+temp*2);\n    }\n    \n    // do the damage\t\n    target->health -= damage;\t\n    if (target->health <= 0)\n    {\n\tP_KillMobj (source, target);\n\treturn;\n    }\n\n    if ( (P_Random () < target->info->painchance)\n\t && !(target->flags&MF_SKULLFLY) )\n    {\n\ttarget->flags |= MF_JUSTHIT;\t// fight back!\n\t\n\tP_SetMobjState (target, target->info->painstate);\n    }\n\t\t\t\n    target->reactiontime = 0;\t\t// we're awake now...\t\n\n    if ( (!target->threshold || target->type == MT_VILE)\n\t && source && source != target\n\t && source->type != MT_VILE)\n    {\n\t// if not intent on another player,\n\t// chase after this one\n\ttarget->target = source;\n\ttarget->threshold = BASETHRESHOLD;\n\tif (target->state == &states[target->info->spawnstate]\n\t    && target->info->seestate != S_NULL)\n\t    P_SetMobjState (target, target->info->seestate);\n    }\n\t\t\t\n}\n\n"
  },
  {
    "path": "fbdoom/p_inter.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\n//\n\n\n#ifndef __P_INTER__\n#define __P_INTER__\n\n\n\n\nboolean\tP_GivePower(player_t*, int);\n\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/p_lights.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tHandle Sector base lighting effects.\n//\tMuzzle flash?\n//\n\n\n\n#include \"z_zone.h\"\n#include \"m_random.h\"\n\n#include \"doomdef.h\"\n#include \"p_local.h\"\n\n\n// State.\n#include \"r_state.h\"\n\n//\n// FIRELIGHT FLICKER\n//\n\n//\n// T_FireFlicker\n//\nvoid T_FireFlicker (fireflicker_t* flick)\n{\n    int\tamount;\n\t\n    if (--flick->count)\n\treturn;\n\t\n    amount = (P_Random()&3)*16;\n    \n    if (flick->sector->lightlevel - amount < flick->minlight)\n\tflick->sector->lightlevel = flick->minlight;\n    else\n\tflick->sector->lightlevel = flick->maxlight - amount;\n\n    flick->count = 4;\n}\n\n\n\n//\n// P_SpawnFireFlicker\n//\nvoid P_SpawnFireFlicker (sector_t*\tsector)\n{\n    fireflicker_t*\tflick;\n\t\n    // Note that we are resetting sector attributes.\n    // Nothing special about it during gameplay.\n    sector->special = 0; \n\t\n    flick = Z_Malloc ( sizeof(*flick), PU_LEVSPEC, 0);\n\n    P_AddThinker (&flick->thinker);\n\n    flick->thinker.function.acp1 = (actionf_p1) T_FireFlicker;\n    flick->sector = sector;\n    flick->maxlight = sector->lightlevel;\n    flick->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel)+16;\n    flick->count = 4;\n}\n\n\n\n//\n// BROKEN LIGHT FLASHING\n//\n\n\n//\n// T_LightFlash\n// Do flashing lights.\n//\nvoid T_LightFlash (lightflash_t* flash)\n{\n    if (--flash->count)\n\treturn;\n\t\n    if (flash->sector->lightlevel == flash->maxlight)\n    {\n\tflash-> sector->lightlevel = flash->minlight;\n\tflash->count = (P_Random()&flash->mintime)+1;\n    }\n    else\n    {\n\tflash-> sector->lightlevel = flash->maxlight;\n\tflash->count = (P_Random()&flash->maxtime)+1;\n    }\n\n}\n\n\n\n\n//\n// P_SpawnLightFlash\n// After the map has been loaded, scan each sector\n// for specials that spawn thinkers\n//\nvoid P_SpawnLightFlash (sector_t*\tsector)\n{\n    lightflash_t*\tflash;\n\n    // nothing special about it during gameplay\n    sector->special = 0;\t\n\t\n    flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0);\n\n    P_AddThinker (&flash->thinker);\n\n    flash->thinker.function.acp1 = (actionf_p1) T_LightFlash;\n    flash->sector = sector;\n    flash->maxlight = sector->lightlevel;\n\n    flash->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel);\n    flash->maxtime = 64;\n    flash->mintime = 7;\n    flash->count = (P_Random()&flash->maxtime)+1;\n}\n\n\n\n//\n// STROBE LIGHT FLASHING\n//\n\n\n//\n// T_StrobeFlash\n//\nvoid T_StrobeFlash (strobe_t*\t\tflash)\n{\n    if (--flash->count)\n\treturn;\n\t\n    if (flash->sector->lightlevel == flash->minlight)\n    {\n\tflash-> sector->lightlevel = flash->maxlight;\n\tflash->count = flash->brighttime;\n    }\n    else\n    {\n\tflash-> sector->lightlevel = flash->minlight;\n\tflash->count =flash->darktime;\n    }\n\n}\n\n\n\n//\n// P_SpawnStrobeFlash\n// After the map has been loaded, scan each sector\n// for specials that spawn thinkers\n//\nvoid\nP_SpawnStrobeFlash\n( sector_t*\tsector,\n  int\t\tfastOrSlow,\n  int\t\tinSync )\n{\n    strobe_t*\tflash;\n\t\n    flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0);\n\n    P_AddThinker (&flash->thinker);\n\n    flash->sector = sector;\n    flash->darktime = fastOrSlow;\n    flash->brighttime = STROBEBRIGHT;\n    flash->thinker.function.acp1 = (actionf_p1) T_StrobeFlash;\n    flash->maxlight = sector->lightlevel;\n    flash->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel);\n\t\t\n    if (flash->minlight == flash->maxlight)\n\tflash->minlight = 0;\n\n    // nothing special about it during gameplay\n    sector->special = 0;\t\n\n    if (!inSync)\n\tflash->count = (P_Random()&7)+1;\n    else\n\tflash->count = 1;\n}\n\n\n//\n// Start strobing lights (usually from a trigger)\n//\nvoid EV_StartLightStrobing(line_t*\tline)\n{\n    int\t\tsecnum;\n    sector_t*\tsec;\n\t\n    secnum = -1;\n    while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)\n    {\n\tsec = &sectors[secnum];\n\tif (sec->specialdata)\n\t    continue;\n\t\n\tP_SpawnStrobeFlash (sec,SLOWDARK, 0);\n    }\n}\n\n\n\n//\n// TURN LINE'S TAG LIGHTS OFF\n//\nvoid EV_TurnTagLightsOff(line_t* line)\n{\n    int\t\t\ti;\n    int\t\t\tj;\n    int\t\t\tmin;\n    sector_t*\t\tsector;\n    sector_t*\t\ttsec;\n    line_t*\t\ttempline;\n\t\n    sector = sectors;\n    \n    for (j = 0;j < numsectors; j++, sector++)\n    {\n\tif (sector->tag == line->tag)\n\t{\n\t    min = sector->lightlevel;\n\t    for (i = 0;i < sector->linecount; i++)\n\t    {\n\t\ttempline = sector->lines[i];\n\t\ttsec = getNextSector(templine,sector);\n\t\tif (!tsec)\n\t\t    continue;\n\t\tif (tsec->lightlevel < min)\n\t\t    min = tsec->lightlevel;\n\t    }\n\t    sector->lightlevel = min;\n\t}\n    }\n}\n\n\n//\n// TURN LINE'S TAG LIGHTS ON\n//\nvoid\nEV_LightTurnOn\n( line_t*\tline,\n  int\t\tbright )\n{\n    int\t\ti;\n    int\t\tj;\n    sector_t*\tsector;\n    sector_t*\ttemp;\n    line_t*\ttempline;\n\t\n    sector = sectors;\n\t\n    for (i=0;i<numsectors;i++, sector++)\n    {\n\tif (sector->tag == line->tag)\n\t{\n\t    // bright = 0 means to search\n\t    // for highest light level\n\t    // surrounding sector\n\t    if (!bright)\n\t    {\n\t\tfor (j = 0;j < sector->linecount; j++)\n\t\t{\n\t\t    templine = sector->lines[j];\n\t\t    temp = getNextSector(templine,sector);\n\n\t\t    if (!temp)\n\t\t\tcontinue;\n\n\t\t    if (temp->lightlevel > bright)\n\t\t\tbright = temp->lightlevel;\n\t\t}\n\t    }\n\t    sector-> lightlevel = bright;\n\t}\n    }\n}\n\n    \n//\n// Spawn glowing light\n//\n\nvoid T_Glow(glow_t*\tg)\n{\n    switch(g->direction)\n    {\n      case -1:\n\t// DOWN\n\tg->sector->lightlevel -= GLOWSPEED;\n\tif (g->sector->lightlevel <= g->minlight)\n\t{\n\t    g->sector->lightlevel += GLOWSPEED;\n\t    g->direction = 1;\n\t}\n\tbreak;\n\t\n      case 1:\n\t// UP\n\tg->sector->lightlevel += GLOWSPEED;\n\tif (g->sector->lightlevel >= g->maxlight)\n\t{\n\t    g->sector->lightlevel -= GLOWSPEED;\n\t    g->direction = -1;\n\t}\n\tbreak;\n    }\n}\n\n\nvoid P_SpawnGlowingLight(sector_t*\tsector)\n{\n    glow_t*\tg;\n\t\n    g = Z_Malloc( sizeof(*g), PU_LEVSPEC, 0);\n\n    P_AddThinker(&g->thinker);\n\n    g->sector = sector;\n    g->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel);\n    g->maxlight = sector->lightlevel;\n    g->thinker.function.acp1 = (actionf_p1) T_Glow;\n    g->direction = -1;\n\n    sector->special = 0;\n}\n\n"
  },
  {
    "path": "fbdoom/p_local.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tPlay functions, animation, global header.\n//\n\n\n#ifndef __P_LOCAL__\n#define __P_LOCAL__\n\n#ifndef __R_LOCAL__\n#include \"r_local.h\"\n#endif\n\n#define FLOATSPEED\t\t(FRACUNIT*4)\n\n\n#define MAXHEALTH\t\t100\n#define VIEWHEIGHT\t\t(41*FRACUNIT)\n\n// mapblocks are used to check movement\n// against lines and things\n#define MAPBLOCKUNITS\t128\n#define MAPBLOCKSIZE\t(MAPBLOCKUNITS*FRACUNIT)\n#define MAPBLOCKSHIFT\t(FRACBITS+7)\n#define MAPBMASK\t\t(MAPBLOCKSIZE-1)\n#define MAPBTOFRAC\t\t(MAPBLOCKSHIFT-FRACBITS)\n\n\n// player radius for movement checking\n#define PLAYERRADIUS\t16*FRACUNIT\n\n// MAXRADIUS is for precalculated sector block boxes\n// the spider demon is larger,\n// but we do not have any moving sectors nearby\n#define MAXRADIUS\t\t32*FRACUNIT\n\n#define GRAVITY\t\tFRACUNIT\n#define MAXMOVE\t\t(30*FRACUNIT)\n\n#define USERANGE\t\t(64*FRACUNIT)\n#define MELEERANGE\t\t(64*FRACUNIT)\n#define MISSILERANGE\t(32*64*FRACUNIT)\n\n// follow a player exlusively for 3 seconds\n#define\tBASETHRESHOLD\t \t100\n\n\n\n//\n// P_TICK\n//\n\n// both the head and tail of the thinker list\nextern\tthinker_t\tthinkercap;\t\n\n\nvoid P_InitThinkers (void);\nvoid P_AddThinker (thinker_t* thinker);\nvoid P_RemoveThinker (thinker_t* thinker);\n\n\n//\n// P_PSPR\n//\nvoid P_SetupPsprites (player_t* curplayer);\nvoid P_MovePsprites (player_t* curplayer);\nvoid P_DropWeapon (player_t* player);\n\n\n//\n// P_USER\n//\nvoid\tP_PlayerThink (player_t* player);\n\n\n//\n// P_MOBJ\n//\n#define ONFLOORZ\t\tINT_MIN\n#define ONCEILINGZ\t\tINT_MAX\n\n// Time interval for item respawning.\n#define ITEMQUESIZE\t\t128\n\nextern mapthing_t\titemrespawnque[ITEMQUESIZE];\nextern int\t\titemrespawntime[ITEMQUESIZE];\nextern int\t\tiquehead;\nextern int\t\tiquetail;\n\n\nvoid P_RespawnSpecials (void);\n\nmobj_t*\nP_SpawnMobj\n( fixed_t\tx,\n  fixed_t\ty,\n  fixed_t\tz,\n  mobjtype_t\ttype );\n\nvoid \tP_RemoveMobj (mobj_t* th);\nmobj_t* P_SubstNullMobj (mobj_t* th);\nboolean\tP_SetMobjState (mobj_t* mobj, statenum_t state);\nvoid \tP_MobjThinker (mobj_t* mobj);\n\nvoid\tP_SpawnPuff (fixed_t x, fixed_t y, fixed_t z);\nvoid \tP_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, int damage);\nmobj_t* P_SpawnMissile (mobj_t* source, mobj_t* dest, mobjtype_t type);\nvoid\tP_SpawnPlayerMissile (mobj_t* source, mobjtype_t type);\n\n\n//\n// P_ENEMY\n//\nvoid P_NoiseAlert (mobj_t* target, mobj_t* emmiter);\n\n\n//\n// P_MAPUTL\n//\ntypedef struct\n{\n    fixed_t\tx;\n    fixed_t\ty;\n    fixed_t\tdx;\n    fixed_t\tdy;\n    \n} divline_t;\n\ntypedef struct\n{\n    fixed_t\tfrac;\t\t// along trace line\n    boolean\tisaline;\n    union {\n\tmobj_t*\tthing;\n\tline_t*\tline;\n    }\t\t\td;\n} intercept_t;\n\n// Extended MAXINTERCEPTS, to allow for intercepts overrun emulation.\n\n#define MAXINTERCEPTS_ORIGINAL 128\n#define MAXINTERCEPTS          (MAXINTERCEPTS_ORIGINAL + 61)\n\nextern intercept_t\tintercepts[MAXINTERCEPTS];\nextern intercept_t*\tintercept_p;\n\ntypedef boolean (*traverser_t) (intercept_t *in);\n\nfixed_t P_AproxDistance (fixed_t dx, fixed_t dy);\nint \tP_PointOnLineSide (fixed_t x, fixed_t y, line_t* line);\nint \tP_PointOnDivlineSide (fixed_t x, fixed_t y, divline_t* line);\nvoid \tP_MakeDivline (line_t* li, divline_t* dl);\nfixed_t P_InterceptVector (divline_t* v2, divline_t* v1);\nint \tP_BoxOnLineSide (fixed_t* tmbox, line_t* ld);\n\nextern fixed_t\t\topentop;\nextern fixed_t \t\topenbottom;\nextern fixed_t\t\topenrange;\nextern fixed_t\t\tlowfloor;\n\nvoid \tP_LineOpening (line_t* linedef);\n\nboolean P_BlockLinesIterator (int x, int y, boolean(*func)(line_t*) );\nboolean P_BlockThingsIterator (int x, int y, boolean(*func)(mobj_t*) );\n\n#define PT_ADDLINES\t\t1\n#define PT_ADDTHINGS\t2\n#define PT_EARLYOUT\t\t4\n\nextern divline_t\ttrace;\n\nboolean\nP_PathTraverse\n( fixed_t\tx1,\n  fixed_t\ty1,\n  fixed_t\tx2,\n  fixed_t\ty2,\n  int\t\tflags,\n  boolean\t(*trav) (intercept_t *));\n\nvoid P_UnsetThingPosition (mobj_t* thing);\nvoid P_SetThingPosition (mobj_t* thing);\n\n\n//\n// P_MAP\n//\n\n// If \"floatok\" true, move would be ok\n// if within \"tmfloorz - tmceilingz\".\nextern boolean\t\tfloatok;\nextern fixed_t\t\ttmfloorz;\nextern fixed_t\t\ttmceilingz;\n\n\nextern\tline_t*\t\tceilingline;\n\n// fraggle: I have increased the size of this buffer.  In the original Doom,\n// overrunning past this limit caused other bits of memory to be overwritten,\n// affecting demo playback.  However, in doing so, the limit was still\n// exceeded.  So we have to support more than 8 specials.\n//\n// We keep the original limit, to detect what variables in memory were\n// overwritten (see SpechitOverrun())\n\n#define MAXSPECIALCROSS \t\t20\n#define MAXSPECIALCROSS_ORIGINAL\t8\n\nextern\tline_t*\tspechit[MAXSPECIALCROSS];\nextern\tint\tnumspechit;\n\nboolean P_CheckPosition (mobj_t *thing, fixed_t x, fixed_t y);\nboolean P_TryMove (mobj_t* thing, fixed_t x, fixed_t y);\nboolean P_TeleportMove (mobj_t* thing, fixed_t x, fixed_t y);\nvoid\tP_SlideMove (mobj_t* mo);\nboolean P_CheckSight (mobj_t* t1, mobj_t* t2);\nvoid \tP_UseLines (player_t* player);\n\nboolean P_ChangeSector (sector_t* sector, boolean crunch);\n\nextern mobj_t*\tlinetarget;\t// who got hit (or NULL)\n\nfixed_t\nP_AimLineAttack\n( mobj_t*\tt1,\n  angle_t\tangle,\n  fixed_t\tdistance );\n\nvoid\nP_LineAttack\n( mobj_t*\tt1,\n  angle_t\tangle,\n  fixed_t\tdistance,\n  fixed_t\tslope,\n  int\t\tdamage );\n\nvoid\nP_RadiusAttack\n( mobj_t*\tspot,\n  mobj_t*\tsource,\n  int\t\tdamage );\n\n\n\n//\n// P_SETUP\n//\nextern byte*\t\trejectmatrix;\t// for fast sight rejection\nextern short*\t\tblockmaplump;\t// offsets in blockmap are from here\nextern short*\t\tblockmap;\nextern int\t\tbmapwidth;\nextern int\t\tbmapheight;\t// in mapblocks\nextern fixed_t\t\tbmaporgx;\nextern fixed_t\t\tbmaporgy;\t// origin of block map\nextern mobj_t**\t\tblocklinks;\t// for thing chains\n\n\n\n//\n// P_INTER\n//\nextern int\t\tmaxammo[NUMAMMO];\nextern int\t\tclipammo[NUMAMMO];\n\nvoid\nP_TouchSpecialThing\n( mobj_t*\tspecial,\n  mobj_t*\ttoucher );\n\nvoid\nP_DamageMobj\n( mobj_t*\ttarget,\n  mobj_t*\tinflictor,\n  mobj_t*\tsource,\n  int\t\tdamage );\n\n\n//\n// P_SPEC\n//\n#include \"p_spec.h\"\n\n\n#endif\t// __P_LOCAL__\n"
  },
  {
    "path": "fbdoom/p_map.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard, Andrey Budko\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tMovement, collision handling.\n//\tShooting and aiming.\n//\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"deh_misc.h\"\n\n#include \"m_bbox.h\"\n#include \"m_random.h\"\n#include \"i_system.h\"\n\n#include \"doomdef.h\"\n#include \"m_argv.h\"\n#include \"m_misc.h\"\n#include \"p_local.h\"\n\n#include \"s_sound.h\"\n\n// State.\n#include \"doomstat.h\"\n#include \"r_state.h\"\n// Data.\n#include \"sounds.h\"\n\n// Spechit overrun magic value.\n//\n// This is the value used by PrBoom-plus.  I think the value below is \n// actually better and works with more demos.  However, I think\n// it's better for the spechits emulation to be compatible with\n// PrBoom-plus, at least so that the big spechits emulation list\n// on Doomworld can also be used with Chocolate Doom.\n\n#define DEFAULT_SPECHIT_MAGIC 0x01C09C98\n\n// This is from a post by myk on the Doomworld forums, \n// outputted from entryway's spechit_magic generator for\n// s205n546.lmp.  The _exact_ value of this isn't too\n// important; as long as it is in the right general\n// range, it will usually work.  Otherwise, we can use\n// the generator (hacked doom2.exe) and provide it \n// with -spechit.\n\n//#define DEFAULT_SPECHIT_MAGIC 0x84f968e8\n\n\nfixed_t\t\ttmbbox[4];\nmobj_t*\t\ttmthing;\nint\t\ttmflags;\nfixed_t\t\ttmx;\nfixed_t\t\ttmy;\n\n\n// If \"floatok\" true, move would be ok\n// if within \"tmfloorz - tmceilingz\".\nboolean\t\tfloatok;\n\nfixed_t\t\ttmfloorz;\nfixed_t\t\ttmceilingz;\nfixed_t\t\ttmdropoffz;\n\n// keep track of the line that lowers the ceiling,\n// so missiles don't explode against sky hack walls\nline_t*\t\tceilingline;\n\n// keep track of special lines as they are hit,\n// but don't process them until the move is proven valid\n\nline_t*\t\tspechit[MAXSPECIALCROSS];\nint\t\tnumspechit;\n\n\n\n//\n// TELEPORT MOVE\n// \n\n//\n// PIT_StompThing\n//\nboolean PIT_StompThing (mobj_t* thing)\n{\n    fixed_t\tblockdist;\n\t\t\n    if (!(thing->flags & MF_SHOOTABLE) )\n\treturn true;\n\t\t\n    blockdist = thing->radius + tmthing->radius;\n    \n    if ( abs(thing->x - tmx) >= blockdist\n\t || abs(thing->y - tmy) >= blockdist )\n    {\n\t// didn't hit it\n\treturn true;\n    }\n    \n    // don't clip against self\n    if (thing == tmthing)\n\treturn true;\n    \n    // monsters don't stomp things except on boss level\n    if ( !tmthing->player && gamemap != 30)\n\treturn false;\t\n\t\t\n    P_DamageMobj (thing, tmthing, tmthing, 10000);\n\t\n    return true;\n}\n\n\n//\n// P_TeleportMove\n//\nboolean\nP_TeleportMove\n( mobj_t*\tthing,\n  fixed_t\tx,\n  fixed_t\ty )\n{\n    int\t\t\txl;\n    int\t\t\txh;\n    int\t\t\tyl;\n    int\t\t\tyh;\n    int\t\t\tbx;\n    int\t\t\tby;\n    \n    subsector_t*\tnewsubsec;\n    \n    // kill anything occupying the position\n    tmthing = thing;\n    tmflags = thing->flags;\n\t\n    tmx = x;\n    tmy = y;\n\t\n    tmbbox[BOXTOP] = y + tmthing->radius;\n    tmbbox[BOXBOTTOM] = y - tmthing->radius;\n    tmbbox[BOXRIGHT] = x + tmthing->radius;\n    tmbbox[BOXLEFT] = x - tmthing->radius;\n\n    newsubsec = R_PointInSubsector (x,y);\n    ceilingline = NULL;\n    \n    // The base floor/ceiling is from the subsector\n    // that contains the point.\n    // Any contacted lines the step closer together\n    // will adjust them.\n    tmfloorz = tmdropoffz = newsubsec->sector->floorheight;\n    tmceilingz = newsubsec->sector->ceilingheight;\n\t\t\t\n    validcount++;\n    numspechit = 0;\n    \n    // stomp on any things contacted\n    xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;\n    xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;\n    yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;\n    yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;\n\n    for (bx=xl ; bx<=xh ; bx++)\n\tfor (by=yl ; by<=yh ; by++)\n\t    if (!P_BlockThingsIterator(bx,by,PIT_StompThing))\n\t\treturn false;\n    \n    // the move is ok,\n    // so link the thing into its new position\n    P_UnsetThingPosition (thing);\n\n    thing->floorz = tmfloorz;\n    thing->ceilingz = tmceilingz;\t\n    thing->x = x;\n    thing->y = y;\n\n    P_SetThingPosition (thing);\n\t\n    return true;\n}\n\n\n//\n// MOVEMENT ITERATOR FUNCTIONS\n//\n\nstatic void SpechitOverrun(line_t *ld);\n\n//\n// PIT_CheckLine\n// Adjusts tmfloorz and tmceilingz as lines are contacted\n//\nboolean PIT_CheckLine (line_t* ld)\n{\n    if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]\n\t|| tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]\n\t|| tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]\n\t|| tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] )\n\treturn true;\n\n    if (P_BoxOnLineSide (tmbbox, ld) != -1)\n\treturn true;\n\t\t\n    // A line has been hit\n    \n    // The moving thing's destination position will cross\n    // the given line.\n    // If this should not be allowed, return false.\n    // If the line is special, keep track of it\n    // to process later if the move is proven ok.\n    // NOTE: specials are NOT sorted by order,\n    // so two special lines that are only 8 pixels apart\n    // could be crossed in either order.\n    \n    if (!ld->backsector)\n\treturn false;\t\t// one sided line\n\t\t\n    if (!(tmthing->flags & MF_MISSILE) )\n    {\n\tif ( ld->flags & ML_BLOCKING )\n\t    return false;\t// explicitly blocking everything\n\n\tif ( !tmthing->player && ld->flags & ML_BLOCKMONSTERS )\n\t    return false;\t// block monsters only\n    }\n\n    // set openrange, opentop, openbottom\n    P_LineOpening (ld);\t\n\t\n    // adjust floor / ceiling heights\n    if (opentop < tmceilingz)\n    {\n\ttmceilingz = opentop;\n\tceilingline = ld;\n    }\n\n    if (openbottom > tmfloorz)\n\ttmfloorz = openbottom;\t\n\n    if (lowfloor < tmdropoffz)\n\ttmdropoffz = lowfloor;\n\t\t\n    // if contacted a special line, add it to the list\n    if (ld->special)\n    {\n        spechit[numspechit] = ld;\n\tnumspechit++;\n\n        // fraggle: spechits overrun emulation code from prboom-plus\n        if (numspechit > MAXSPECIALCROSS_ORIGINAL)\n        {\n            SpechitOverrun(ld);\n        }\n    }\n\n    return true;\n}\n\n//\n// PIT_CheckThing\n//\nboolean PIT_CheckThing (mobj_t* thing)\n{\n    fixed_t\t\tblockdist;\n    boolean\t\tsolid;\n    int\t\t\tdamage;\n\t\t\n    if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) ))\n\treturn true;\n    \n    blockdist = thing->radius + tmthing->radius;\n\n    if ( abs(thing->x - tmx) >= blockdist\n\t || abs(thing->y - tmy) >= blockdist )\n    {\n\t// didn't hit it\n\treturn true;\t\n    }\n    \n    // don't clip against self\n    if (thing == tmthing)\n\treturn true;\n    \n    // check for skulls slamming into things\n    if (tmthing->flags & MF_SKULLFLY)\n    {\n\tdamage = ((P_Random()%8)+1)*tmthing->info->damage;\n\t\n\tP_DamageMobj (thing, tmthing, tmthing, damage);\n\t\n\ttmthing->flags &= ~MF_SKULLFLY;\n\ttmthing->momx = tmthing->momy = tmthing->momz = 0;\n\t\n\tP_SetMobjState (tmthing, tmthing->info->spawnstate);\n\t\n\treturn false;\t\t// stop moving\n    }\n\n    \n    // missiles can hit other things\n    if (tmthing->flags & MF_MISSILE)\n    {\n\t// see if it went over / under\n\tif (tmthing->z > thing->z + thing->height)\n\t    return true;\t\t// overhead\n\tif (tmthing->z+tmthing->height < thing->z)\n\t    return true;\t\t// underneath\n\t\t\n\tif (tmthing->target \n         && (tmthing->target->type == thing->type || \n\t    (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)||\n\t    (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT) ) )\n\t{\n\t    // Don't hit same species as originator.\n\t    if (thing == tmthing->target)\n\t\treturn true;\n\n            // sdh: Add deh_species_infighting here.  We can override the\n            // \"monsters of the same species cant hurt each other\" behavior\n            // through dehacked patches\n\n\t    if (thing->type != MT_PLAYER && !deh_species_infighting)\n\t    {\n\t\t// Explode, but do no damage.\n\t\t// Let players missile other players.\n\t\treturn false;\n\t    }\n\t}\n\t\n\tif (! (thing->flags & MF_SHOOTABLE) )\n\t{\n\t    // didn't do any damage\n\t    return !(thing->flags & MF_SOLID);\t\n\t}\n\t\n\t// damage / explode\n\tdamage = ((P_Random()%8)+1)*tmthing->info->damage;\n\tP_DamageMobj (thing, tmthing, tmthing->target, damage);\n\n\t// don't traverse any more\n\treturn false;\t\t\t\t\n    }\n    \n    // check for special pickup\n    if (thing->flags & MF_SPECIAL)\n    {\n\tsolid = thing->flags&MF_SOLID;\n\tif (tmflags&MF_PICKUP)\n\t{\n\t    // can remove thing\n\t    P_TouchSpecialThing (thing, tmthing);\n\t}\n\treturn !solid;\n    }\n\t\n    return !(thing->flags & MF_SOLID);\n}\n\n\n//\n// MOVEMENT CLIPPING\n//\n\n//\n// P_CheckPosition\n// This is purely informative, nothing is modified\n// (except things picked up).\n// \n// in:\n//  a mobj_t (can be valid or invalid)\n//  a position to be checked\n//   (doesn't need to be related to the mobj_t->x,y)\n//\n// during:\n//  special things are touched if MF_PICKUP\n//  early out on solid lines?\n//\n// out:\n//  newsubsec\n//  floorz\n//  ceilingz\n//  tmdropoffz\n//   the lowest point contacted\n//   (monsters won't move to a dropoff)\n//  speciallines[]\n//  numspeciallines\n//\nboolean\nP_CheckPosition\n( mobj_t*\tthing,\n  fixed_t\tx,\n  fixed_t\ty )\n{\n    int\t\t\txl;\n    int\t\t\txh;\n    int\t\t\tyl;\n    int\t\t\tyh;\n    int\t\t\tbx;\n    int\t\t\tby;\n    subsector_t*\tnewsubsec;\n\n    tmthing = thing;\n    tmflags = thing->flags;\n\t\n    tmx = x;\n    tmy = y;\n\t\n    tmbbox[BOXTOP] = y + tmthing->radius;\n    tmbbox[BOXBOTTOM] = y - tmthing->radius;\n    tmbbox[BOXRIGHT] = x + tmthing->radius;\n    tmbbox[BOXLEFT] = x - tmthing->radius;\n\n    newsubsec = R_PointInSubsector (x,y);\n    ceilingline = NULL;\n    \n    // The base floor / ceiling is from the subsector\n    // that contains the point.\n    // Any contacted lines the step closer together\n    // will adjust them.\n    tmfloorz = tmdropoffz = newsubsec->sector->floorheight;\n    tmceilingz = newsubsec->sector->ceilingheight;\n\t\t\t\n    validcount++;\n    numspechit = 0;\n\n    if ( tmflags & MF_NOCLIP )\n\treturn true;\n    \n    // Check things first, possibly picking things up.\n    // The bounding box is extended by MAXRADIUS\n    // because mobj_ts are grouped into mapblocks\n    // based on their origin point, and can overlap\n    // into adjacent blocks by up to MAXRADIUS units.\n    xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;\n    xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;\n    yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;\n    yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;\n\n    for (bx=xl ; bx<=xh ; bx++)\n\tfor (by=yl ; by<=yh ; by++)\n\t    if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))\n\t\treturn false;\n    \n    // check lines\n    xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;\n    xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;\n    yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;\n    yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;\n\n    for (bx=xl ; bx<=xh ; bx++)\n\tfor (by=yl ; by<=yh ; by++)\n\t    if (!P_BlockLinesIterator (bx,by,PIT_CheckLine))\n\t\treturn false;\n\n    return true;\n}\n\n\n//\n// P_TryMove\n// Attempt to move to a new position,\n// crossing special lines unless MF_TELEPORT is set.\n//\nboolean\nP_TryMove\n( mobj_t*\tthing,\n  fixed_t\tx,\n  fixed_t\ty )\n{\n    fixed_t\toldx;\n    fixed_t\toldy;\n    int\t\tside;\n    int\t\toldside;\n    line_t*\tld;\n\n    floatok = false;\n    if (!P_CheckPosition (thing, x, y))\n\treturn false;\t\t// solid wall or thing\n    \n    if ( !(thing->flags & MF_NOCLIP) )\n    {\n\tif (tmceilingz - tmfloorz < thing->height)\n\t    return false;\t// doesn't fit\n\n\tfloatok = true;\n\t\n\tif ( !(thing->flags&MF_TELEPORT) \n\t     &&tmceilingz - thing->z < thing->height)\n\t    return false;\t// mobj must lower itself to fit\n\n\tif ( !(thing->flags&MF_TELEPORT)\n\t     && tmfloorz - thing->z > 24*FRACUNIT )\n\t    return false;\t// too big a step up\n\n\tif ( !(thing->flags&(MF_DROPOFF|MF_FLOAT))\n\t     && tmfloorz - tmdropoffz > 24*FRACUNIT )\n\t    return false;\t// don't stand over a dropoff\n    }\n    \n    // the move is ok,\n    // so link the thing into its new position\n    P_UnsetThingPosition (thing);\n\n    oldx = thing->x;\n    oldy = thing->y;\n    thing->floorz = tmfloorz;\n    thing->ceilingz = tmceilingz;\t\n    thing->x = x;\n    thing->y = y;\n\n    P_SetThingPosition (thing);\n    \n    // if any special lines were hit, do the effect\n    if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) )\n    {\n\twhile (numspechit--)\n\t{\n\t    // see if the line was crossed\n\t    ld = spechit[numspechit];\n\t    side = P_PointOnLineSide (thing->x, thing->y, ld);\n\t    oldside = P_PointOnLineSide (oldx, oldy, ld);\n\t    if (side != oldside)\n\t    {\n\t\tif (ld->special)\n\t\t    P_CrossSpecialLine (ld-lines, oldside, thing);\n\t    }\n\t}\n    }\n\n    return true;\n}\n\n\n//\n// P_ThingHeightClip\n// Takes a valid thing and adjusts the thing->floorz,\n// thing->ceilingz, and possibly thing->z.\n// This is called for all nearby monsters\n// whenever a sector changes height.\n// If the thing doesn't fit,\n// the z will be set to the lowest value\n// and false will be returned.\n//\nboolean P_ThingHeightClip (mobj_t* thing)\n{\n    boolean\t\tonfloor;\n\t\n    onfloor = (thing->z == thing->floorz);\n\t\n    P_CheckPosition (thing, thing->x, thing->y);\t\n    // what about stranding a monster partially off an edge?\n\t\n    thing->floorz = tmfloorz;\n    thing->ceilingz = tmceilingz;\n\t\n    if (onfloor)\n    {\n\t// walking monsters rise and fall with the floor\n\tthing->z = thing->floorz;\n    }\n    else\n    {\n\t// don't adjust a floating monster unless forced to\n\tif (thing->z+thing->height > thing->ceilingz)\n\t    thing->z = thing->ceilingz - thing->height;\n    }\n\t\n    if (thing->ceilingz - thing->floorz < thing->height)\n\treturn false;\n\t\t\n    return true;\n}\n\n\n\n//\n// SLIDE MOVE\n// Allows the player to slide along any angled walls.\n//\nfixed_t\t\tbestslidefrac;\nfixed_t\t\tsecondslidefrac;\n\nline_t*\t\tbestslideline;\nline_t*\t\tsecondslideline;\n\nmobj_t*\t\tslidemo;\n\nfixed_t\t\ttmxmove;\nfixed_t\t\ttmymove;\n\n\n\n//\n// P_HitSlideLine\n// Adjusts the xmove / ymove\n// so that the next move will slide along the wall.\n//\nvoid P_HitSlideLine (line_t* ld)\n{\n    int\t\t\tside;\n\n    angle_t\t\tlineangle;\n    angle_t\t\tmoveangle;\n    angle_t\t\tdeltaangle;\n    \n    fixed_t\t\tmovelen;\n    fixed_t\t\tnewlen;\n\t\n\t\n    if (ld->slopetype == ST_HORIZONTAL)\n    {\n\ttmymove = 0;\n\treturn;\n    }\n    \n    if (ld->slopetype == ST_VERTICAL)\n    {\n\ttmxmove = 0;\n\treturn;\n    }\n\t\n    side = P_PointOnLineSide (slidemo->x, slidemo->y, ld);\n\t\n    lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy);\n\n    if (side == 1)\n\tlineangle += ANG180;\n\n    moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove);\n    deltaangle = moveangle-lineangle;\n\n    if (deltaangle > ANG180)\n\tdeltaangle += ANG180;\n    //\tI_Error (\"SlideLine: ang>ANG180\");\n\n    lineangle >>= ANGLETOFINESHIFT;\n    deltaangle >>= ANGLETOFINESHIFT;\n\t\n    movelen = P_AproxDistance (tmxmove, tmymove);\n    newlen = FixedMul (movelen, finecosine[deltaangle]);\n\n    tmxmove = FixedMul (newlen, finecosine[lineangle]);\t\n    tmymove = FixedMul (newlen, finesine[lineangle]);\t\n}\n\n\n//\n// PTR_SlideTraverse\n//\nboolean PTR_SlideTraverse (intercept_t* in)\n{\n    line_t*\tli;\n\t\n    if (!in->isaline)\n\tI_Error (\"PTR_SlideTraverse: not a line?\");\n\t\t\n    li = in->d.line;\n    \n    if ( ! (li->flags & ML_TWOSIDED) )\n    {\n\tif (P_PointOnLineSide (slidemo->x, slidemo->y, li))\n\t{\n\t    // don't hit the back side\n\t    return true;\t\t\n\t}\n\tgoto isblocking;\n    }\n\n    // set openrange, opentop, openbottom\n    P_LineOpening (li);\n    \n    if (openrange < slidemo->height)\n\tgoto isblocking;\t\t// doesn't fit\n\t\t\n    if (opentop - slidemo->z < slidemo->height)\n\tgoto isblocking;\t\t// mobj is too high\n\n    if (openbottom - slidemo->z > 24*FRACUNIT )\n\tgoto isblocking;\t\t// too big a step up\n\n    // this line doesn't block movement\n    return true;\t\t\n\t\n    // the line does block movement,\n    // see if it is closer than best so far\n  isblocking:\t\t\n    if (in->frac < bestslidefrac)\n    {\n\tsecondslidefrac = bestslidefrac;\n\tsecondslideline = bestslideline;\n\tbestslidefrac = in->frac;\n\tbestslideline = li;\n    }\n\t\n    return false;\t// stop\n}\n\n\n\n//\n// P_SlideMove\n// The momx / momy move is bad, so try to slide\n// along a wall.\n// Find the first line hit, move flush to it,\n// and slide along it\n//\n// This is a kludgy mess.\n//\nvoid P_SlideMove (mobj_t* mo)\n{\n    fixed_t\t\tleadx;\n    fixed_t\t\tleady;\n    fixed_t\t\ttrailx;\n    fixed_t\t\ttraily;\n    fixed_t\t\tnewx;\n    fixed_t\t\tnewy;\n    int\t\t\thitcount;\n\t\t\n    slidemo = mo;\n    hitcount = 0;\n    \n  retry:\n    if (++hitcount == 3)\n\tgoto stairstep;\t\t// don't loop forever\n\n    \n    // trace along the three leading corners\n    if (mo->momx > 0)\n    {\n\tleadx = mo->x + mo->radius;\n\ttrailx = mo->x - mo->radius;\n    }\n    else\n    {\n\tleadx = mo->x - mo->radius;\n\ttrailx = mo->x + mo->radius;\n    }\n\t\n    if (mo->momy > 0)\n    {\n\tleady = mo->y + mo->radius;\n\ttraily = mo->y - mo->radius;\n    }\n    else\n    {\n\tleady = mo->y - mo->radius;\n\ttraily = mo->y + mo->radius;\n    }\n\t\t\n    bestslidefrac = FRACUNIT+1;\n\t\n    P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy,\n\t\t     PT_ADDLINES, PTR_SlideTraverse );\n    P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy,\n\t\t     PT_ADDLINES, PTR_SlideTraverse );\n    P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy,\n\t\t     PT_ADDLINES, PTR_SlideTraverse );\n    \n    // move up to the wall\n    if (bestslidefrac == FRACUNIT+1)\n    {\n\t// the move most have hit the middle, so stairstep\n      stairstep:\n\tif (!P_TryMove (mo, mo->x, mo->y + mo->momy))\n\t    P_TryMove (mo, mo->x + mo->momx, mo->y);\n\treturn;\n    }\n\n    // fudge a bit to make sure it doesn't hit\n    bestslidefrac -= 0x800;\t\n    if (bestslidefrac > 0)\n    {\n\tnewx = FixedMul (mo->momx, bestslidefrac);\n\tnewy = FixedMul (mo->momy, bestslidefrac);\n\t\n\tif (!P_TryMove (mo, mo->x+newx, mo->y+newy))\n\t    goto stairstep;\n    }\n    \n    // Now continue along the wall.\n    // First calculate remainder.\n    bestslidefrac = FRACUNIT-(bestslidefrac+0x800);\n    \n    if (bestslidefrac > FRACUNIT)\n\tbestslidefrac = FRACUNIT;\n    \n    if (bestslidefrac <= 0)\n\treturn;\n    \n    tmxmove = FixedMul (mo->momx, bestslidefrac);\n    tmymove = FixedMul (mo->momy, bestslidefrac);\n\n    P_HitSlideLine (bestslideline);\t// clip the moves\n\n    mo->momx = tmxmove;\n    mo->momy = tmymove;\n\t\t\n    if (!P_TryMove (mo, mo->x+tmxmove, mo->y+tmymove))\n    {\n\tgoto retry;\n    }\n}\n\n\n//\n// P_LineAttack\n//\nmobj_t*\t\tlinetarget;\t// who got hit (or NULL)\nmobj_t*\t\tshootthing;\n\n// Height if not aiming up or down\n// ???: use slope for monsters?\nfixed_t\t\tshootz;\t\n\nint\t\tla_damage;\nfixed_t\t\tattackrange;\n\nfixed_t\t\taimslope;\n\n// slopes to top and bottom of target\nextern fixed_t\ttopslope;\nextern fixed_t\tbottomslope;\t\n\n\n//\n// PTR_AimTraverse\n// Sets linetaget and aimslope when a target is aimed at.\n//\nboolean\nPTR_AimTraverse (intercept_t* in)\n{\n    line_t*\t\tli;\n    mobj_t*\t\tth;\n    fixed_t\t\tslope;\n    fixed_t\t\tthingtopslope;\n    fixed_t\t\tthingbottomslope;\n    fixed_t\t\tdist;\n\t\t\n    if (in->isaline)\n    {\n\tli = in->d.line;\n\t\n\tif ( !(li->flags & ML_TWOSIDED) )\n\t    return false;\t\t// stop\n\t\n\t// Crosses a two sided line.\n\t// A two sided line will restrict\n\t// the possible target ranges.\n\tP_LineOpening (li);\n\t\n\tif (openbottom >= opentop)\n\t    return false;\t\t// stop\n\t\n\tdist = FixedMul (attackrange, in->frac);\n\n        if (li->backsector == NULL\n         || li->frontsector->floorheight != li->backsector->floorheight)\n\t{\n\t    slope = FixedDiv (openbottom - shootz , dist);\n\t    if (slope > bottomslope)\n\t\tbottomslope = slope;\n\t}\n\t\t\n\tif (li->backsector == NULL\n         || li->frontsector->ceilingheight != li->backsector->ceilingheight)\n\t{\n\t    slope = FixedDiv (opentop - shootz , dist);\n\t    if (slope < topslope)\n\t\ttopslope = slope;\n\t}\n\t\t\n\tif (topslope <= bottomslope)\n\t    return false;\t\t// stop\n\t\t\t\n\treturn true;\t\t\t// shot continues\n    }\n    \n    // shoot a thing\n    th = in->d.thing;\n    if (th == shootthing)\n\treturn true;\t\t\t// can't shoot self\n    \n    if (!(th->flags&MF_SHOOTABLE))\n\treturn true;\t\t\t// corpse or something\n\n    // check angles to see if the thing can be aimed at\n    dist = FixedMul (attackrange, in->frac);\n    thingtopslope = FixedDiv (th->z+th->height - shootz , dist);\n\n    if (thingtopslope < bottomslope)\n\treturn true;\t\t\t// shot over the thing\n\n    thingbottomslope = FixedDiv (th->z - shootz, dist);\n\n    if (thingbottomslope > topslope)\n\treturn true;\t\t\t// shot under the thing\n    \n    // this thing can be hit!\n    if (thingtopslope > topslope)\n\tthingtopslope = topslope;\n    \n    if (thingbottomslope < bottomslope)\n\tthingbottomslope = bottomslope;\n\n    aimslope = (thingtopslope+thingbottomslope)/2;\n    linetarget = th;\n\n    return false;\t\t\t// don't go any farther\n}\n\n\n//\n// PTR_ShootTraverse\n//\nboolean PTR_ShootTraverse (intercept_t* in)\n{\n    fixed_t\t\tx;\n    fixed_t\t\ty;\n    fixed_t\t\tz;\n    fixed_t\t\tfrac;\n    \n    line_t*\t\tli;\n    \n    mobj_t*\t\tth;\n\n    fixed_t\t\tslope;\n    fixed_t\t\tdist;\n    fixed_t\t\tthingtopslope;\n    fixed_t\t\tthingbottomslope;\n\t\t\n    if (in->isaline)\n    {\n\tli = in->d.line;\n\t\n\tif (li->special)\n\t    P_ShootSpecialLine (shootthing, li);\n\n\tif ( !(li->flags & ML_TWOSIDED) )\n\t    goto hitline;\n\t\n\t// crosses a two sided line\n\tP_LineOpening (li);\n\t\t\n\tdist = FixedMul (attackrange, in->frac);\n\n        // e6y: emulation of missed back side on two-sided lines.\n        // backsector can be NULL when emulating missing back side.\n\n        if (li->backsector == NULL)\n        {\n            slope = FixedDiv (openbottom - shootz , dist);\n            if (slope > aimslope)\n                goto hitline;\n\n            slope = FixedDiv (opentop - shootz , dist);\n            if (slope < aimslope)\n                goto hitline;\n        }\n        else\n        {\n            if (li->frontsector->floorheight != li->backsector->floorheight)\n            {\n                slope = FixedDiv (openbottom - shootz , dist);\n                if (slope > aimslope)\n                    goto hitline;\n            }\n\n            if (li->frontsector->ceilingheight != li->backsector->ceilingheight)\n            {\n                slope = FixedDiv (opentop - shootz , dist);\n                if (slope < aimslope)\n                    goto hitline;\n            }\n        }\n\n\t// shot continues\n\treturn true;\n\t\n\t\n\t// hit line\n      hitline:\n\t// position a bit closer\n\tfrac = in->frac - FixedDiv (4*FRACUNIT,attackrange);\n\tx = trace.x + FixedMul (trace.dx, frac);\n\ty = trace.y + FixedMul (trace.dy, frac);\n\tz = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));\n\n\tif (li->frontsector->ceilingpic == skyflatnum)\n\t{\n\t    // don't shoot the sky!\n\t    if (z > li->frontsector->ceilingheight)\n\t\treturn false;\n\t    \n\t    // it's a sky hack wall\n\t    if\t(li->backsector && li->backsector->ceilingpic == skyflatnum)\n\t\treturn false;\t\t\n\t}\n\n\t// Spawn bullet puffs.\n\tP_SpawnPuff (x,y,z);\n\t\n\t// don't go any farther\n\treturn false;\t\n    }\n    \n    // shoot a thing\n    th = in->d.thing;\n    if (th == shootthing)\n\treturn true;\t\t// can't shoot self\n    \n    if (!(th->flags&MF_SHOOTABLE))\n\treturn true;\t\t// corpse or something\n\t\t\n    // check angles to see if the thing can be aimed at\n    dist = FixedMul (attackrange, in->frac);\n    thingtopslope = FixedDiv (th->z+th->height - shootz , dist);\n\n    if (thingtopslope < aimslope)\n\treturn true;\t\t// shot over the thing\n\n    thingbottomslope = FixedDiv (th->z - shootz, dist);\n\n    if (thingbottomslope > aimslope)\n\treturn true;\t\t// shot under the thing\n\n    \n    // hit thing\n    // position a bit closer\n    frac = in->frac - FixedDiv (10*FRACUNIT,attackrange);\n\n    x = trace.x + FixedMul (trace.dx, frac);\n    y = trace.y + FixedMul (trace.dy, frac);\n    z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));\n\n    // Spawn bullet puffs or blod spots,\n    // depending on target type.\n    if (in->d.thing->flags & MF_NOBLOOD)\n\tP_SpawnPuff (x,y,z);\n    else\n\tP_SpawnBlood (x,y,z, la_damage);\n\n    if (la_damage)\n\tP_DamageMobj (th, shootthing, shootthing, la_damage);\n\n    // don't go any farther\n    return false;\n\t\n}\n\n\n//\n// P_AimLineAttack\n//\nfixed_t\nP_AimLineAttack\n( mobj_t*\tt1,\n  angle_t\tangle,\n  fixed_t\tdistance )\n{\n    fixed_t\tx2;\n    fixed_t\ty2;\n\n    t1 = P_SubstNullMobj(t1);\n\t\n    angle >>= ANGLETOFINESHIFT;\n    shootthing = t1;\n    \n    x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];\n    y2 = t1->y + (distance>>FRACBITS)*finesine[angle];\n    shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;\n\n    // can't shoot outside view angles\n    topslope = 100*FRACUNIT/160;\t\n    bottomslope = -100*FRACUNIT/160;\n    \n    attackrange = distance;\n    linetarget = NULL;\n\t\n    P_PathTraverse ( t1->x, t1->y,\n\t\t     x2, y2,\n\t\t     PT_ADDLINES|PT_ADDTHINGS,\n\t\t     PTR_AimTraverse );\n\t\t\n    if (linetarget)\n\treturn aimslope;\n\n    return 0;\n}\n \n\n//\n// P_LineAttack\n// If damage == 0, it is just a test trace\n// that will leave linetarget set.\n//\nvoid\nP_LineAttack\n( mobj_t*\tt1,\n  angle_t\tangle,\n  fixed_t\tdistance,\n  fixed_t\tslope,\n  int\t\tdamage )\n{\n    fixed_t\tx2;\n    fixed_t\ty2;\n\t\n    angle >>= ANGLETOFINESHIFT;\n    shootthing = t1;\n    la_damage = damage;\n    x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];\n    y2 = t1->y + (distance>>FRACBITS)*finesine[angle];\n    shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;\n    attackrange = distance;\n    aimslope = slope;\n\t\t\n    P_PathTraverse ( t1->x, t1->y,\n\t\t     x2, y2,\n\t\t     PT_ADDLINES|PT_ADDTHINGS,\n\t\t     PTR_ShootTraverse );\n}\n \n\n\n//\n// USE LINES\n//\nmobj_t*\t\tusething;\n\nboolean\tPTR_UseTraverse (intercept_t* in)\n{\n    int\t\tside;\n\t\n    if (!in->d.line->special)\n    {\n\tP_LineOpening (in->d.line);\n\tif (openrange <= 0)\n\t{\n\t    S_StartSound (usething, sfx_noway);\n\t    \n\t    // can't use through a wall\n\t    return false;\t\n\t}\n\t// not a special line, but keep checking\n\treturn true ;\t\t\n    }\n\t\n    side = 0;\n    if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1)\n\tside = 1;\n    \n    //\treturn false;\t\t// don't use back side\n\t\n    P_UseSpecialLine (usething, in->d.line, side);\n\n    // can't use for than one special line in a row\n    return false;\n}\n\n\n//\n// P_UseLines\n// Looks for special lines in front of the player to activate.\n//\nvoid P_UseLines (player_t*\tplayer) \n{\n    int\t\tangle;\n    fixed_t\tx1;\n    fixed_t\ty1;\n    fixed_t\tx2;\n    fixed_t\ty2;\n\t\n    usething = player->mo;\n\t\t\n    angle = player->mo->angle >> ANGLETOFINESHIFT;\n\n    x1 = player->mo->x;\n    y1 = player->mo->y;\n    x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];\n    y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];\n\t\n    P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse );\n}\n\n\n//\n// RADIUS ATTACK\n//\nmobj_t*\t\tbombsource;\nmobj_t*\t\tbombspot;\nint\t\tbombdamage;\n\n\n//\n// PIT_RadiusAttack\n// \"bombsource\" is the creature\n// that caused the explosion at \"bombspot\".\n//\nboolean PIT_RadiusAttack (mobj_t* thing)\n{\n    fixed_t\tdx;\n    fixed_t\tdy;\n    fixed_t\tdist;\n\t\n    if (!(thing->flags & MF_SHOOTABLE) )\n\treturn true;\n\n    // Boss spider and cyborg\n    // take no damage from concussion.\n    if (thing->type == MT_CYBORG\n\t|| thing->type == MT_SPIDER)\n\treturn true;\t\n\t\t\n    dx = abs(thing->x - bombspot->x);\n    dy = abs(thing->y - bombspot->y);\n    \n    dist = dx>dy ? dx : dy;\n    dist = (dist - thing->radius) >> FRACBITS;\n\n    if (dist < 0)\n\tdist = 0;\n\n    if (dist >= bombdamage)\n\treturn true;\t// out of range\n\n    if ( P_CheckSight (thing, bombspot) )\n    {\n\t// must be in direct path\n\tP_DamageMobj (thing, bombspot, bombsource, bombdamage - dist);\n    }\n    \n    return true;\n}\n\n\n//\n// P_RadiusAttack\n// Source is the creature that caused the explosion at spot.\n//\nvoid\nP_RadiusAttack\n( mobj_t*\tspot,\n  mobj_t*\tsource,\n  int\t\tdamage )\n{\n    int\t\tx;\n    int\t\ty;\n    \n    int\t\txl;\n    int\t\txh;\n    int\t\tyl;\n    int\t\tyh;\n    \n    fixed_t\tdist;\n\t\n    dist = (damage+MAXRADIUS)<<FRACBITS;\n    yh = (spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;\n    yl = (spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;\n    xh = (spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;\n    xl = (spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT;\n    bombspot = spot;\n    bombsource = source;\n    bombdamage = damage;\n\t\n    for (y=yl ; y<=yh ; y++)\n\tfor (x=xl ; x<=xh ; x++)\n\t    P_BlockThingsIterator (x, y, PIT_RadiusAttack );\n}\n\n\n\n//\n// SECTOR HEIGHT CHANGING\n// After modifying a sectors floor or ceiling height,\n// call this routine to adjust the positions\n// of all things that touch the sector.\n//\n// If anything doesn't fit anymore, true will be returned.\n// If crunch is true, they will take damage\n//  as they are being crushed.\n// If Crunch is false, you should set the sector height back\n//  the way it was and call P_ChangeSector again\n//  to undo the changes.\n//\nboolean\t\tcrushchange;\nboolean\t\tnofit;\n\n\n//\n// PIT_ChangeSector\n//\nboolean PIT_ChangeSector (mobj_t*\tthing)\n{\n    mobj_t*\tmo;\n\t\n    if (P_ThingHeightClip (thing))\n    {\n\t// keep checking\n\treturn true;\n    }\n    \n\n    // crunch bodies to giblets\n    if (thing->health <= 0)\n    {\n\tP_SetMobjState (thing, S_GIBS);\n\n\tthing->flags &= ~MF_SOLID;\n\tthing->height = 0;\n\tthing->radius = 0;\n\n\t// keep checking\n\treturn true;\t\t\n    }\n\n    // crunch dropped items\n    if (thing->flags & MF_DROPPED)\n    {\n\tP_RemoveMobj (thing);\n\t\n\t// keep checking\n\treturn true;\t\t\n    }\n\n    if (! (thing->flags & MF_SHOOTABLE) )\n    {\n\t// assume it is bloody gibs or something\n\treturn true;\t\t\t\n    }\n    \n    nofit = true;\n\n    if (crushchange && !(leveltime&3) )\n    {\n\tP_DamageMobj(thing,NULL,NULL,10);\n\n\t// spray blood in a random direction\n\tmo = P_SpawnMobj (thing->x,\n\t\t\t  thing->y,\n\t\t\t  thing->z + thing->height/2, MT_BLOOD);\n\t\n\tmo->momx = (P_Random() - P_Random ())<<12;\n\tmo->momy = (P_Random() - P_Random ())<<12;\n    }\n\n    // keep checking (crush other things)\t\n    return true;\t\n}\n\n\n\n//\n// P_ChangeSector\n//\nboolean\nP_ChangeSector\n( sector_t*\tsector,\n  boolean\tcrunch )\n{\n    int\t\tx;\n    int\t\ty;\n\t\n    nofit = false;\n    crushchange = crunch;\n\t\n    // re-check heights for all things near the moving sector\n    for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++)\n\tfor (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++)\n\t    P_BlockThingsIterator (x, y, PIT_ChangeSector);\n\t\n\t\n    return nofit;\n}\n\n// Code to emulate the behavior of Vanilla Doom when encountering an overrun\n// of the spechit array.  This is by Andrey Budko (e6y) and comes from his\n// PrBoom plus port.  A big thanks to Andrey for this.\n\nstatic void SpechitOverrun(line_t *ld)\n{\n    static unsigned int baseaddr = 0;\n    unsigned int addr;\n   \n    if (baseaddr == 0)\n    {\n        int p;\n\n        // This is the first time we have had an overrun.  Work out\n        // what base address we are going to use.\n        // Allow a spechit value to be specified on the command line.\n\n        //!\n        // @category compat\n        // @arg <n>\n        //\n        // Use the specified magic value when emulating spechit overruns.\n        //\n\n        p = M_CheckParmWithArgs(\"-spechit\", 1);\n        \n        if (p > 0)\n        {\n            M_StrToInt(myargv[p+1], (int *) &baseaddr);\n        }\n        else\n        {\n            baseaddr = DEFAULT_SPECHIT_MAGIC;\n        }\n    }\n    \n    // Calculate address used in doom2.exe\n\n    addr = baseaddr + (ld - lines) * 0x3E;\n\n    switch(numspechit)\n    {\n        case 9: \n        case 10:\n        case 11:\n        case 12:\n            tmbbox[numspechit-9] = addr;\n            break;\n        case 13: \n            crushchange = addr; \n            break;\n        case 14: \n            nofit = addr; \n            break;\n        default:\n            fprintf(stderr, \"SpechitOverrun: Warning: unable to emulate\"\n                            \"an overrun where numspechit=%i\\n\",\n                            numspechit);\n            break;\n    }\n}\n\n"
  },
  {
    "path": "fbdoom/p_maputl.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n// Copyright(C) 2005, 2006 Andrey Budko\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tMovement/collision utility functions,\n//\tas used by function in p_map.c. \n//\tBLOCKMAP Iterator functions,\n//\tand some PIT_* functions to use for iteration.\n//\n\n\n\n#include <stdlib.h>\n\n\n#include \"m_bbox.h\"\n\n#include \"doomdef.h\"\n#include \"doomstat.h\"\n#include \"p_local.h\"\n\n\n// State.\n#include \"r_state.h\"\n\n//\n// P_AproxDistance\n// Gives an estimation of distance (not exact)\n//\n\nfixed_t\nP_AproxDistance\n( fixed_t\tdx,\n  fixed_t\tdy )\n{\n    dx = abs(dx);\n    dy = abs(dy);\n    if (dx < dy)\n\treturn dx+dy-(dx>>1);\n    return dx+dy-(dy>>1);\n}\n\n\n//\n// P_PointOnLineSide\n// Returns 0 or 1\n//\nint\nP_PointOnLineSide\n( fixed_t\tx,\n  fixed_t\ty,\n  line_t*\tline )\n{\n    fixed_t\tdx;\n    fixed_t\tdy;\n    fixed_t\tleft;\n    fixed_t\tright;\n\t\n    if (!line->dx)\n    {\n\tif (x <= line->v1->x)\n\t    return line->dy > 0;\n\t\n\treturn line->dy < 0;\n    }\n    if (!line->dy)\n    {\n\tif (y <= line->v1->y)\n\t    return line->dx < 0;\n\t\n\treturn line->dx > 0;\n    }\n\t\n    dx = (x - line->v1->x);\n    dy = (y - line->v1->y);\n\t\n    left = FixedMul ( line->dy>>FRACBITS , dx );\n    right = FixedMul ( dy , line->dx>>FRACBITS );\n\t\n    if (right < left)\n\treturn 0;\t\t// front side\n    return 1;\t\t\t// back side\n}\n\n\n\n//\n// P_BoxOnLineSide\n// Considers the line to be infinite\n// Returns side 0 or 1, -1 if box crosses the line.\n//\nint\nP_BoxOnLineSide\n( fixed_t*\ttmbox,\n  line_t*\tld )\n{\n    int\t\tp1 = 0;\n    int\t\tp2 = 0;\n\t\n    switch (ld->slopetype)\n    {\n      case ST_HORIZONTAL:\n\tp1 = tmbox[BOXTOP] > ld->v1->y;\n\tp2 = tmbox[BOXBOTTOM] > ld->v1->y;\n\tif (ld->dx < 0)\n\t{\n\t    p1 ^= 1;\n\t    p2 ^= 1;\n\t}\n\tbreak;\n\t\n      case ST_VERTICAL:\n\tp1 = tmbox[BOXRIGHT] < ld->v1->x;\n\tp2 = tmbox[BOXLEFT] < ld->v1->x;\n\tif (ld->dy < 0)\n\t{\n\t    p1 ^= 1;\n\t    p2 ^= 1;\n\t}\n\tbreak;\n\t\n      case ST_POSITIVE:\n\tp1 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXTOP], ld);\n\tp2 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld);\n\tbreak;\n\t\n      case ST_NEGATIVE:\n\tp1 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXTOP], ld);\n\tp2 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld);\n\tbreak;\n    }\n\n    if (p1 == p2)\n\treturn p1;\n    return -1;\n}\n\n\n//\n// P_PointOnDivlineSide\n// Returns 0 or 1.\n//\nint\nP_PointOnDivlineSide\n( fixed_t\tx,\n  fixed_t\ty,\n  divline_t*\tline )\n{\n    fixed_t\tdx;\n    fixed_t\tdy;\n    fixed_t\tleft;\n    fixed_t\tright;\n\t\n    if (!line->dx)\n    {\n\tif (x <= line->x)\n\t    return line->dy > 0;\n\t\n\treturn line->dy < 0;\n    }\n    if (!line->dy)\n    {\n\tif (y <= line->y)\n\t    return line->dx < 0;\n\n\treturn line->dx > 0;\n    }\n\t\n    dx = (x - line->x);\n    dy = (y - line->y);\n\t\n    // try to quickly decide by looking at sign bits\n    if ( (line->dy ^ line->dx ^ dx ^ dy)&0x80000000 )\n    {\n\tif ( (line->dy ^ dx) & 0x80000000 )\n\t    return 1;\t\t// (left is negative)\n\treturn 0;\n    }\n\t\n    left = FixedMul ( line->dy>>8, dx>>8 );\n    right = FixedMul ( dy>>8 , line->dx>>8 );\n\t\n    if (right < left)\n\treturn 0;\t\t// front side\n    return 1;\t\t\t// back side\n}\n\n\n\n//\n// P_MakeDivline\n//\nvoid\nP_MakeDivline\n( line_t*\tli,\n  divline_t*\tdl )\n{\n    dl->x = li->v1->x;\n    dl->y = li->v1->y;\n    dl->dx = li->dx;\n    dl->dy = li->dy;\n}\n\n\n\n//\n// P_InterceptVector\n// Returns the fractional intercept point\n// along the first divline.\n// This is only called by the addthings\n// and addlines traversers.\n//\nfixed_t\nP_InterceptVector\n( divline_t*\tv2,\n  divline_t*\tv1 )\n{\n#if 1\n    fixed_t\tfrac;\n    fixed_t\tnum;\n    fixed_t\tden;\n\t\n    den = FixedMul (v1->dy>>8,v2->dx) - FixedMul(v1->dx>>8,v2->dy);\n\n    if (den == 0)\n\treturn 0;\n    //\tI_Error (\"P_InterceptVector: parallel\");\n    \n    num =\n\tFixedMul ( (v1->x - v2->x)>>8 ,v1->dy )\n\t+FixedMul ( (v2->y - v1->y)>>8, v1->dx );\n\n    frac = FixedDiv (num , den);\n\n    return frac;\n#else\t// UNUSED, float debug.\n    float\tfrac;\n    float\tnum;\n    float\tden;\n    float\tv1x;\n    float\tv1y;\n    float\tv1dx;\n    float\tv1dy;\n    float\tv2x;\n    float\tv2y;\n    float\tv2dx;\n    float\tv2dy;\n\n    v1x = (float)v1->x/FRACUNIT;\n    v1y = (float)v1->y/FRACUNIT;\n    v1dx = (float)v1->dx/FRACUNIT;\n    v1dy = (float)v1->dy/FRACUNIT;\n    v2x = (float)v2->x/FRACUNIT;\n    v2y = (float)v2->y/FRACUNIT;\n    v2dx = (float)v2->dx/FRACUNIT;\n    v2dy = (float)v2->dy/FRACUNIT;\n\t\n    den = v1dy*v2dx - v1dx*v2dy;\n\n    if (den == 0)\n\treturn 0;\t// parallel\n    \n    num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx;\n    frac = num / den;\n\n    return frac*FRACUNIT;\n#endif\n}\n\n\n//\n// P_LineOpening\n// Sets opentop and openbottom to the window\n// through a two sided line.\n// OPTIMIZE: keep this precalculated\n//\nfixed_t opentop;\nfixed_t openbottom;\nfixed_t openrange;\nfixed_t\tlowfloor;\n\n\nvoid P_LineOpening (line_t* linedef)\n{\n    sector_t*\tfront;\n    sector_t*\tback;\n\t\n    if (linedef->sidenum[1] == -1)\n    {\n\t// single sided line\n\topenrange = 0;\n\treturn;\n    }\n\t \n    front = linedef->frontsector;\n    back = linedef->backsector;\n\t\n    if (front->ceilingheight < back->ceilingheight)\n\topentop = front->ceilingheight;\n    else\n\topentop = back->ceilingheight;\n\n    if (front->floorheight > back->floorheight)\n    {\n\topenbottom = front->floorheight;\n\tlowfloor = back->floorheight;\n    }\n    else\n    {\n\topenbottom = back->floorheight;\n\tlowfloor = front->floorheight;\n    }\n\t\n    openrange = opentop - openbottom;\n}\n\n\n//\n// THING POSITION SETTING\n//\n\n\n//\n// P_UnsetThingPosition\n// Unlinks a thing from block map and sectors.\n// On each position change, BLOCKMAP and other\n// lookups maintaining lists ot things inside\n// these structures need to be updated.\n//\nvoid P_UnsetThingPosition (mobj_t* thing)\n{\n    int\t\tblockx;\n    int\t\tblocky;\n\n    if ( ! (thing->flags & MF_NOSECTOR) )\n    {\n\t// inert things don't need to be in blockmap?\n\t// unlink from subsector\n\tif (thing->snext)\n\t    thing->snext->sprev = thing->sprev;\n\n\tif (thing->sprev)\n\t    thing->sprev->snext = thing->snext;\n\telse\n\t    thing->subsector->sector->thinglist = thing->snext;\n    }\n\t\n    if ( ! (thing->flags & MF_NOBLOCKMAP) )\n    {\n\t// inert things don't need to be in blockmap\n\t// unlink from block map\n\tif (thing->bnext)\n\t    thing->bnext->bprev = thing->bprev;\n\t\n\tif (thing->bprev)\n\t    thing->bprev->bnext = thing->bnext;\n\telse\n\t{\n\t    blockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT;\n\t    blocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT;\n\n\t    if (blockx>=0 && blockx < bmapwidth\n\t\t&& blocky>=0 && blocky <bmapheight)\n\t    {\n\t\tblocklinks[blocky*bmapwidth+blockx] = thing->bnext;\n\t    }\n\t}\n    }\n}\n\n\n//\n// P_SetThingPosition\n// Links a thing into both a block and a subsector\n// based on it's x y.\n// Sets thing->subsector properly\n//\nvoid\nP_SetThingPosition (mobj_t* thing)\n{\n    subsector_t*\tss;\n    sector_t*\t\tsec;\n    int\t\t\tblockx;\n    int\t\t\tblocky;\n    mobj_t**\t\tlink;\n\n    \n    // link into subsector\n    ss = R_PointInSubsector (thing->x,thing->y);\n    thing->subsector = ss;\n    \n    if ( ! (thing->flags & MF_NOSECTOR) )\n    {\n\t// invisible things don't go into the sector links\n\tsec = ss->sector;\n\t\n\tthing->sprev = NULL;\n\tthing->snext = sec->thinglist;\n\n\tif (sec->thinglist)\n\t    sec->thinglist->sprev = thing;\n\n\tsec->thinglist = thing;\n    }\n\n    \n    // link into blockmap\n    if ( ! (thing->flags & MF_NOBLOCKMAP) )\n    {\n\t// inert things don't need to be in blockmap\t\t\n\tblockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT;\n\tblocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT;\n\n\tif (blockx>=0\n\t    && blockx < bmapwidth\n\t    && blocky>=0\n\t    && blocky < bmapheight)\n\t{\n\t    link = &blocklinks[blocky*bmapwidth+blockx];\n\t    thing->bprev = NULL;\n\t    thing->bnext = *link;\n\t    if (*link)\n\t\t(*link)->bprev = thing;\n\n\t    *link = thing;\n\t}\n\telse\n\t{\n\t    // thing is off the map\n\t    thing->bnext = thing->bprev = NULL;\n\t}\n    }\n}\n\n\n\n//\n// BLOCK MAP ITERATORS\n// For each line/thing in the given mapblock,\n// call the passed PIT_* function.\n// If the function returns false,\n// exit with false without checking anything else.\n//\n\n\n//\n// P_BlockLinesIterator\n// The validcount flags are used to avoid checking lines\n// that are marked in multiple mapblocks,\n// so increment validcount before the first call\n// to P_BlockLinesIterator, then make one or more calls\n// to it.\n//\nboolean\nP_BlockLinesIterator\n( int\t\t\tx,\n  int\t\t\ty,\n  boolean(*func)(line_t*) )\n{\n    int\t\t\toffset;\n    short*\t\tlist;\n    line_t*\t\tld;\n\t\n    if (x<0\n\t|| y<0\n\t|| x>=bmapwidth\n\t|| y>=bmapheight)\n    {\n\treturn true;\n    }\n    \n    offset = y*bmapwidth+x;\n\t\n    offset = *(blockmap+offset);\n\n    for ( list = blockmaplump+offset ; *list != -1 ; list++)\n    {\n\tld = &lines[*list];\n\n\tif (ld->validcount == validcount)\n\t    continue; \t// line has already been checked\n\n\tld->validcount = validcount;\n\t\t\n\tif ( !func(ld) )\n\t    return false;\n    }\n    return true;\t// everything was checked\n}\n\n\n//\n// P_BlockThingsIterator\n//\nboolean\nP_BlockThingsIterator\n( int\t\t\tx,\n  int\t\t\ty,\n  boolean(*func)(mobj_t*) )\n{\n    mobj_t*\t\tmobj;\n\t\n    if ( x<0\n\t || y<0\n\t || x>=bmapwidth\n\t || y>=bmapheight)\n    {\n\treturn true;\n    }\n    \n\n    for (mobj = blocklinks[y*bmapwidth+x] ;\n\t mobj ;\n\t mobj = mobj->bnext)\n    {\n\tif (!func( mobj ) )\n\t    return false;\n    }\n    return true;\n}\n\n\n\n//\n// INTERCEPT ROUTINES\n//\nintercept_t\tintercepts[MAXINTERCEPTS];\nintercept_t*\tintercept_p;\n\ndivline_t \ttrace;\nboolean \tearlyout;\nint\t\tptflags;\n\nstatic void InterceptsOverrun(int num_intercepts, intercept_t *intercept);\n\n//\n// PIT_AddLineIntercepts.\n// Looks for lines in the given block\n// that intercept the given trace\n// to add to the intercepts list.\n//\n// A line is crossed if its endpoints\n// are on opposite sides of the trace.\n// Returns true if earlyout and a solid line hit.\n//\nboolean\nPIT_AddLineIntercepts (line_t* ld)\n{\n    int\t\t\ts1;\n    int\t\t\ts2;\n    fixed_t\t\tfrac;\n    divline_t\t\tdl;\n\t\n    // avoid precision problems with two routines\n    if ( trace.dx > FRACUNIT*16\n\t || trace.dy > FRACUNIT*16\n\t || trace.dx < -FRACUNIT*16\n\t || trace.dy < -FRACUNIT*16)\n    {\n\ts1 = P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &trace);\n\ts2 = P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &trace);\n    }\n    else\n    {\n\ts1 = P_PointOnLineSide (trace.x, trace.y, ld);\n\ts2 = P_PointOnLineSide (trace.x+trace.dx, trace.y+trace.dy, ld);\n    }\n    \n    if (s1 == s2)\n\treturn true;\t// line isn't crossed\n    \n    // hit the line\n    P_MakeDivline (ld, &dl);\n    frac = P_InterceptVector (&trace, &dl);\n\n    if (frac < 0)\n\treturn true;\t// behind source\n\t\n    // try to early out the check\n    if (earlyout\n\t&& frac < FRACUNIT\n\t&& !ld->backsector)\n    {\n\treturn false;\t// stop checking\n    }\n    \n\t\n    intercept_p->frac = frac;\n    intercept_p->isaline = true;\n    intercept_p->d.line = ld;\n    InterceptsOverrun(intercept_p - intercepts, intercept_p);\n    intercept_p++;\n\n    return true;\t// continue\n}\n\n\n\n//\n// PIT_AddThingIntercepts\n//\nboolean PIT_AddThingIntercepts (mobj_t* thing)\n{\n    fixed_t\t\tx1;\n    fixed_t\t\ty1;\n    fixed_t\t\tx2;\n    fixed_t\t\ty2;\n    \n    int\t\t\ts1;\n    int\t\t\ts2;\n    \n    boolean\t\ttracepositive;\n\n    divline_t\t\tdl;\n    \n    fixed_t\t\tfrac;\n\t\n    tracepositive = (trace.dx ^ trace.dy)>0;\n\t\t\n    // check a corner to corner crossection for hit\n    if (tracepositive)\n    {\n\tx1 = thing->x - thing->radius;\n\ty1 = thing->y + thing->radius;\n\t\t\n\tx2 = thing->x + thing->radius;\n\ty2 = thing->y - thing->radius;\t\t\t\n    }\n    else\n    {\n\tx1 = thing->x - thing->radius;\n\ty1 = thing->y - thing->radius;\n\t\t\n\tx2 = thing->x + thing->radius;\n\ty2 = thing->y + thing->radius;\t\t\t\n    }\n    \n    s1 = P_PointOnDivlineSide (x1, y1, &trace);\n    s2 = P_PointOnDivlineSide (x2, y2, &trace);\n\n    if (s1 == s2)\n\treturn true;\t\t// line isn't crossed\n\t\n    dl.x = x1;\n    dl.y = y1;\n    dl.dx = x2-x1;\n    dl.dy = y2-y1;\n    \n    frac = P_InterceptVector (&trace, &dl);\n\n    if (frac < 0)\n\treturn true;\t\t// behind source\n\n    intercept_p->frac = frac;\n    intercept_p->isaline = false;\n    intercept_p->d.thing = thing;\n    InterceptsOverrun(intercept_p - intercepts, intercept_p);\n    intercept_p++;\n\n    return true;\t\t// keep going\n}\n\n\n//\n// P_TraverseIntercepts\n// Returns true if the traverser function returns true\n// for all lines.\n// \nboolean\nP_TraverseIntercepts\n( traverser_t\tfunc,\n  fixed_t\tmaxfrac )\n{\n    int\t\t\tcount;\n    fixed_t\t\tdist;\n    intercept_t*\tscan;\n    intercept_t*\tin;\n\t\n    count = intercept_p - intercepts;\n    \n    in = 0;\t\t\t// shut up compiler warning\n\t\n    while (count--)\n    {\n\tdist = INT_MAX;\n\tfor (scan = intercepts ; scan<intercept_p ; scan++)\n\t{\n\t    if (scan->frac < dist)\n\t    {\n\t\tdist = scan->frac;\n\t\tin = scan;\n\t    }\n\t}\n\t\n\tif (dist > maxfrac)\n\t    return true;\t// checked everything in range\t\t\n\n#if 0  // UNUSED\n    {\n\t// don't check these yet, there may be others inserted\n\tin = scan = intercepts;\n\tfor ( scan = intercepts ; scan<intercept_p ; scan++)\n\t    if (scan->frac > maxfrac)\n\t\t*in++ = *scan;\n\tintercept_p = in;\n\treturn false;\n    }\n#endif\n\n        if ( !func (in) )\n\t    return false;\t// don't bother going farther\n\n\tin->frac = INT_MAX;\n    }\n\t\n    return true;\t\t// everything was traversed\n}\n\nextern fixed_t bulletslope;\n\n// Intercepts Overrun emulation, from PrBoom-plus.\n// Thanks to Andrey Budko (entryway) for researching this and his \n// implementation of Intercepts Overrun emulation in PrBoom-plus\n// which this is based on.\n\ntypedef struct\n{\n    int len;\n    void *addr;\n    boolean int16_array;\n} intercepts_overrun_t;\n\n// Intercepts memory table.  This is where various variables are located\n// in memory in Vanilla Doom.  When the intercepts table overflows, we\n// need to write to them.\n//\n// Almost all of the values to overwrite are 32-bit integers, except for\n// playerstarts, which is effectively an array of 16-bit integers and\n// must be treated differently.\n\nstatic intercepts_overrun_t intercepts_overrun[] =\n{\n    {4,   NULL,                          false},\n    {4,   NULL, /* &earlyout, */         false},\n    {4,   NULL, /* &intercept_p, */      false},\n    {4,   &lowfloor,                     false},\n    {4,   &openbottom,                   false},\n    {4,   &opentop,                      false},\n    {4,   &openrange,                    false},\n    {4,   NULL,                          false},\n    {120, NULL, /* &activeplats, */      false},\n    {8,   NULL,                          false},\n    {4,   &bulletslope,                  false},\n    {4,   NULL, /* &swingx, */           false},\n    {4,   NULL, /* &swingy, */           false},\n    {4,   NULL,                          false},\n    {40,  &playerstarts,                 true},\n    {4,   NULL, /* &blocklinks, */       false},\n    {4,   &bmapwidth,                    false},\n    {4,   NULL, /* &blockmap, */         false},\n    {4,   &bmaporgx,                     false},\n    {4,   &bmaporgy,                     false},\n    {4,   NULL, /* &blockmaplump, */     false},\n    {4,   &bmapheight,                   false},\n    {0,   NULL,                          false},\n};\n\n// Overwrite a specific memory location with a value.\n\nstatic void InterceptsMemoryOverrun(int location, int value)\n{\n    int i, offset;\n    int index;\n    void *addr;\n\n    i = 0;\n    offset = 0;\n\n    // Search down the array until we find the right entry\n\n    while (intercepts_overrun[i].len != 0)\n    {\n        if (offset + intercepts_overrun[i].len > location)\n        {\n            addr = intercepts_overrun[i].addr;\n\n            // Write the value to the memory location.\n            // 16-bit and 32-bit values are written differently.\n\n            if (addr != NULL)\n            {\n                if (intercepts_overrun[i].int16_array)\n                {\n                    index = (location - offset) / 2;\n                    ((short *) addr)[index] = value & 0xffff;\n                    ((short *) addr)[index + 1] = (value >> 16) & 0xffff;\n                }\n                else\n                {\n                    index = (location - offset) / 4;\n                    ((int *) addr)[index] = value;\n                }\n            }\n\n            break;\n        }\n\n        offset += intercepts_overrun[i].len;\n        ++i;\n    }\n}\n\n// Emulate overruns of the intercepts[] array.\n\nstatic void InterceptsOverrun(int num_intercepts, intercept_t *intercept)\n{\n    int location;\n\n    if (num_intercepts <= MAXINTERCEPTS_ORIGINAL)\n    {\n        // No overrun\n\n        return;\n    }\n\n    location = (num_intercepts - MAXINTERCEPTS_ORIGINAL - 1) * 12;\n\n    // Overwrite memory that is overwritten in Vanilla Doom, using\n    // the values from the intercept structure.\n    //\n    // Note: the ->d.{thing,line} member should really have its\n    // address translated into the correct address value for \n    // Vanilla Doom.\n\n    InterceptsMemoryOverrun(location, intercept->frac);\n    InterceptsMemoryOverrun(location + 4, intercept->isaline);\n    InterceptsMemoryOverrun(location + 8, (int) intercept->d.thing);\n}\n\n\n//\n// P_PathTraverse\n// Traces a line from x1,y1 to x2,y2,\n// calling the traverser function for each.\n// Returns true if the traverser function returns true\n// for all lines.\n//\nboolean\nP_PathTraverse\n( fixed_t\t\tx1,\n  fixed_t\t\ty1,\n  fixed_t\t\tx2,\n  fixed_t\t\ty2,\n  int\t\t\tflags,\n  boolean (*trav) (intercept_t *))\n{\n    fixed_t\txt1;\n    fixed_t\tyt1;\n    fixed_t\txt2;\n    fixed_t\tyt2;\n    \n    fixed_t\txstep;\n    fixed_t\tystep;\n    \n    fixed_t\tpartial;\n    \n    fixed_t\txintercept;\n    fixed_t\tyintercept;\n    \n    int\t\tmapx;\n    int\t\tmapy;\n    \n    int\t\tmapxstep;\n    int\t\tmapystep;\n\n    int\t\tcount;\n\t\t\n    earlyout = flags & PT_EARLYOUT;\n\t\t\n    validcount++;\n    intercept_p = intercepts;\n\t\n    if ( ((x1-bmaporgx)&(MAPBLOCKSIZE-1)) == 0)\n\tx1 += FRACUNIT;\t// don't side exactly on a line\n    \n    if ( ((y1-bmaporgy)&(MAPBLOCKSIZE-1)) == 0)\n\ty1 += FRACUNIT;\t// don't side exactly on a line\n\n    trace.x = x1;\n    trace.y = y1;\n    trace.dx = x2 - x1;\n    trace.dy = y2 - y1;\n\n    x1 -= bmaporgx;\n    y1 -= bmaporgy;\n    xt1 = x1>>MAPBLOCKSHIFT;\n    yt1 = y1>>MAPBLOCKSHIFT;\n\n    x2 -= bmaporgx;\n    y2 -= bmaporgy;\n    xt2 = x2>>MAPBLOCKSHIFT;\n    yt2 = y2>>MAPBLOCKSHIFT;\n\n    if (xt2 > xt1)\n    {\n\tmapxstep = 1;\n\tpartial = FRACUNIT - ((x1>>MAPBTOFRAC)&(FRACUNIT-1));\n\tystep = FixedDiv (y2-y1,abs(x2-x1));\n    }\n    else if (xt2 < xt1)\n    {\n\tmapxstep = -1;\n\tpartial = (x1>>MAPBTOFRAC)&(FRACUNIT-1);\n\tystep = FixedDiv (y2-y1,abs(x2-x1));\n    }\n    else\n    {\n\tmapxstep = 0;\n\tpartial = FRACUNIT;\n\tystep = 256*FRACUNIT;\n    }\t\n\n    yintercept = (y1>>MAPBTOFRAC) + FixedMul (partial, ystep);\n\n\t\n    if (yt2 > yt1)\n    {\n\tmapystep = 1;\n\tpartial = FRACUNIT - ((y1>>MAPBTOFRAC)&(FRACUNIT-1));\n\txstep = FixedDiv (x2-x1,abs(y2-y1));\n    }\n    else if (yt2 < yt1)\n    {\n\tmapystep = -1;\n\tpartial = (y1>>MAPBTOFRAC)&(FRACUNIT-1);\n\txstep = FixedDiv (x2-x1,abs(y2-y1));\n    }\n    else\n    {\n\tmapystep = 0;\n\tpartial = FRACUNIT;\n\txstep = 256*FRACUNIT;\n    }\t\n    xintercept = (x1>>MAPBTOFRAC) + FixedMul (partial, xstep);\n    \n    // Step through map blocks.\n    // Count is present to prevent a round off error\n    // from skipping the break.\n    mapx = xt1;\n    mapy = yt1;\n\t\n    for (count = 0 ; count < 64 ; count++)\n    {\n\tif (flags & PT_ADDLINES)\n\t{\n\t    if (!P_BlockLinesIterator (mapx, mapy,PIT_AddLineIntercepts))\n\t\treturn false;\t// early out\n\t}\n\t\n\tif (flags & PT_ADDTHINGS)\n\t{\n\t    if (!P_BlockThingsIterator (mapx, mapy,PIT_AddThingIntercepts))\n\t\treturn false;\t// early out\n\t}\n\t\t\n\tif (mapx == xt2\n\t    && mapy == yt2)\n\t{\n\t    break;\n\t}\n\t\n\tif ( (yintercept >> FRACBITS) == mapy)\n\t{\n\t    yintercept += ystep;\n\t    mapx += mapxstep;\n\t}\n\telse if ( (xintercept >> FRACBITS) == mapx)\n\t{\n\t    xintercept += xstep;\n\t    mapy += mapystep;\n\t}\n\t\t\n    }\n    // go through the sorted list\n    return P_TraverseIntercepts ( trav, FRACUNIT );\n}\n\n\n\n"
  },
  {
    "path": "fbdoom/p_mobj.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tMoving object handling. Spawn functions.\n//\n\n#include <stdio.h>\n\n#include \"i_system.h\"\n#include \"z_zone.h\"\n#include \"m_random.h\"\n\n#include \"doomdef.h\"\n#include \"p_local.h\"\n#include \"sounds.h\"\n\n#include \"st_stuff.h\"\n#include \"hu_stuff.h\"\n\n#include \"s_sound.h\"\n\n#include \"doomstat.h\"\n\n\nvoid G_PlayerReborn (int player);\nvoid P_SpawnMapThing (mapthing_t*\tmthing);\n\n\n//\n// P_SetMobjState\n// Returns true if the mobj is still present.\n//\nint test;\n\nboolean\nP_SetMobjState\n( mobj_t*\tmobj,\n  statenum_t\tstate )\n{\n    state_t*\tst;\n\n    do\n    {\n\tif (state == S_NULL)\n\t{\n\t    mobj->state = (state_t *) S_NULL;\n\t    P_RemoveMobj (mobj);\n\t    return false;\n\t}\n\n\tst = &states[state];\n\tmobj->state = st;\n\tmobj->tics = st->tics;\n\tmobj->sprite = st->sprite;\n\tmobj->frame = st->frame;\n\n\t// Modified handling.\n\t// Call action functions when the state is set\n\tif (st->action.acp1)\t\t\n\t    st->action.acp1(mobj);\t\n\t\n\tstate = st->nextstate;\n    } while (!mobj->tics);\n\t\t\t\t\n    return true;\n}\n\n\n//\n// P_ExplodeMissile  \n//\nvoid P_ExplodeMissile (mobj_t* mo)\n{\n    mo->momx = mo->momy = mo->momz = 0;\n\n    P_SetMobjState (mo, mobjinfo[mo->type].deathstate);\n\n    mo->tics -= P_Random()&3;\n\n    if (mo->tics < 1)\n\tmo->tics = 1;\n\n    mo->flags &= ~MF_MISSILE;\n\n    if (mo->info->deathsound)\n\tS_StartSound (mo, mo->info->deathsound);\n}\n\n\n//\n// P_XYMovement  \n//\n#define STOPSPEED\t\t0x1000\n#define FRICTION\t\t0xe800\n\nvoid P_XYMovement (mobj_t* mo) \n{ \t\n    fixed_t \tptryx;\n    fixed_t\tptryy;\n    player_t*\tplayer;\n    fixed_t\txmove;\n    fixed_t\tymove;\n\t\t\t\n    if (!mo->momx && !mo->momy)\n    {\n\tif (mo->flags & MF_SKULLFLY)\n\t{\n\t    // the skull slammed into something\n\t    mo->flags &= ~MF_SKULLFLY;\n\t    mo->momx = mo->momy = mo->momz = 0;\n\n\t    P_SetMobjState (mo, mo->info->spawnstate);\n\t}\n\treturn;\n    }\n\t\n    player = mo->player;\n\t\t\n    if (mo->momx > MAXMOVE)\n\tmo->momx = MAXMOVE;\n    else if (mo->momx < -MAXMOVE)\n\tmo->momx = -MAXMOVE;\n\n    if (mo->momy > MAXMOVE)\n\tmo->momy = MAXMOVE;\n    else if (mo->momy < -MAXMOVE)\n\tmo->momy = -MAXMOVE;\n\t\t\n    xmove = mo->momx;\n    ymove = mo->momy;\n\t\n    do\n    {\n\tif (xmove > MAXMOVE/2 || ymove > MAXMOVE/2)\n\t{\n\t    ptryx = mo->x + xmove/2;\n\t    ptryy = mo->y + ymove/2;\n\t    xmove >>= 1;\n\t    ymove >>= 1;\n\t}\n\telse\n\t{\n\t    ptryx = mo->x + xmove;\n\t    ptryy = mo->y + ymove;\n\t    xmove = ymove = 0;\n\t}\n\t\t\n\tif (!P_TryMove (mo, ptryx, ptryy))\n\t{\n\t    // blocked move\n\t    if (mo->player)\n\t    {\t// try to slide along it\n\t\tP_SlideMove (mo);\n\t    }\n\t    else if (mo->flags & MF_MISSILE)\n\t    {\n\t\t// explode a missile\n\t\tif (ceilingline &&\n\t\t    ceilingline->backsector &&\n\t\t    ceilingline->backsector->ceilingpic == skyflatnum)\n\t\t{\n\t\t    // Hack to prevent missiles exploding\n\t\t    // against the sky.\n\t\t    // Does not handle sky floors.\n\t\t    P_RemoveMobj (mo);\n\t\t    return;\n\t\t}\n\t\tP_ExplodeMissile (mo);\n\t    }\n\t    else\n\t\tmo->momx = mo->momy = 0;\n\t}\n    } while (xmove || ymove);\n    \n    // slow down\n    if (player && player->cheats & CF_NOMOMENTUM)\n    {\n\t// debug option for no sliding at all\n\tmo->momx = mo->momy = 0;\n\treturn;\n    }\n\n    if (mo->flags & (MF_MISSILE | MF_SKULLFLY) )\n\treturn; \t// no friction for missiles ever\n\t\t\n    if (mo->z > mo->floorz)\n\treturn;\t\t// no friction when airborne\n\n    if (mo->flags & MF_CORPSE)\n    {\n\t// do not stop sliding\n\t//  if halfway off a step with some momentum\n\tif (mo->momx > FRACUNIT/4\n\t    || mo->momx < -FRACUNIT/4\n\t    || mo->momy > FRACUNIT/4\n\t    || mo->momy < -FRACUNIT/4)\n\t{\n\t    if (mo->floorz != mo->subsector->sector->floorheight)\n\t\treturn;\n\t}\n    }\n\n    if (mo->momx > -STOPSPEED\n\t&& mo->momx < STOPSPEED\n\t&& mo->momy > -STOPSPEED\n\t&& mo->momy < STOPSPEED\n\t&& (!player\n\t    || (player->cmd.forwardmove== 0\n\t\t&& player->cmd.sidemove == 0 ) ) )\n    {\n\t// if in a walking frame, stop moving\n\tif ( player&&(unsigned)((player->mo->state - states)- S_PLAY_RUN1) < 4)\n\t    P_SetMobjState (player->mo, S_PLAY);\n\t\n\tmo->momx = 0;\n\tmo->momy = 0;\n    }\n    else\n    {\n\tmo->momx = FixedMul (mo->momx, FRICTION);\n\tmo->momy = FixedMul (mo->momy, FRICTION);\n    }\n}\n\n//\n// P_ZMovement\n//\nvoid P_ZMovement (mobj_t* mo)\n{\n    fixed_t\tdist;\n    fixed_t\tdelta;\n    \n    // check for smooth step up\n    if (mo->player && mo->z < mo->floorz)\n    {\n\tmo->player->viewheight -= mo->floorz-mo->z;\n\n\tmo->player->deltaviewheight\n\t    = (VIEWHEIGHT - mo->player->viewheight)>>3;\n    }\n    \n    // adjust height\n    mo->z += mo->momz;\n\t\n    if ( mo->flags & MF_FLOAT\n\t && mo->target)\n    {\n\t// float down towards target if too close\n\tif ( !(mo->flags & MF_SKULLFLY)\n\t     && !(mo->flags & MF_INFLOAT) )\n\t{\n\t    dist = P_AproxDistance (mo->x - mo->target->x,\n\t\t\t\t    mo->y - mo->target->y);\n\t    \n\t    delta =(mo->target->z + (mo->height>>1)) - mo->z;\n\n\t    if (delta<0 && dist < -(delta*3) )\n\t\tmo->z -= FLOATSPEED;\n\t    else if (delta>0 && dist < (delta*3) )\n\t\tmo->z += FLOATSPEED;\t\t\t\n\t}\n\t\n    }\n    \n    // clip movement\n    if (mo->z <= mo->floorz)\n    {\n\t// hit the floor\n\n\t// Note (id):\n\t//  somebody left this after the setting momz to 0,\n\t//  kinda useless there.\n\t//\n\t// cph - This was the a bug in the linuxdoom-1.10 source which\n\t//  caused it not to sync Doom 2 v1.9 demos. Someone\n\t//  added the above comment and moved up the following code. So\n\t//  demos would desync in close lost soul fights.\n\t// Note that this only applies to original Doom 1 or Doom2 demos - not\n\t//  Final Doom and Ultimate Doom.  So we test demo_compatibility *and*\n\t//  gamemission. (Note we assume that Doom1 is always Ult Doom, which\n\t//  seems to hold for most published demos.)\n        //  \n        //  fraggle - cph got the logic here slightly wrong.  There are three\n        //  versions of Doom 1.9:\n        //\n        //  * The version used in registered doom 1.9 + doom2 - no bounce\n        //  * The version used in ultimate doom - has bounce\n        //  * The version used in final doom - has bounce\n        //\n        // So we need to check that this is either retail or commercial\n        // (but not doom2)\n\t\n\tint correct_lost_soul_bounce = gameversion >= exe_ultimate;\n\n\tif (correct_lost_soul_bounce && mo->flags & MF_SKULLFLY)\n\t{\n\t    // the skull slammed into something\n\t    mo->momz = -mo->momz;\n\t}\n\t\n\tif (mo->momz < 0)\n\t{\n\t    if (mo->player\n\t\t&& mo->momz < -GRAVITY*8)\t\n\t    {\n\t\t// Squat down.\n\t\t// Decrease viewheight for a moment\n\t\t// after hitting the ground (hard),\n\t\t// and utter appropriate sound.\n\t\tmo->player->deltaviewheight = mo->momz>>3;\n\t\tS_StartSound (mo, sfx_oof);\n\t    }\n\t    mo->momz = 0;\n\t}\n\tmo->z = mo->floorz;\n\n\n\t// cph 2001/05/26 -\n\t// See lost soul bouncing comment above. We need this here for bug\n\t// compatibility with original Doom2 v1.9 - if a soul is charging and\n\t// hit by a raising floor this incorrectly reverses its Y momentum.\n\t//\n\n        if (!correct_lost_soul_bounce && mo->flags & MF_SKULLFLY)\n            mo->momz = -mo->momz;\n\n\tif ( (mo->flags & MF_MISSILE)\n\t     && !(mo->flags & MF_NOCLIP) )\n\t{\n\t    P_ExplodeMissile (mo);\n\t    return;\n\t}\n    }\n    else if (! (mo->flags & MF_NOGRAVITY) )\n    {\n\tif (mo->momz == 0)\n\t    mo->momz = -GRAVITY*2;\n\telse\n\t    mo->momz -= GRAVITY;\n    }\n\t\n    if (mo->z + mo->height > mo->ceilingz)\n    {\n\t// hit the ceiling\n\tif (mo->momz > 0)\n\t    mo->momz = 0;\n\t{\n\t    mo->z = mo->ceilingz - mo->height;\n\t}\n\n\tif (mo->flags & MF_SKULLFLY)\n\t{\t// the skull slammed into something\n\t    mo->momz = -mo->momz;\n\t}\n\t\n\tif ( (mo->flags & MF_MISSILE)\n\t     && !(mo->flags & MF_NOCLIP) )\n\t{\n\t    P_ExplodeMissile (mo);\n\t    return;\n\t}\n    }\n} \n\n\n\n//\n// P_NightmareRespawn\n//\nvoid\nP_NightmareRespawn (mobj_t* mobj)\n{\n    fixed_t\t\tx;\n    fixed_t\t\ty;\n    fixed_t\t\tz; \n    subsector_t*\tss; \n    mobj_t*\t\tmo;\n    mapthing_t*\t\tmthing;\n\t\t\n    x = mobj->spawnpoint.x << FRACBITS; \n    y = mobj->spawnpoint.y << FRACBITS; \n\n    // somthing is occupying it's position?\n    if (!P_CheckPosition (mobj, x, y) ) \n\treturn;\t// no respwan\n\n    // spawn a teleport fog at old spot\n    // because of removal of the body?\n    mo = P_SpawnMobj (mobj->x,\n\t\t      mobj->y,\n\t\t      mobj->subsector->sector->floorheight , MT_TFOG); \n    // initiate teleport sound\n    S_StartSound (mo, sfx_telept);\n\n    // spawn a teleport fog at the new spot\n    ss = R_PointInSubsector (x,y); \n\n    mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_TFOG); \n\n    S_StartSound (mo, sfx_telept);\n\n    // spawn the new monster\n    mthing = &mobj->spawnpoint;\n\t\n    // spawn it\n    if (mobj->info->flags & MF_SPAWNCEILING)\n\tz = ONCEILINGZ;\n    else\n\tz = ONFLOORZ;\n\n    // inherit attributes from deceased one\n    mo = P_SpawnMobj (x,y,z, mobj->type);\n    mo->spawnpoint = mobj->spawnpoint;\t\n    mo->angle = ANG45 * (mthing->angle/45);\n\n    if (mthing->options & MTF_AMBUSH)\n\tmo->flags |= MF_AMBUSH;\n\n    mo->reactiontime = 18;\n\t\n    // remove the old monster,\n    P_RemoveMobj (mobj);\n}\n\n\n//\n// P_MobjThinker\n//\nvoid P_MobjThinker (mobj_t* mobj)\n{\n    // momentum movement\n    if (mobj->momx\n\t|| mobj->momy\n\t|| (mobj->flags&MF_SKULLFLY) )\n    {\n\tP_XYMovement (mobj);\n\n\t// FIXME: decent NOP/NULL/Nil function pointer please.\n\tif (mobj->thinker.function.acv == (actionf_v) (-1))\n\t    return;\t\t// mobj was removed\n    }\n    if ( (mobj->z != mobj->floorz)\n\t || mobj->momz )\n    {\n\tP_ZMovement (mobj);\n\t\n\t// FIXME: decent NOP/NULL/Nil function pointer please.\n\tif (mobj->thinker.function.acv == (actionf_v) (-1))\n\t    return;\t\t// mobj was removed\n    }\n\n    \n    // cycle through states,\n    // calling action functions at transitions\n    if (mobj->tics != -1)\n    {\n\tmobj->tics--;\n\t\t\n\t// you can cycle through multiple states in a tic\n\tif (!mobj->tics)\n\t    if (!P_SetMobjState (mobj, mobj->state->nextstate) )\n\t\treturn;\t\t// freed itself\n    }\n    else\n    {\n\t// check for nightmare respawn\n\tif (! (mobj->flags & MF_COUNTKILL) )\n\t    return;\n\n\tif (!respawnmonsters)\n\t    return;\n\n\tmobj->movecount++;\n\n\tif (mobj->movecount < 12*TICRATE)\n\t    return;\n\n\tif ( leveltime&31 )\n\t    return;\n\n\tif (P_Random () > 4)\n\t    return;\n\n\tP_NightmareRespawn (mobj);\n    }\n\n}\n\n\n//\n// P_SpawnMobj\n//\nmobj_t*\nP_SpawnMobj\n( fixed_t\tx,\n  fixed_t\ty,\n  fixed_t\tz,\n  mobjtype_t\ttype )\n{\n    mobj_t*\tmobj;\n    state_t*\tst;\n    mobjinfo_t*\tinfo;\n\t\n    mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL);\n    memset (mobj, 0, sizeof (*mobj));\n    info = &mobjinfo[type];\n\t\n    mobj->type = type;\n    mobj->info = info;\n    mobj->x = x;\n    mobj->y = y;\n    mobj->radius = info->radius;\n    mobj->height = info->height;\n    mobj->flags = info->flags;\n    mobj->health = info->spawnhealth;\n\n    if (gameskill != sk_nightmare)\n\tmobj->reactiontime = info->reactiontime;\n    \n    mobj->lastlook = P_Random () % MAXPLAYERS;\n    // do not set the state with P_SetMobjState,\n    // because action routines can not be called yet\n    st = &states[info->spawnstate];\n\n    mobj->state = st;\n    mobj->tics = st->tics;\n    mobj->sprite = st->sprite;\n    mobj->frame = st->frame;\n\n    // set subsector and/or block links\n    P_SetThingPosition (mobj);\n\t\n    mobj->floorz = mobj->subsector->sector->floorheight;\n    mobj->ceilingz = mobj->subsector->sector->ceilingheight;\n\n    if (z == ONFLOORZ)\n\tmobj->z = mobj->floorz;\n    else if (z == ONCEILINGZ)\n\tmobj->z = mobj->ceilingz - mobj->info->height;\n    else \n\tmobj->z = z;\n\n    mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker;\n\t\n    P_AddThinker (&mobj->thinker);\n\n    return mobj;\n}\n\n\n//\n// P_RemoveMobj\n//\nmapthing_t\titemrespawnque[ITEMQUESIZE];\nint\t\titemrespawntime[ITEMQUESIZE];\nint\t\tiquehead;\nint\t\tiquetail;\n\n\nvoid P_RemoveMobj (mobj_t* mobj)\n{\n    if ((mobj->flags & MF_SPECIAL)\n\t&& !(mobj->flags & MF_DROPPED)\n\t&& (mobj->type != MT_INV)\n\t&& (mobj->type != MT_INS))\n    {\n\titemrespawnque[iquehead] = mobj->spawnpoint;\n\titemrespawntime[iquehead] = leveltime;\n\tiquehead = (iquehead+1)&(ITEMQUESIZE-1);\n\n\t// lose one off the end?\n\tif (iquehead == iquetail)\n\t    iquetail = (iquetail+1)&(ITEMQUESIZE-1);\n    }\n\t\n    // unlink from sector and block lists\n    P_UnsetThingPosition (mobj);\n    \n    // stop any playing sound\n    S_StopSound (mobj);\n    \n    // free block\n    P_RemoveThinker ((thinker_t*)mobj);\n}\n\n\n\n\n//\n// P_RespawnSpecials\n//\nvoid P_RespawnSpecials (void)\n{\n    fixed_t\t\tx;\n    fixed_t\t\ty;\n    fixed_t\t\tz;\n    \n    subsector_t*\tss; \n    mobj_t*\t\tmo;\n    mapthing_t*\t\tmthing;\n    \n    int\t\t\ti;\n\n    // only respawn items in deathmatch\n    if (deathmatch != 2)\n\treturn;\t// \n\n    // nothing left to respawn?\n    if (iquehead == iquetail)\n\treturn;\t\t\n\n    // wait at least 30 seconds\n    if (leveltime - itemrespawntime[iquetail] < 30*TICRATE)\n\treturn;\t\t\t\n\n    mthing = &itemrespawnque[iquetail];\n\t\n    x = mthing->x << FRACBITS; \n    y = mthing->y << FRACBITS; \n\t  \n    // spawn a teleport fog at the new spot\n    ss = R_PointInSubsector (x,y); \n    mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_IFOG); \n    S_StartSound (mo, sfx_itmbk);\n\n    // find which type to spawn\n    for (i=0 ; i< NUMMOBJTYPES ; i++)\n    {\n\tif (mthing->type == mobjinfo[i].doomednum)\n\t    break;\n    }\n    \n    // spawn it\n    if (mobjinfo[i].flags & MF_SPAWNCEILING)\n\tz = ONCEILINGZ;\n    else\n\tz = ONFLOORZ;\n\n    mo = P_SpawnMobj (x,y,z, i);\n    mo->spawnpoint = *mthing;\t\n    mo->angle = ANG45 * (mthing->angle/45);\n\n    // pull it from the que\n    iquetail = (iquetail+1)&(ITEMQUESIZE-1);\n}\n\n\n\n\n//\n// P_SpawnPlayer\n// Called when a player is spawned on the level.\n// Most of the player structure stays unchanged\n//  between levels.\n//\nvoid P_SpawnPlayer (mapthing_t* mthing)\n{\n    player_t*\t\tp;\n    fixed_t\t\tx;\n    fixed_t\t\ty;\n    fixed_t\t\tz;\n\n    mobj_t*\t\tmobj;\n\n    int\t\t\ti;\n\n    if (mthing->type == 0)\n    {\n        return;\n    }\n\n    // not playing?\n    if (!playeringame[mthing->type-1])\n\treturn;\t\t\t\t\t\n\t\t\n    p = &players[mthing->type-1];\n\n    if (p->playerstate == PST_REBORN)\n\tG_PlayerReborn (mthing->type-1);\n\n    x \t\t= mthing->x << FRACBITS;\n    y \t\t= mthing->y << FRACBITS;\n    z\t\t= ONFLOORZ;\n    mobj\t= P_SpawnMobj (x,y,z, MT_PLAYER);\n\n    // set color translations for player sprites\n    if (mthing->type > 1)\t\t\n\tmobj->flags |= (mthing->type-1)<<MF_TRANSSHIFT;\n\t\t\n    mobj->angle\t= ANG45 * (mthing->angle/45);\n    mobj->player = p;\n    mobj->health = p->health;\n\n    p->mo = mobj;\n    p->playerstate = PST_LIVE;\t\n    p->refire = 0;\n    p->message = NULL;\n    p->damagecount = 0;\n    p->bonuscount = 0;\n    p->extralight = 0;\n    p->fixedcolormap = 0;\n    p->viewheight = VIEWHEIGHT;\n\n    // setup gun psprite\n    P_SetupPsprites (p);\n    \n    // give all cards in death match mode\n    if (deathmatch)\n\tfor (i=0 ; i<NUMCARDS ; i++)\n\t    p->cards[i] = true;\n\t\t\t\n    if (mthing->type-1 == consoleplayer)\n    {\n\t// wake up the status bar\n\tST_Start ();\n\t// wake up the heads up text\n\tHU_Start ();\t\t\n    }\n}\n\n\n//\n// P_SpawnMapThing\n// The fields of the mapthing should\n// already be in host byte order.\n//\nvoid P_SpawnMapThing (mapthing_t* mthing)\n{\n    int\t\t\ti;\n    int\t\t\tbit;\n    mobj_t*\t\tmobj;\n    fixed_t\t\tx;\n    fixed_t\t\ty;\n    fixed_t\t\tz;\n\t\t\n    // count deathmatch start positions\n    if (mthing->type == 11)\n    {\n\tif (deathmatch_p < &deathmatchstarts[10])\n\t{\n\t    memcpy (deathmatch_p, mthing, sizeof(*mthing));\n\t    deathmatch_p++;\n\t}\n\treturn;\n    }\n\n    if (mthing->type <= 0)\n    {\n        // Thing type 0 is actually \"player -1 start\".  \n        // For some reason, Vanilla Doom accepts/ignores this.\n\n        return;\n    }\n\t\n    // check for players specially\n    if (mthing->type <= 4)\n    {\n\t// save spots for respawning in network games\n\tplayerstarts[mthing->type-1] = *mthing;\n\tif (!deathmatch)\n\t    P_SpawnPlayer (mthing);\n\n\treturn;\n    }\n\n    // check for apropriate skill level\n    if (!netgame && (mthing->options & 16) )\n\treturn;\n\t\t\n    if (gameskill == sk_baby)\n\tbit = 1;\n    else if (gameskill == sk_nightmare)\n\tbit = 4;\n    else\n\tbit = 1<<(gameskill-1);\n\n    if (!(mthing->options & bit) )\n\treturn;\n\t\n    // find which type to spawn\n    for (i=0 ; i< NUMMOBJTYPES ; i++)\n\tif (mthing->type == mobjinfo[i].doomednum)\n\t    break;\n\t\n    if (i==NUMMOBJTYPES)\n\tI_Error (\"P_SpawnMapThing: Unknown type %i at (%i, %i)\",\n\t\t mthing->type,\n\t\t mthing->x, mthing->y);\n\t\t\n    // don't spawn keycards and players in deathmatch\n    if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH)\n\treturn;\n\t\t\n    // don't spawn any monsters if -nomonsters\n    if (nomonsters\n\t&& ( i == MT_SKULL\n\t     || (mobjinfo[i].flags & MF_COUNTKILL)) )\n    {\n\treturn;\n    }\n    \n    // spawn it\n    x = mthing->x << FRACBITS;\n    y = mthing->y << FRACBITS;\n\n    if (mobjinfo[i].flags & MF_SPAWNCEILING)\n\tz = ONCEILINGZ;\n    else\n\tz = ONFLOORZ;\n    \n    mobj = P_SpawnMobj (x,y,z, i);\n    mobj->spawnpoint = *mthing;\n\n    if (mobj->tics > 0)\n\tmobj->tics = 1 + (P_Random () % mobj->tics);\n    if (mobj->flags & MF_COUNTKILL)\n\ttotalkills++;\n    if (mobj->flags & MF_COUNTITEM)\n\ttotalitems++;\n\t\t\n    mobj->angle = ANG45 * (mthing->angle/45);\n    if (mthing->options & MTF_AMBUSH)\n\tmobj->flags |= MF_AMBUSH;\n}\n\n\n\n//\n// GAME SPAWN FUNCTIONS\n//\n\n\n//\n// P_SpawnPuff\n//\nextern fixed_t attackrange;\n\nvoid\nP_SpawnPuff\n( fixed_t\tx,\n  fixed_t\ty,\n  fixed_t\tz )\n{\n    mobj_t*\tth;\n\t\n    z += ((P_Random()-P_Random())<<10);\n\n    th = P_SpawnMobj (x,y,z, MT_PUFF);\n    th->momz = FRACUNIT;\n    th->tics -= P_Random()&3;\n\n    if (th->tics < 1)\n\tth->tics = 1;\n\t\n    // don't make punches spark on the wall\n    if (attackrange == MELEERANGE)\n\tP_SetMobjState (th, S_PUFF3);\n}\n\n\n\n//\n// P_SpawnBlood\n// \nvoid\nP_SpawnBlood\n( fixed_t\tx,\n  fixed_t\ty,\n  fixed_t\tz,\n  int\t\tdamage )\n{\n    mobj_t*\tth;\n\t\n    z += ((P_Random()-P_Random())<<10);\n    th = P_SpawnMobj (x,y,z, MT_BLOOD);\n    th->momz = FRACUNIT*2;\n    th->tics -= P_Random()&3;\n\n    if (th->tics < 1)\n\tth->tics = 1;\n\t\t\n    if (damage <= 12 && damage >= 9)\n\tP_SetMobjState (th,S_BLOOD2);\n    else if (damage < 9)\n\tP_SetMobjState (th,S_BLOOD3);\n}\n\n\n\n//\n// P_CheckMissileSpawn\n// Moves the missile forward a bit\n//  and possibly explodes it right there.\n//\nvoid P_CheckMissileSpawn (mobj_t* th)\n{\n    th->tics -= P_Random()&3;\n    if (th->tics < 1)\n\tth->tics = 1;\n    \n    // move a little forward so an angle can\n    // be computed if it immediately explodes\n    th->x += (th->momx>>1);\n    th->y += (th->momy>>1);\n    th->z += (th->momz>>1);\n\n    if (!P_TryMove (th, th->x, th->y))\n\tP_ExplodeMissile (th);\n}\n\n// Certain functions assume that a mobj_t pointer is non-NULL,\n// causing a crash in some situations where it is NULL.  Vanilla\n// Doom did not crash because of the lack of proper memory \n// protection. This function substitutes NULL pointers for\n// pointers to a dummy mobj, to avoid a crash.\n\nmobj_t *P_SubstNullMobj(mobj_t *mobj)\n{\n    if (mobj == NULL)\n    {\n        static mobj_t dummy_mobj;\n\n        dummy_mobj.x = 0;\n        dummy_mobj.y = 0;\n        dummy_mobj.z = 0;\n        dummy_mobj.flags = 0;\n\n        mobj = &dummy_mobj;\n    }\n\n    return mobj;\n}\n\n//\n// P_SpawnMissile\n//\nmobj_t*\nP_SpawnMissile\n( mobj_t*\tsource,\n  mobj_t*\tdest,\n  mobjtype_t\ttype )\n{\n    mobj_t*\tth;\n    angle_t\tan;\n    int\t\tdist;\n\n    th = P_SpawnMobj (source->x,\n\t\t      source->y,\n\t\t      source->z + 4*8*FRACUNIT, type);\n    \n    if (th->info->seesound)\n\tS_StartSound (th, th->info->seesound);\n\n    th->target = source;\t// where it came from\n    an = R_PointToAngle2 (source->x, source->y, dest->x, dest->y);\n\n    // fuzzy player\n    if (dest->flags & MF_SHADOW)\n\tan += (P_Random()-P_Random())<<20;\t\n\n    th->angle = an;\n    an >>= ANGLETOFINESHIFT;\n    th->momx = FixedMul (th->info->speed, finecosine[an]);\n    th->momy = FixedMul (th->info->speed, finesine[an]);\n\t\n    dist = P_AproxDistance (dest->x - source->x, dest->y - source->y);\n    dist = dist / th->info->speed;\n\n    if (dist < 1)\n\tdist = 1;\n\n    th->momz = (dest->z - source->z) / dist;\n    P_CheckMissileSpawn (th);\n\t\n    return th;\n}\n\n\n//\n// P_SpawnPlayerMissile\n// Tries to aim at a nearby monster\n//\nvoid\nP_SpawnPlayerMissile\n( mobj_t*\tsource,\n  mobjtype_t\ttype )\n{\n    mobj_t*\tth;\n    angle_t\tan;\n    \n    fixed_t\tx;\n    fixed_t\ty;\n    fixed_t\tz;\n    fixed_t\tslope;\n    \n    // see which target is to be aimed at\n    an = source->angle;\n    slope = P_AimLineAttack (source, an, 16*64*FRACUNIT);\n    \n    if (!linetarget)\n    {\n\tan += 1<<26;\n\tslope = P_AimLineAttack (source, an, 16*64*FRACUNIT);\n\n\tif (!linetarget)\n\t{\n\t    an -= 2<<26;\n\t    slope = P_AimLineAttack (source, an, 16*64*FRACUNIT);\n\t}\n\n\tif (!linetarget)\n\t{\n\t    an = source->angle;\n\t    slope = 0;\n\t}\n    }\n\t\t\n    x = source->x;\n    y = source->y;\n    z = source->z + 4*8*FRACUNIT;\n\t\n    th = P_SpawnMobj (x,y,z, type);\n\n    if (th->info->seesound)\n\tS_StartSound (th, th->info->seesound);\n\n    th->target = source;\n    th->angle = an;\n    th->momx = FixedMul( th->info->speed,\n\t\t\t finecosine[an>>ANGLETOFINESHIFT]);\n    th->momy = FixedMul( th->info->speed,\n\t\t\t finesine[an>>ANGLETOFINESHIFT]);\n    th->momz = FixedMul( th->info->speed, slope);\n\n    P_CheckMissileSpawn (th);\n}\n\n"
  },
  {
    "path": "fbdoom/p_mobj.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tMap Objects, MObj, definition and handling.\n//\n\n\n#ifndef __P_MOBJ__\n#define __P_MOBJ__\n\n// Basics.\n#include \"tables.h\"\n#include \"m_fixed.h\"\n\n// We need the thinker_t stuff.\n#include \"d_think.h\"\n\n// We need the WAD data structure for Map things,\n// from the THINGS lump.\n#include \"doomdata.h\"\n\n// States are tied to finite states are\n//  tied to animation frames.\n// Needs precompiled tables/data structures.\n#include \"info.h\"\n\n\n\n\n\n\n//\n// NOTES: mobj_t\n//\n// mobj_ts are used to tell the refresh where to draw an image,\n// tell the world simulation when objects are contacted,\n// and tell the sound driver how to position a sound.\n//\n// The refresh uses the next and prev links to follow\n// lists of things in sectors as they are being drawn.\n// The sprite, frame, and angle elements determine which patch_t\n// is used to draw the sprite if it is visible.\n// The sprite and frame values are allmost allways set\n// from state_t structures.\n// The statescr.exe utility generates the states.h and states.c\n// files that contain the sprite/frame numbers from the\n// statescr.txt source file.\n// The xyz origin point represents a point at the bottom middle\n// of the sprite (between the feet of a biped).\n// This is the default origin position for patch_ts grabbed\n// with lumpy.exe.\n// A walking creature will have its z equal to the floor\n// it is standing on.\n//\n// The sound code uses the x,y, and subsector fields\n// to do stereo positioning of any sound effited by the mobj_t.\n//\n// The play simulation uses the blocklinks, x,y,z, radius, height\n// to determine when mobj_ts are touching each other,\n// touching lines in the map, or hit by trace lines (gunshots,\n// lines of sight, etc).\n// The mobj_t->flags element has various bit flags\n// used by the simulation.\n//\n// Every mobj_t is linked into a single sector\n// based on its origin coordinates.\n// The subsector_t is found with R_PointInSubsector(x,y),\n// and the sector_t can be found with subsector->sector.\n// The sector links are only used by the rendering code,\n// the play simulation does not care about them at all.\n//\n// Any mobj_t that needs to be acted upon by something else\n// in the play world (block movement, be shot, etc) will also\n// need to be linked into the blockmap.\n// If the thing has the MF_NOBLOCK flag set, it will not use\n// the block links. It can still interact with other things,\n// but only as the instigator (missiles will run into other\n// things, but nothing can run into a missile).\n// Each block in the grid is 128*128 units, and knows about\n// every line_t that it contains a piece of, and every\n// interactable mobj_t that has its origin contained.  \n//\n// A valid mobj_t is a mobj_t that has the proper subsector_t\n// filled in for its xy coordinates and is linked into the\n// sector from which the subsector was made, or has the\n// MF_NOSECTOR flag set (the subsector_t needs to be valid\n// even if MF_NOSECTOR is set), and is linked into a blockmap\n// block or has the MF_NOBLOCKMAP flag set.\n// Links should only be modified by the P_[Un]SetThingPosition()\n// functions.\n// Do not change the MF_NO? flags while a thing is valid.\n//\n// Any questions?\n//\n\n//\n// Misc. mobj flags\n//\ntypedef enum\n{\n    // Call P_SpecialThing when touched.\n    MF_SPECIAL\t\t= 1,\n    // Blocks.\n    MF_SOLID\t\t= 2,\n    // Can be hit.\n    MF_SHOOTABLE\t= 4,\n    // Don't use the sector links (invisible but touchable).\n    MF_NOSECTOR\t\t= 8,\n    // Don't use the blocklinks (inert but displayable)\n    MF_NOBLOCKMAP\t= 16,                    \n\n    // Not to be activated by sound, deaf monster.\n    MF_AMBUSH\t\t= 32,\n    // Will try to attack right back.\n    MF_JUSTHIT\t\t= 64,\n    // Will take at least one step before attacking.\n    MF_JUSTATTACKED\t= 128,\n    // On level spawning (initial position),\n    //  hang from ceiling instead of stand on floor.\n    MF_SPAWNCEILING\t= 256,\n    // Don't apply gravity (every tic),\n    //  that is, object will float, keeping current height\n    //  or changing it actively.\n    MF_NOGRAVITY\t= 512,\n\n    // Movement flags.\n    // This allows jumps from high places.\n    MF_DROPOFF\t\t= 0x400,\n    // For players, will pick up items.\n    MF_PICKUP\t\t= 0x800,\n    // Player cheat. ???\n    MF_NOCLIP\t\t= 0x1000,\n    // Player: keep info about sliding along walls.\n    MF_SLIDE\t\t= 0x2000,\n    // Allow moves to any height, no gravity.\n    // For active floaters, e.g. cacodemons, pain elementals.\n    MF_FLOAT\t\t= 0x4000,\n    // Don't cross lines\n    //   ??? or look at heights on teleport.\n    MF_TELEPORT\t\t= 0x8000,\n    // Don't hit same species, explode on block.\n    // Player missiles as well as fireballs of various kinds.\n    MF_MISSILE\t\t= 0x10000,\t\n    // Dropped by a demon, not level spawned.\n    // E.g. ammo clips dropped by dying former humans.\n    MF_DROPPED\t\t= 0x20000,\n    // Use fuzzy draw (shadow demons or spectres),\n    //  temporary player invisibility powerup.\n    MF_SHADOW\t\t= 0x40000,\n    // Flag: don't bleed when shot (use puff),\n    //  barrels and shootable furniture shall not bleed.\n    MF_NOBLOOD\t\t= 0x80000,\n    // Don't stop moving halfway off a step,\n    //  that is, have dead bodies slide down all the way.\n    MF_CORPSE\t\t= 0x100000,\n    // Floating to a height for a move, ???\n    //  don't auto float to target's height.\n    MF_INFLOAT\t\t= 0x200000,\n\n    // On kill, count this enemy object\n    //  towards intermission kill total.\n    // Happy gathering.\n    MF_COUNTKILL\t= 0x400000,\n    \n    // On picking up, count this item object\n    //  towards intermission item total.\n    MF_COUNTITEM\t= 0x800000,\n\n    // Special handling: skull in flight.\n    // Neither a cacodemon nor a missile.\n    MF_SKULLFLY\t\t= 0x1000000,\n\n    // Don't spawn this object\n    //  in death match mode (e.g. key cards).\n    MF_NOTDMATCH    \t= 0x2000000,\n\n    // Player sprites in multiplayer modes are modified\n    //  using an internal color lookup table for re-indexing.\n    // If 0x4 0x8 or 0xc,\n    //  use a translation table for player colormaps\n    MF_TRANSLATION  \t= 0xc000000,\n    // Hmm ???.\n    MF_TRANSSHIFT\t= 26\n\n} mobjflag_t;\n\n\n// Map Object definition.\ntypedef struct mobj_s\n{\n    // List: thinker links.\n    thinker_t\t\tthinker;\n\n    // Info for drawing: position.\n    fixed_t\t\tx;\n    fixed_t\t\ty;\n    fixed_t\t\tz;\n\n    // More list: links in sector (if needed)\n    struct mobj_s*\tsnext;\n    struct mobj_s*\tsprev;\n\n    //More drawing info: to determine current sprite.\n    angle_t\t\tangle;\t// orientation\n    spritenum_t\t\tsprite;\t// used to find patch_t and flip value\n    int\t\t\tframe;\t// might be ORed with FF_FULLBRIGHT\n\n    // Interaction info, by BLOCKMAP.\n    // Links in blocks (if needed).\n    struct mobj_s*\tbnext;\n    struct mobj_s*\tbprev;\n    \n    struct subsector_s*\tsubsector;\n\n    // The closest interval over all contacted Sectors.\n    fixed_t\t\tfloorz;\n    fixed_t\t\tceilingz;\n\n    // For movement checking.\n    fixed_t\t\tradius;\n    fixed_t\t\theight;\t\n\n    // Momentums, used to update position.\n    fixed_t\t\tmomx;\n    fixed_t\t\tmomy;\n    fixed_t\t\tmomz;\n\n    // If == validcount, already checked.\n    int\t\t\tvalidcount;\n\n    mobjtype_t\t\ttype;\n    mobjinfo_t*\t\tinfo;\t// &mobjinfo[mobj->type]\n    \n    int\t\t\ttics;\t// state tic counter\n    state_t*\t\tstate;\n    int\t\t\tflags;\n    int\t\t\thealth;\n\n    // Movement direction, movement generation (zig-zagging).\n    int\t\t\tmovedir;\t// 0-7\n    int\t\t\tmovecount;\t// when 0, select a new dir\n\n    // Thing being chased/attacked (or NULL),\n    // also the originator for missiles.\n    struct mobj_s*\ttarget;\n\n    // Reaction time: if non 0, don't attack yet.\n    // Used by player to freeze a bit after teleporting.\n    int\t\t\treactiontime;   \n\n    // If >0, the target will be chased\n    // no matter what (even if shot)\n    int\t\t\tthreshold;\n\n    // Additional info record for player avatars only.\n    // Only valid if type == MT_PLAYER\n    struct player_s*\tplayer;\n\n    // Player number last looked for.\n    int\t\t\tlastlook;\t\n\n    // For nightmare respawn.\n    mapthing_t\t\tspawnpoint;\t\n\n    // Thing being chased/attacked for tracers.\n    struct mobj_s*\ttracer;\t\n    \n} mobj_t;\n\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/p_plats.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tPlats (i.e. elevator platforms) code, raising/lowering.\n//\n\n#include <stdio.h>\n\n#include \"i_system.h\"\n#include \"z_zone.h\"\n#include \"m_random.h\"\n\n#include \"doomdef.h\"\n#include \"p_local.h\"\n\n#include \"s_sound.h\"\n\n// State.\n#include \"doomstat.h\"\n#include \"r_state.h\"\n\n// Data.\n#include \"sounds.h\"\n\n\nplat_t*\t\tactiveplats[MAXPLATS];\n\n\n\n//\n// Move a plat up and down\n//\nvoid T_PlatRaise(plat_t* plat)\n{\n    result_e\tres;\n\t\n    switch(plat->status)\n    {\n      case up:\n\tres = T_MovePlane(plat->sector,\n\t\t\t  plat->speed,\n\t\t\t  plat->high,\n\t\t\t  plat->crush,0,1);\n\t\t\t\t\t\n\tif (plat->type == raiseAndChange\n\t    || plat->type == raiseToNearestAndChange)\n\t{\n\t    if (!(leveltime&7))\n\t\tS_StartSound(&plat->sector->soundorg, sfx_stnmov);\n\t}\n\t\n\t\t\t\t\n\tif (res == crushed && (!plat->crush))\n\t{\n\t    plat->count = plat->wait;\n\t    plat->status = down;\n\t    S_StartSound(&plat->sector->soundorg, sfx_pstart);\n\t}\n\telse\n\t{\n\t    if (res == pastdest)\n\t    {\n\t\tplat->count = plat->wait;\n\t\tplat->status = waiting;\n\t\tS_StartSound(&plat->sector->soundorg, sfx_pstop);\n\n\t\tswitch(plat->type)\n\t\t{\n\t\t  case blazeDWUS:\n\t\t  case downWaitUpStay:\n\t\t    P_RemoveActivePlat(plat);\n\t\t    break;\n\t\t    \n\t\t  case raiseAndChange:\n\t\t  case raiseToNearestAndChange:\n\t\t    P_RemoveActivePlat(plat);\n\t\t    break;\n\t\t    \n\t\t  default:\n\t\t    break;\n\t\t}\n\t    }\n\t}\n\tbreak;\n\t\n      case\tdown:\n\tres = T_MovePlane(plat->sector,plat->speed,plat->low,false,0,-1);\n\n\tif (res == pastdest)\n\t{\n\t    plat->count = plat->wait;\n\t    plat->status = waiting;\n\t    S_StartSound(&plat->sector->soundorg,sfx_pstop);\n\t}\n\tbreak;\n\t\n      case\twaiting:\n\tif (!--plat->count)\n\t{\n\t    if (plat->sector->floorheight == plat->low)\n\t\tplat->status = up;\n\t    else\n\t\tplat->status = down;\n\t    S_StartSound(&plat->sector->soundorg,sfx_pstart);\n\t}\n      case\tin_stasis:\n\tbreak;\n    }\n}\n\n\n//\n// Do Platforms\n//  \"amount\" is only used for SOME platforms.\n//\nint\nEV_DoPlat\n( line_t*\tline,\n  plattype_e\ttype,\n  int\t\tamount )\n{\n    plat_t*\tplat;\n    int\t\tsecnum;\n    int\t\trtn;\n    sector_t*\tsec;\n\t\n    secnum = -1;\n    rtn = 0;\n\n    \n    //\tActivate all <type> plats that are in_stasis\n    switch(type)\n    {\n      case perpetualRaise:\n\tP_ActivateInStasis(line->tag);\n\tbreak;\n\t\n      default:\n\tbreak;\n    }\n\t\n    while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)\n    {\n\tsec = &sectors[secnum];\n\n\tif (sec->specialdata)\n\t    continue;\n\t\n\t// Find lowest & highest floors around sector\n\trtn = 1;\n\tplat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0);\n\tP_AddThinker(&plat->thinker);\n\t\t\n\tplat->type = type;\n\tplat->sector = sec;\n\tplat->sector->specialdata = plat;\n\tplat->thinker.function.acp1 = (actionf_p1) T_PlatRaise;\n\tplat->crush = false;\n\tplat->tag = line->tag;\n\t\n\tswitch(type)\n\t{\n\t  case raiseToNearestAndChange:\n\t    plat->speed = PLATSPEED/2;\n\t    sec->floorpic = sides[line->sidenum[0]].sector->floorpic;\n\t    plat->high = P_FindNextHighestFloor(sec,sec->floorheight);\n\t    plat->wait = 0;\n\t    plat->status = up;\n\t    // NO MORE DAMAGE, IF APPLICABLE\n\t    sec->special = 0;\t\t\n\n\t    S_StartSound(&sec->soundorg,sfx_stnmov);\n\t    break;\n\t    \n\t  case raiseAndChange:\n\t    plat->speed = PLATSPEED/2;\n\t    sec->floorpic = sides[line->sidenum[0]].sector->floorpic;\n\t    plat->high = sec->floorheight + amount*FRACUNIT;\n\t    plat->wait = 0;\n\t    plat->status = up;\n\n\t    S_StartSound(&sec->soundorg,sfx_stnmov);\n\t    break;\n\t    \n\t  case downWaitUpStay:\n\t    plat->speed = PLATSPEED * 4;\n\t    plat->low = P_FindLowestFloorSurrounding(sec);\n\n\t    if (plat->low > sec->floorheight)\n\t\tplat->low = sec->floorheight;\n\n\t    plat->high = sec->floorheight;\n\t    plat->wait = TICRATE*PLATWAIT;\n\t    plat->status = down;\n\t    S_StartSound(&sec->soundorg,sfx_pstart);\n\t    break;\n\t    \n\t  case blazeDWUS:\n\t    plat->speed = PLATSPEED * 8;\n\t    plat->low = P_FindLowestFloorSurrounding(sec);\n\n\t    if (plat->low > sec->floorheight)\n\t\tplat->low = sec->floorheight;\n\n\t    plat->high = sec->floorheight;\n\t    plat->wait = TICRATE*PLATWAIT;\n\t    plat->status = down;\n\t    S_StartSound(&sec->soundorg,sfx_pstart);\n\t    break;\n\t    \n\t  case perpetualRaise:\n\t    plat->speed = PLATSPEED;\n\t    plat->low = P_FindLowestFloorSurrounding(sec);\n\n\t    if (plat->low > sec->floorheight)\n\t\tplat->low = sec->floorheight;\n\n\t    plat->high = P_FindHighestFloorSurrounding(sec);\n\n\t    if (plat->high < sec->floorheight)\n\t\tplat->high = sec->floorheight;\n\n\t    plat->wait = TICRATE*PLATWAIT;\n\t    plat->status = P_Random()&1;\n\n\t    S_StartSound(&sec->soundorg,sfx_pstart);\n\t    break;\n\t}\n\tP_AddActivePlat(plat);\n    }\n    return rtn;\n}\n\n\n\nvoid P_ActivateInStasis(int tag)\n{\n    int\t\ti;\n\t\n    for (i = 0;i < MAXPLATS;i++)\n\tif (activeplats[i]\n\t    && (activeplats[i])->tag == tag\n\t    && (activeplats[i])->status == in_stasis)\n\t{\n\t    (activeplats[i])->status = (activeplats[i])->oldstatus;\n\t    (activeplats[i])->thinker.function.acp1\n\t      = (actionf_p1) T_PlatRaise;\n\t}\n}\n\nvoid EV_StopPlat(line_t* line)\n{\n    int\t\tj;\n\t\n    for (j = 0;j < MAXPLATS;j++)\n\tif (activeplats[j]\n\t    && ((activeplats[j])->status != in_stasis)\n\t    && ((activeplats[j])->tag == line->tag))\n\t{\n\t    (activeplats[j])->oldstatus = (activeplats[j])->status;\n\t    (activeplats[j])->status = in_stasis;\n\t    (activeplats[j])->thinker.function.acv = (actionf_v)NULL;\n\t}\n}\n\nvoid P_AddActivePlat(plat_t* plat)\n{\n    int\t\ti;\n    \n    for (i = 0;i < MAXPLATS;i++)\n\tif (activeplats[i] == NULL)\n\t{\n\t    activeplats[i] = plat;\n\t    return;\n\t}\n    I_Error (\"P_AddActivePlat: no more plats!\");\n}\n\nvoid P_RemoveActivePlat(plat_t* plat)\n{\n    int\t\ti;\n    for (i = 0;i < MAXPLATS;i++)\n\tif (plat == activeplats[i])\n\t{\n\t    (activeplats[i])->sector->specialdata = NULL;\n\t    P_RemoveThinker(&(activeplats[i])->thinker);\n\t    activeplats[i] = NULL;\n\t    \n\t    return;\n\t}\n    I_Error (\"P_RemoveActivePlat: can't find plat!\");\n}\n"
  },
  {
    "path": "fbdoom/p_pspr.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tWeapon sprite animation, weapon objects.\n//\tAction functions for weapons.\n//\n\n\n#include \"doomdef.h\"\n#include \"d_event.h\"\n\n#include \"deh_misc.h\"\n\n#include \"m_random.h\"\n#include \"p_local.h\"\n#include \"s_sound.h\"\n\n// State.\n#include \"doomstat.h\"\n\n// Data.\n#include \"sounds.h\"\n\n#include \"p_pspr.h\"\n\n#define LOWERSPEED\t\tFRACUNIT*6\n#define RAISESPEED\t\tFRACUNIT*6\n\n#define WEAPONBOTTOM\t128*FRACUNIT\n#define WEAPONTOP\t\t32*FRACUNIT\n\n\n\n//\n// P_SetPsprite\n//\nvoid\nP_SetPsprite\n( player_t*\tplayer,\n  int\t\tposition,\n  statenum_t\tstnum ) \n{\n    pspdef_t*\tpsp;\n    state_t*\tstate;\n\t\n    psp = &player->psprites[position];\n\t\n    do\n    {\n\tif (!stnum)\n\t{\n\t    // object removed itself\n\t    psp->state = NULL;\n\t    break;\t\n\t}\n\t\n\tstate = &states[stnum];\n\tpsp->state = state;\n\tpsp->tics = state->tics;\t// could be 0\n\n\tif (state->misc1)\n\t{\n\t    // coordinate set\n\t    psp->sx = state->misc1 << FRACBITS;\n\t    psp->sy = state->misc2 << FRACBITS;\n\t}\n\t\n\t// Call action routine.\n\t// Modified handling.\n\tif (state->action.acp2)\n\t{\n\t    state->action.acp2(player, psp);\n\t    if (!psp->state)\n\t\tbreak;\n\t}\n\t\n\tstnum = psp->state->nextstate;\n\t\n    } while (!psp->tics);\n    // an initial state of 0 could cycle through\n}\n\n\n\n//\n// P_CalcSwing\n//\t\nfixed_t\t\tswingx;\nfixed_t\t\tswingy;\n\nvoid P_CalcSwing (player_t*\tplayer)\n{\n    fixed_t\tswing;\n    int\t\tangle;\n\t\n    // OPTIMIZE: tablify this.\n    // A LUT would allow for different modes,\n    //  and add flexibility.\n\n    swing = player->bob;\n\n    angle = (FINEANGLES/70*leveltime)&FINEMASK;\n    swingx = FixedMul ( swing, finesine[angle]);\n\n    angle = (FINEANGLES/70*leveltime+FINEANGLES/2)&FINEMASK;\n    swingy = -FixedMul ( swingx, finesine[angle]);\n}\n\n\n\n//\n// P_BringUpWeapon\n// Starts bringing the pending weapon up\n// from the bottom of the screen.\n// Uses player\n//\nvoid P_BringUpWeapon (player_t* player)\n{\n    statenum_t\tnewstate;\n\t\n    if (player->pendingweapon == wp_nochange)\n\tplayer->pendingweapon = player->readyweapon;\n\t\t\n    if (player->pendingweapon == wp_chainsaw)\n\tS_StartSound (player->mo, sfx_sawup);\n\t\t\n    newstate = weaponinfo[player->pendingweapon].upstate;\n\n    player->pendingweapon = wp_nochange;\n    player->psprites[ps_weapon].sy = WEAPONBOTTOM;\n\n    P_SetPsprite (player, ps_weapon, newstate);\n}\n\n//\n// P_CheckAmmo\n// Returns true if there is enough ammo to shoot.\n// If not, selects the next weapon to use.\n//\nboolean P_CheckAmmo (player_t* player)\n{\n    ammotype_t\t\tammo;\n    int\t\t\tcount;\n\n    ammo = weaponinfo[player->readyweapon].ammo;\n\n    // Minimal amount for one shot varies.\n    if (player->readyweapon == wp_bfg)\n\tcount = deh_bfg_cells_per_shot;\n    else if (player->readyweapon == wp_supershotgun)\n\tcount = 2;\t// Double barrel.\n    else\n\tcount = 1;\t// Regular.\n\n    // Some do not need ammunition anyway.\n    // Return if current ammunition sufficient.\n    if (ammo == am_noammo || player->ammo[ammo] >= count)\n\treturn true;\n\t\t\n    // Out of ammo, pick a weapon to change to.\n    // Preferences are set here.\n    do\n    {\n\tif (player->weaponowned[wp_plasma]\n\t    && player->ammo[am_cell]\n\t    && (gamemode != shareware) )\n\t{\n\t    player->pendingweapon = wp_plasma;\n\t}\n\telse if (player->weaponowned[wp_supershotgun] \n\t\t && player->ammo[am_shell]>2\n\t\t && (gamemode == commercial) )\n\t{\n\t    player->pendingweapon = wp_supershotgun;\n\t}\n\telse if (player->weaponowned[wp_chaingun]\n\t\t && player->ammo[am_clip])\n\t{\n\t    player->pendingweapon = wp_chaingun;\n\t}\n\telse if (player->weaponowned[wp_shotgun]\n\t\t && player->ammo[am_shell])\n\t{\n\t    player->pendingweapon = wp_shotgun;\n\t}\n\telse if (player->ammo[am_clip])\n\t{\n\t    player->pendingweapon = wp_pistol;\n\t}\n\telse if (player->weaponowned[wp_chainsaw])\n\t{\n\t    player->pendingweapon = wp_chainsaw;\n\t}\n\telse if (player->weaponowned[wp_missile]\n\t\t && player->ammo[am_misl])\n\t{\n\t    player->pendingweapon = wp_missile;\n\t}\n\telse if (player->weaponowned[wp_bfg]\n\t\t && player->ammo[am_cell]>40\n\t\t && (gamemode != shareware) )\n\t{\n\t    player->pendingweapon = wp_bfg;\n\t}\n\telse\n\t{\n\t    // If everything fails.\n\t    player->pendingweapon = wp_fist;\n\t}\n\t\n    } while (player->pendingweapon == wp_nochange);\n\n    // Now set appropriate weapon overlay.\n    P_SetPsprite (player,\n\t\t  ps_weapon,\n\t\t  weaponinfo[player->readyweapon].downstate);\n\n    return false;\t\n}\n\n\n//\n// P_FireWeapon.\n//\nvoid P_FireWeapon (player_t* player)\n{\n    statenum_t\tnewstate;\n\t\n    if (!P_CheckAmmo (player))\n\treturn;\n\t\n    P_SetMobjState (player->mo, S_PLAY_ATK1);\n    newstate = weaponinfo[player->readyweapon].atkstate;\n    P_SetPsprite (player, ps_weapon, newstate);\n    P_NoiseAlert (player->mo, player->mo);\n}\n\n\n\n//\n// P_DropWeapon\n// Player died, so put the weapon away.\n//\nvoid P_DropWeapon (player_t* player)\n{\n    P_SetPsprite (player,\n\t\t  ps_weapon,\n\t\t  weaponinfo[player->readyweapon].downstate);\n}\n\n\n\n//\n// A_WeaponReady\n// The player can fire the weapon\n// or change to another weapon at this time.\n// Follows after getting weapon up,\n// or after previous attack/fire sequence.\n//\nvoid\nA_WeaponReady\n( player_t*\tplayer,\n  pspdef_t*\tpsp )\n{\t\n    statenum_t\tnewstate;\n    int\t\tangle;\n    \n    // get out of attack state\n    if (player->mo->state == &states[S_PLAY_ATK1]\n\t|| player->mo->state == &states[S_PLAY_ATK2] )\n    {\n\tP_SetMobjState (player->mo, S_PLAY);\n    }\n    \n    if (player->readyweapon == wp_chainsaw\n\t&& psp->state == &states[S_SAW])\n    {\n\tS_StartSound (player->mo, sfx_sawidl);\n    }\n    \n    // check for change\n    //  if player is dead, put the weapon away\n    if (player->pendingweapon != wp_nochange || !player->health)\n    {\n\t// change weapon\n\t//  (pending weapon should allready be validated)\n\tnewstate = weaponinfo[player->readyweapon].downstate;\n\tP_SetPsprite (player, ps_weapon, newstate);\n\treturn;\t\n    }\n    \n    // check for fire\n    //  the missile launcher and bfg do not auto fire\n    if (player->cmd.buttons & BT_ATTACK)\n    {\n\tif ( !player->attackdown\n\t     || (player->readyweapon != wp_missile\n\t\t && player->readyweapon != wp_bfg) )\n\t{\n\t    player->attackdown = true;\n\t    P_FireWeapon (player);\t\t\n\t    return;\n\t}\n    }\n    else\n\tplayer->attackdown = false;\n    \n    // bob the weapon based on movement speed\n    angle = (128*leveltime)&FINEMASK;\n    psp->sx = FRACUNIT + FixedMul (player->bob, finecosine[angle]);\n    angle &= FINEANGLES/2-1;\n    psp->sy = WEAPONTOP + FixedMul (player->bob, finesine[angle]);\n}\n\n\n\n//\n// A_ReFire\n// The player can re-fire the weapon\n// without lowering it entirely.\n//\nvoid A_ReFire\n( player_t*\tplayer,\n  pspdef_t*\tpsp )\n{\n    \n    // check for fire\n    //  (if a weaponchange is pending, let it go through instead)\n    if ( (player->cmd.buttons & BT_ATTACK) \n\t && player->pendingweapon == wp_nochange\n\t && player->health)\n    {\n\tplayer->refire++;\n\tP_FireWeapon (player);\n    }\n    else\n    {\n\tplayer->refire = 0;\n\tP_CheckAmmo (player);\n    }\n}\n\n\nvoid\nA_CheckReload\n( player_t*\tplayer,\n  pspdef_t*\tpsp )\n{\n    P_CheckAmmo (player);\n#if 0\n    if (player->ammo[am_shell]<2)\n\tP_SetPsprite (player, ps_weapon, S_DSNR1);\n#endif\n}\n\n\n\n//\n// A_Lower\n// Lowers current weapon,\n//  and changes weapon at bottom.\n//\nvoid\nA_Lower\n( player_t*\tplayer,\n  pspdef_t*\tpsp )\n{\t\n    psp->sy += LOWERSPEED;\n\n    // Is already down.\n    if (psp->sy < WEAPONBOTTOM )\n\treturn;\n\n    // Player is dead.\n    if (player->playerstate == PST_DEAD)\n    {\n\tpsp->sy = WEAPONBOTTOM;\n\n\t// don't bring weapon back up\n\treturn;\t\t\n    }\n    \n    // The old weapon has been lowered off the screen,\n    // so change the weapon and start raising it\n    if (!player->health)\n    {\n\t// Player is dead, so keep the weapon off screen.\n\tP_SetPsprite (player,  ps_weapon, S_NULL);\n\treturn;\t\n    }\n\t\n    player->readyweapon = player->pendingweapon; \n\n    P_BringUpWeapon (player);\n}\n\n\n//\n// A_Raise\n//\nvoid\nA_Raise\n( player_t*\tplayer,\n  pspdef_t*\tpsp )\n{\n    statenum_t\tnewstate;\n\t\n    psp->sy -= RAISESPEED;\n\n    if (psp->sy > WEAPONTOP )\n\treturn;\n    \n    psp->sy = WEAPONTOP;\n    \n    // The weapon has been raised all the way,\n    //  so change to the ready state.\n    newstate = weaponinfo[player->readyweapon].readystate;\n\n    P_SetPsprite (player, ps_weapon, newstate);\n}\n\n\n\n//\n// A_GunFlash\n//\nvoid\nA_GunFlash\n( player_t*\tplayer,\n  pspdef_t*\tpsp ) \n{\n    P_SetMobjState (player->mo, S_PLAY_ATK2);\n    P_SetPsprite (player,ps_flash,weaponinfo[player->readyweapon].flashstate);\n}\n\n\n\n//\n// WEAPON ATTACKS\n//\n\n\n//\n// A_Punch\n//\nvoid\nA_Punch\n( player_t*\tplayer,\n  pspdef_t*\tpsp ) \n{\n    angle_t\tangle;\n    int\t\tdamage;\n    int\t\tslope;\n\t\n    damage = (P_Random ()%10+1)<<1;\n\n    if (player->powers[pw_strength])\t\n\tdamage *= 10;\n\n    angle = player->mo->angle;\n    angle += (P_Random()-P_Random())<<18;\n    slope = P_AimLineAttack (player->mo, angle, MELEERANGE);\n    P_LineAttack (player->mo, angle, MELEERANGE, slope, damage);\n\n    // turn to face target\n    if (linetarget)\n    {\n\tS_StartSound (player->mo, sfx_punch);\n\tplayer->mo->angle = R_PointToAngle2 (player->mo->x,\n\t\t\t\t\t     player->mo->y,\n\t\t\t\t\t     linetarget->x,\n\t\t\t\t\t     linetarget->y);\n    }\n}\n\n\n//\n// A_Saw\n//\nvoid\nA_Saw\n( player_t*\tplayer,\n  pspdef_t*\tpsp ) \n{\n    angle_t\tangle;\n    int\t\tdamage;\n    int\t\tslope;\n\n    damage = 2*(P_Random ()%10+1);\n    angle = player->mo->angle;\n    angle += (P_Random()-P_Random())<<18;\n    \n    // use meleerange + 1 se the puff doesn't skip the flash\n    slope = P_AimLineAttack (player->mo, angle, MELEERANGE+1);\n    P_LineAttack (player->mo, angle, MELEERANGE+1, slope, damage);\n\n    if (!linetarget)\n    {\n\tS_StartSound (player->mo, sfx_sawful);\n\treturn;\n    }\n    S_StartSound (player->mo, sfx_sawhit);\n\t\n    // turn to face target\n    angle = R_PointToAngle2 (player->mo->x, player->mo->y,\n\t\t\t     linetarget->x, linetarget->y);\n    if (angle - player->mo->angle > ANG180)\n    {\n\tif ((signed int) (angle - player->mo->angle) < -ANG90/20)\n\t    player->mo->angle = angle + ANG90/21;\n\telse\n\t    player->mo->angle -= ANG90/20;\n    }\n    else\n    {\n\tif (angle - player->mo->angle > ANG90/20)\n\t    player->mo->angle = angle - ANG90/21;\n\telse\n\t    player->mo->angle += ANG90/20;\n    }\n    player->mo->flags |= MF_JUSTATTACKED;\n}\n\n// Doom does not check the bounds of the ammo array.  As a result,\n// it is possible to use an ammo type > 4 that overflows into the\n// maxammo array and affects that instead.  Through dehacked, for\n// example, it is possible to make a weapon that decreases the max\n// number of ammo for another weapon.  Emulate this.\n\nstatic void DecreaseAmmo(player_t *player, int ammonum, int amount)\n{\n    if (ammonum < NUMAMMO)\n    {\n        player->ammo[ammonum] -= amount;\n    }\n    else\n    {\n        player->maxammo[ammonum - NUMAMMO] -= amount;\n    }\n}\n\n\n//\n// A_FireMissile\n//\nvoid\nA_FireMissile\n( player_t*\tplayer,\n  pspdef_t*\tpsp ) \n{\n    DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1);\n    P_SpawnPlayerMissile (player->mo, MT_ROCKET);\n}\n\n\n//\n// A_FireBFG\n//\nvoid\nA_FireBFG\n( player_t*\tplayer,\n  pspdef_t*\tpsp ) \n{\n    DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, \n                 deh_bfg_cells_per_shot);\n    P_SpawnPlayerMissile (player->mo, MT_BFG);\n}\n\n\n\n//\n// A_FirePlasma\n//\nvoid\nA_FirePlasma\n( player_t*\tplayer,\n  pspdef_t*\tpsp ) \n{\n    DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1);\n\n    P_SetPsprite (player,\n\t\t  ps_flash,\n\t\t  weaponinfo[player->readyweapon].flashstate+(P_Random ()&1) );\n\n    P_SpawnPlayerMissile (player->mo, MT_PLASMA);\n}\n\n\n\n//\n// P_BulletSlope\n// Sets a slope so a near miss is at aproximately\n// the height of the intended target\n//\nfixed_t\t\tbulletslope;\n\n\nvoid P_BulletSlope (mobj_t*\tmo)\n{\n    angle_t\tan;\n    \n    // see which target is to be aimed at\n    an = mo->angle;\n    bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);\n\n    if (!linetarget)\n    {\n\tan += 1<<26;\n\tbulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);\n\tif (!linetarget)\n\t{\n\t    an -= 2<<26;\n\t    bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);\n\t}\n    }\n}\n\n\n//\n// P_GunShot\n//\nvoid\nP_GunShot\n( mobj_t*\tmo,\n  boolean\taccurate )\n{\n    angle_t\tangle;\n    int\t\tdamage;\n\t\n    damage = 5*(P_Random ()%3+1);\n    angle = mo->angle;\n\n    if (!accurate)\n\tangle += (P_Random()-P_Random())<<18;\n\n    P_LineAttack (mo, angle, MISSILERANGE, bulletslope, damage);\n}\n\n\n//\n// A_FirePistol\n//\nvoid\nA_FirePistol\n( player_t*\tplayer,\n  pspdef_t*\tpsp ) \n{\n    S_StartSound (player->mo, sfx_pistol);\n\n    P_SetMobjState (player->mo, S_PLAY_ATK2);\n    DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1);\n\n    P_SetPsprite (player,\n\t\t  ps_flash,\n\t\t  weaponinfo[player->readyweapon].flashstate);\n\n    P_BulletSlope (player->mo);\n    P_GunShot (player->mo, !player->refire);\n}\n\n\n//\n// A_FireShotgun\n//\nvoid\nA_FireShotgun\n( player_t*\tplayer,\n  pspdef_t*\tpsp ) \n{\n    int\t\ti;\n\t\n    S_StartSound (player->mo, sfx_shotgn);\n    P_SetMobjState (player->mo, S_PLAY_ATK2);\n\n    DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1);\n\n    P_SetPsprite (player,\n\t\t  ps_flash,\n\t\t  weaponinfo[player->readyweapon].flashstate);\n\n    P_BulletSlope (player->mo);\n\t\n    for (i=0 ; i<7 ; i++)\n\tP_GunShot (player->mo, false);\n}\n\n\n\n//\n// A_FireShotgun2\n//\nvoid\nA_FireShotgun2\n( player_t*\tplayer,\n  pspdef_t*\tpsp ) \n{\n    int\t\ti;\n    angle_t\tangle;\n    int\t\tdamage;\n\t\t\n\t\n    S_StartSound (player->mo, sfx_dshtgn);\n    P_SetMobjState (player->mo, S_PLAY_ATK2);\n\n    DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 2);\n\n    P_SetPsprite (player,\n\t\t  ps_flash,\n\t\t  weaponinfo[player->readyweapon].flashstate);\n\n    P_BulletSlope (player->mo);\n\t\n    for (i=0 ; i<20 ; i++)\n    {\n\tdamage = 5*(P_Random ()%3+1);\n\tangle = player->mo->angle;\n\tangle += (P_Random()-P_Random())<<19;\n\tP_LineAttack (player->mo,\n\t\t      angle,\n\t\t      MISSILERANGE,\n\t\t      bulletslope + ((P_Random()-P_Random())<<5), damage);\n    }\n}\n\n\n//\n// A_FireCGun\n//\nvoid\nA_FireCGun\n( player_t*\tplayer,\n  pspdef_t*\tpsp ) \n{\n    S_StartSound (player->mo, sfx_pistol);\n\n    if (!player->ammo[weaponinfo[player->readyweapon].ammo])\n\treturn;\n\t\t\n    P_SetMobjState (player->mo, S_PLAY_ATK2);\n    DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1);\n\n    P_SetPsprite (player,\n\t\t  ps_flash,\n\t\t  weaponinfo[player->readyweapon].flashstate\n\t\t  + psp->state\n\t\t  - &states[S_CHAIN1] );\n\n    P_BulletSlope (player->mo);\n\t\n    P_GunShot (player->mo, !player->refire);\n}\n\n\n\n//\n// ?\n//\nvoid A_Light0 (player_t *player, pspdef_t *psp)\n{\n    player->extralight = 0;\n}\n\nvoid A_Light1 (player_t *player, pspdef_t *psp)\n{\n    player->extralight = 1;\n}\n\nvoid A_Light2 (player_t *player, pspdef_t *psp)\n{\n    player->extralight = 2;\n}\n\n\n//\n// A_BFGSpray\n// Spawn a BFG explosion on every monster in view\n//\nvoid A_BFGSpray (mobj_t* mo) \n{\n    int\t\t\ti;\n    int\t\t\tj;\n    int\t\t\tdamage;\n    angle_t\t\tan;\n\t\n    // offset angles from its attack angle\n    for (i=0 ; i<40 ; i++)\n    {\n\tan = mo->angle - ANG90/2 + ANG90/40*i;\n\n\t// mo->target is the originator (player)\n\t//  of the missile\n\tP_AimLineAttack (mo->target, an, 16*64*FRACUNIT);\n\n\tif (!linetarget)\n\t    continue;\n\n\tP_SpawnMobj (linetarget->x,\n\t\t     linetarget->y,\n\t\t     linetarget->z + (linetarget->height>>2),\n\t\t     MT_EXTRABFG);\n\t\n\tdamage = 0;\n\tfor (j=0;j<15;j++)\n\t    damage += (P_Random()&7) + 1;\n\n\tP_DamageMobj (linetarget, mo->target,mo->target, damage);\n    }\n}\n\n\n//\n// A_BFGsound\n//\nvoid\nA_BFGsound\n( player_t*\tplayer,\n  pspdef_t*\tpsp )\n{\n    S_StartSound (player->mo, sfx_bfg);\n}\n\n\n\n//\n// P_SetupPsprites\n// Called at start of level for each player.\n//\nvoid P_SetupPsprites (player_t* player) \n{\n    int\ti;\n\t\n    // remove all psprites\n    for (i=0 ; i<NUMPSPRITES ; i++)\n\tplayer->psprites[i].state = NULL;\n\t\t\n    // spawn the gun\n    player->pendingweapon = player->readyweapon;\n    P_BringUpWeapon (player);\n}\n\n\n\n\n//\n// P_MovePsprites\n// Called every tic by player thinking routine.\n//\nvoid P_MovePsprites (player_t* player) \n{\n    int\t\ti;\n    pspdef_t*\tpsp;\n    state_t*\tstate;\n\t\n    psp = &player->psprites[0];\n    for (i=0 ; i<NUMPSPRITES ; i++, psp++)\n    {\n\t// a null state means not active\n\tif ( (state = psp->state) )\t\n\t{\n\t    // drop tic count and possibly change state\n\n\t    // a -1 tic count never changes\n\t    if (psp->tics != -1)\t\n\t    {\n\t\tpsp->tics--;\n\t\tif (!psp->tics)\n\t\t    P_SetPsprite (player, i, psp->state->nextstate);\n\t    }\t\t\t\t\n\t}\n    }\n    \n    player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx;\n    player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy;\n}\n\n\n"
  },
  {
    "path": "fbdoom/p_pspr.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//  Sprite animation.\n//\n\n\n#ifndef __P_PSPR__\n#define __P_PSPR__\n\n// Basic data types.\n// Needs fixed point, and BAM angles.\n#include \"m_fixed.h\"\n#include \"tables.h\"\n\n\n//\n// Needs to include the precompiled\n//  sprite animation tables.\n// Header generated by multigen utility.\n// This includes all the data for thing animation,\n// i.e. the Thing Atrributes table\n// and the Frame Sequence table.\n#include \"info.h\"\n\n\n\n//\n// Frame flags:\n// handles maximum brightness (torches, muzzle flare, light sources)\n//\n#define FF_FULLBRIGHT\t0x8000\t// flag in thing->frame\n#define FF_FRAMEMASK\t0x7fff\n\n\n\n//\n// Overlay psprites are scaled shapes\n// drawn directly on the view screen,\n// coordinates are given for a 320*200 view screen.\n//\ntypedef enum\n{\n    ps_weapon,\n    ps_flash,\n    NUMPSPRITES\n\n} psprnum_t;\n\ntypedef struct\n{\n    state_t*\tstate;\t// a NULL state means not active\n    int\t\ttics;\n    fixed_t\tsx;\n    fixed_t\tsy;\n\n} pspdef_t;\n\n#endif\n"
  },
  {
    "path": "fbdoom/p_saveg.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tArchiving: SaveGame I/O.\n//\n\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"dstrings.h\"\n#include \"deh_main.h\"\n#include \"i_system.h\"\n#include \"z_zone.h\"\n#include \"p_local.h\"\n#include \"p_saveg.h\"\n\n// State.\n#include \"doomstat.h\"\n#include \"g_game.h\"\n#include \"m_misc.h\"\n#include \"r_state.h\"\n\n#define SAVEGAME_EOF 0x1d\n#define VERSIONSIZE 16 \n\nFILE *save_stream;\nint savegamelength;\nboolean savegame_error;\n\n// Get the filename of a temporary file to write the savegame to.  After\n// the file has been successfully saved, it will be renamed to the \n// real file.\n\nchar *P_TempSaveGameFile(void)\n{\n    static char *filename = NULL;\n\n    if (filename == NULL)\n    {\n        filename = M_StringJoin(savegamedir, \"temp.dsg\", NULL);\n    }\n\n    return filename;\n}\n\n// Get the filename of the save game file to use for the specified slot.\n\nchar *P_SaveGameFile(int slot)\n{\n    static char *filename = NULL;\n    static size_t filename_size = 0;\n    char basename[32];\n\n    if (filename == NULL)\n    {\n        filename_size = strlen(savegamedir) + 32;\n        filename = malloc(filename_size);\n    }\n\n    DEH_snprintf(basename, 32, SAVEGAMENAME \"%d.dsg\", slot);\n    M_snprintf(filename, filename_size, \"%s%s\", savegamedir, basename);\n\n    return filename;\n}\n\n// Endian-safe integer read/write functions\n\nstatic byte saveg_read8(void)\n{\n    byte result;\n\n    if (fread(&result, 1, 1, save_stream) < 1)\n    {\n        if (!savegame_error)\n        {\n            fprintf(stderr, \"saveg_read8: Unexpected end of file while \"\n                            \"reading save game\\n\");\n\n            savegame_error = true;\n        }\n    }\n\n    return result;\n}\n\nstatic void saveg_write8(byte value)\n{\n    if (fwrite(&value, 1, 1, save_stream) < 1)\n    {\n        if (!savegame_error)\n        {\n            fprintf(stderr, \"saveg_write8: Error while writing save game\\n\");\n\n            savegame_error = true;\n        }\n    }\n}\n\nstatic short saveg_read16(void)\n{\n    int result;\n\n    result = saveg_read8();\n    result |= saveg_read8() << 8;\n\n    return result;\n}\n\nstatic void saveg_write16(short value)\n{\n    saveg_write8(value & 0xff);\n    saveg_write8((value >> 8) & 0xff);\n}\n\nstatic int saveg_read32(void)\n{\n    int result;\n\n    result = saveg_read8();\n    result |= saveg_read8() << 8;\n    result |= saveg_read8() << 16;\n    result |= saveg_read8() << 24;\n\n    return result;\n}\n\nstatic void saveg_write32(int value)\n{\n    saveg_write8(value & 0xff);\n    saveg_write8((value >> 8) & 0xff);\n    saveg_write8((value >> 16) & 0xff);\n    saveg_write8((value >> 24) & 0xff);\n}\n\n// Pad to 4-byte boundaries\n\nstatic void saveg_read_pad(void)\n{\n    unsigned long pos;\n    int padding;\n    int i;\n\n    pos = ftell(save_stream);\n\n    padding = (4 - (pos & 3)) & 3;\n\n    for (i=0; i<padding; ++i)\n    {\n        saveg_read8();\n    }\n}\n\nstatic void saveg_write_pad(void)\n{\n    unsigned long pos;\n    int padding;\n    int i;\n\n    pos = ftell(save_stream);\n\n    padding = (4 - (pos & 3)) & 3;\n\n    for (i=0; i<padding; ++i)\n    {\n        saveg_write8(0);\n    }\n}\n\n\n// Pointers\n\nstatic void *saveg_readp(void)\n{\n    return (void *) (intptr_t) saveg_read32();\n}\n\nstatic void saveg_writep(void *p)\n{\n    saveg_write32((intptr_t) p);\n}\n\n// Enum values are 32-bit integers.\n\n#define saveg_read_enum saveg_read32\n#define saveg_write_enum saveg_write32\n\n//\n// Structure read/write functions\n//\n\n//\n// mapthing_t\n//\n\nstatic void saveg_read_mapthing_t(mapthing_t *str)\n{\n    // short x;\n    str->x = saveg_read16();\n\n    // short y;\n    str->y = saveg_read16();\n\n    // short angle;\n    str->angle = saveg_read16();\n\n    // short type;\n    str->type = saveg_read16();\n\n    // short options;\n    str->options = saveg_read16();\n}\n\nstatic void saveg_write_mapthing_t(mapthing_t *str)\n{\n    // short x;\n    saveg_write16(str->x);\n\n    // short y;\n    saveg_write16(str->y);\n\n    // short angle;\n    saveg_write16(str->angle);\n\n    // short type;\n    saveg_write16(str->type);\n\n    // short options;\n    saveg_write16(str->options);\n}\n\n//\n// actionf_t\n// \n\nstatic void saveg_read_actionf_t(actionf_t *str)\n{\n    // actionf_p1 acp1;\n    str->acp1 = saveg_readp();\n}\n\nstatic void saveg_write_actionf_t(actionf_t *str)\n{\n    // actionf_p1 acp1;\n    saveg_writep(str->acp1);\n}\n\n//\n// think_t\n//\n// This is just an actionf_t.\n//\n\n#define saveg_read_think_t saveg_read_actionf_t\n#define saveg_write_think_t saveg_write_actionf_t\n\n//\n// thinker_t\n//\n\nstatic void saveg_read_thinker_t(thinker_t *str)\n{\n    // struct thinker_s* prev;\n    str->prev = saveg_readp();\n\n    // struct thinker_s* next;\n    str->next = saveg_readp();\n\n    // think_t function;\n    saveg_read_think_t(&str->function);\n}\n\nstatic void saveg_write_thinker_t(thinker_t *str)\n{\n    // struct thinker_s* prev;\n    saveg_writep(str->prev);\n\n    // struct thinker_s* next;\n    saveg_writep(str->next);\n\n    // think_t function;\n    saveg_write_think_t(&str->function);\n}\n\n//\n// mobj_t\n//\n\nstatic void saveg_read_mobj_t(mobj_t *str)\n{\n    int pl;\n\n    // thinker_t thinker;\n    saveg_read_thinker_t(&str->thinker);\n\n    // fixed_t x;\n    str->x = saveg_read32();\n\n    // fixed_t y;\n    str->y = saveg_read32();\n\n    // fixed_t z;\n    str->z = saveg_read32();\n\n    // struct mobj_s* snext;\n    str->snext = saveg_readp();\n\n    // struct mobj_s* sprev;\n    str->sprev = saveg_readp();\n\n    // angle_t angle;\n    str->angle = saveg_read32();\n\n    // spritenum_t sprite;\n    str->sprite = saveg_read_enum();\n\n    // int frame;\n    str->frame = saveg_read32();\n\n    // struct mobj_s* bnext;\n    str->bnext = saveg_readp();\n\n    // struct mobj_s* bprev;\n    str->bprev = saveg_readp();\n\n    // struct subsector_s* subsector;\n    str->subsector = saveg_readp();\n\n    // fixed_t floorz;\n    str->floorz = saveg_read32();\n\n    // fixed_t ceilingz;\n    str->ceilingz = saveg_read32();\n\n    // fixed_t radius;\n    str->radius = saveg_read32();\n\n    // fixed_t height;\n    str->height = saveg_read32();\n\n    // fixed_t momx;\n    str->momx = saveg_read32();\n\n    // fixed_t momy;\n    str->momy = saveg_read32();\n\n    // fixed_t momz;\n    str->momz = saveg_read32();\n\n    // int validcount;\n    str->validcount = saveg_read32();\n\n    // mobjtype_t type;\n    str->type = saveg_read_enum();\n\n    // mobjinfo_t* info;\n    str->info = saveg_readp();\n\n    // int tics;\n    str->tics = saveg_read32();\n\n    // state_t* state;\n    str->state = &states[saveg_read32()];\n\n    // int flags;\n    str->flags = saveg_read32();\n\n    // int health;\n    str->health = saveg_read32();\n\n    // int movedir;\n    str->movedir = saveg_read32();\n\n    // int movecount;\n    str->movecount = saveg_read32();\n\n    // struct mobj_s* target;\n    str->target = saveg_readp();\n\n    // int reactiontime;\n    str->reactiontime = saveg_read32();\n\n    // int threshold;\n    str->threshold = saveg_read32();\n\n    // struct player_s* player;\n    pl = saveg_read32();\n\n    if (pl > 0)\n    {\n        str->player = &players[pl - 1];\n        str->player->mo = str;\n    }\n    else\n    {\n        str->player = NULL;\n    }\n\n    // int lastlook;\n    str->lastlook = saveg_read32();\n\n    // mapthing_t spawnpoint;\n    saveg_read_mapthing_t(&str->spawnpoint);\n\n    // struct mobj_s* tracer;\n    str->tracer = saveg_readp();\n}\n\nstatic void saveg_write_mobj_t(mobj_t *str)\n{\n    // thinker_t thinker;\n    saveg_write_thinker_t(&str->thinker);\n\n    // fixed_t x;\n    saveg_write32(str->x);\n\n    // fixed_t y;\n    saveg_write32(str->y);\n\n    // fixed_t z;\n    saveg_write32(str->z);\n\n    // struct mobj_s* snext;\n    saveg_writep(str->snext);\n\n    // struct mobj_s* sprev;\n    saveg_writep(str->sprev);\n\n    // angle_t angle;\n    saveg_write32(str->angle);\n\n    // spritenum_t sprite;\n    saveg_write_enum(str->sprite);\n\n    // int frame;\n    saveg_write32(str->frame);\n\n    // struct mobj_s* bnext;\n    saveg_writep(str->bnext);\n\n    // struct mobj_s* bprev;\n    saveg_writep(str->bprev);\n\n    // struct subsector_s* subsector;\n    saveg_writep(str->subsector);\n\n    // fixed_t floorz;\n    saveg_write32(str->floorz);\n\n    // fixed_t ceilingz;\n    saveg_write32(str->ceilingz);\n\n    // fixed_t radius;\n    saveg_write32(str->radius);\n\n    // fixed_t height;\n    saveg_write32(str->height);\n\n    // fixed_t momx;\n    saveg_write32(str->momx);\n\n    // fixed_t momy;\n    saveg_write32(str->momy);\n\n    // fixed_t momz;\n    saveg_write32(str->momz);\n\n    // int validcount;\n    saveg_write32(str->validcount);\n\n    // mobjtype_t type;\n    saveg_write_enum(str->type);\n\n    // mobjinfo_t* info;\n    saveg_writep(str->info);\n\n    // int tics;\n    saveg_write32(str->tics);\n\n    // state_t* state;\n    saveg_write32(str->state - states);\n\n    // int flags;\n    saveg_write32(str->flags);\n\n    // int health;\n    saveg_write32(str->health);\n\n    // int movedir;\n    saveg_write32(str->movedir);\n\n    // int movecount;\n    saveg_write32(str->movecount);\n\n    // struct mobj_s* target;\n    saveg_writep(str->target);\n\n    // int reactiontime;\n    saveg_write32(str->reactiontime);\n\n    // int threshold;\n    saveg_write32(str->threshold);\n\n    // struct player_s* player;\n    if (str->player)\n    {\n        saveg_write32(str->player - players + 1);\n    }\n    else\n    {\n        saveg_write32(0);\n    }\n\n    // int lastlook;\n    saveg_write32(str->lastlook);\n\n    // mapthing_t spawnpoint;\n    saveg_write_mapthing_t(&str->spawnpoint);\n\n    // struct mobj_s* tracer;\n    saveg_writep(str->tracer);\n}\n\n\n//\n// ticcmd_t\n//\n\nstatic void saveg_read_ticcmd_t(ticcmd_t *str)\n{\n\n    // signed char forwardmove;\n    str->forwardmove = saveg_read8();\n\n    // signed char sidemove;\n    str->sidemove = saveg_read8();\n\n    // short angleturn;\n    str->angleturn = saveg_read16();\n\n    // short consistancy;\n    str->consistancy = saveg_read16();\n\n    // byte chatchar;\n    str->chatchar = saveg_read8();\n\n    // byte buttons;\n    str->buttons = saveg_read8();\n}\n\nstatic void saveg_write_ticcmd_t(ticcmd_t *str)\n{\n\n    // signed char forwardmove;\n    saveg_write8(str->forwardmove);\n\n    // signed char sidemove;\n    saveg_write8(str->sidemove);\n\n    // short angleturn;\n    saveg_write16(str->angleturn);\n\n    // short consistancy;\n    saveg_write16(str->consistancy);\n\n    // byte chatchar;\n    saveg_write8(str->chatchar);\n\n    // byte buttons;\n    saveg_write8(str->buttons);\n}\n\n//\n// pspdef_t\n//\n\nstatic void saveg_read_pspdef_t(pspdef_t *str)\n{\n    int state;\n\n    // state_t* state;\n    state = saveg_read32();\n\n    if (state > 0)\n    {\n        str->state = &states[state];\n    }\n    else\n    {\n        str->state = NULL;\n    }\n\n    // int tics;\n    str->tics = saveg_read32();\n\n    // fixed_t sx;\n    str->sx = saveg_read32();\n\n    // fixed_t sy;\n    str->sy = saveg_read32();\n}\n\nstatic void saveg_write_pspdef_t(pspdef_t *str)\n{\n    // state_t* state;\n    if (str->state)\n    {\n        saveg_write32(str->state - states);\n    }\n    else\n    {\n        saveg_write32(0);\n    }\n\n    // int tics;\n    saveg_write32(str->tics);\n\n    // fixed_t sx;\n    saveg_write32(str->sx);\n\n    // fixed_t sy;\n    saveg_write32(str->sy);\n}\n\n//\n// player_t\n//\n\nstatic void saveg_read_player_t(player_t *str)\n{\n    int i;\n\n    // mobj_t* mo;\n    str->mo = saveg_readp();\n\n    // playerstate_t playerstate;\n    str->playerstate = saveg_read_enum();\n\n    // ticcmd_t cmd;\n    saveg_read_ticcmd_t(&str->cmd);\n\n    // fixed_t viewz;\n    str->viewz = saveg_read32();\n\n    // fixed_t viewheight;\n    str->viewheight = saveg_read32();\n\n    // fixed_t deltaviewheight;\n    str->deltaviewheight = saveg_read32();\n\n    // fixed_t bob;\n    str->bob = saveg_read32();\n\n    // int health;\n    str->health = saveg_read32();\n\n    // int armorpoints;\n    str->armorpoints = saveg_read32();\n\n    // int armortype;\n    str->armortype = saveg_read32();\n\n    // int powers[NUMPOWERS];\n    for (i=0; i<NUMPOWERS; ++i)\n    {\n        str->powers[i] = saveg_read32();\n    }\n\n    // boolean cards[NUMCARDS];\n    for (i=0; i<NUMCARDS; ++i)\n    {\n        str->cards[i] = saveg_read32();\n    }\n\n    // boolean backpack;\n    str->backpack = saveg_read32();\n\n    // int frags[MAXPLAYERS];\n    for (i=0; i<MAXPLAYERS; ++i)\n    {\n        str->frags[i] = saveg_read32();\n    }\n\n    // weapontype_t readyweapon;\n    str->readyweapon = saveg_read_enum();\n\n    // weapontype_t pendingweapon;\n    str->pendingweapon = saveg_read_enum();\n\n    // boolean weaponowned[NUMWEAPONS];\n    for (i=0; i<NUMWEAPONS; ++i)\n    {\n        str->weaponowned[i] = saveg_read32();\n    }\n\n    // int ammo[NUMAMMO];\n    for (i=0; i<NUMAMMO; ++i)\n    {\n        str->ammo[i] = saveg_read32();\n    }\n\n    // int maxammo[NUMAMMO];\n    for (i=0; i<NUMAMMO; ++i)\n    {\n        str->maxammo[i] = saveg_read32();\n    }\n\n    // int attackdown;\n    str->attackdown = saveg_read32();\n\n    // int usedown;\n    str->usedown = saveg_read32();\n\n    // int cheats;\n    str->cheats = saveg_read32();\n\n    // int refire;\n    str->refire = saveg_read32();\n\n    // int killcount;\n    str->killcount = saveg_read32();\n\n    // int itemcount;\n    str->itemcount = saveg_read32();\n\n    // int secretcount;\n    str->secretcount = saveg_read32();\n\n    // char* message;\n    str->message = saveg_readp();\n\n    // int damagecount;\n    str->damagecount = saveg_read32();\n\n    // int bonuscount;\n    str->bonuscount = saveg_read32();\n\n    // mobj_t* attacker;\n    str->attacker = saveg_readp();\n\n    // int extralight;\n    str->extralight = saveg_read32();\n\n    // int fixedcolormap;\n    str->fixedcolormap = saveg_read32();\n\n    // int colormap;\n    str->colormap = saveg_read32();\n\n    // pspdef_t psprites[NUMPSPRITES];\n    for (i=0; i<NUMPSPRITES; ++i)\n    {\n        saveg_read_pspdef_t(&str->psprites[i]);\n    }\n\n    // boolean didsecret;\n    str->didsecret = saveg_read32();\n}\n\nstatic void saveg_write_player_t(player_t *str)\n{\n    int i;\n\n    // mobj_t* mo;\n    saveg_writep(str->mo);\n\n    // playerstate_t playerstate;\n    saveg_write_enum(str->playerstate);\n\n    // ticcmd_t cmd;\n    saveg_write_ticcmd_t(&str->cmd);\n\n    // fixed_t viewz;\n    saveg_write32(str->viewz);\n\n    // fixed_t viewheight;\n    saveg_write32(str->viewheight);\n\n    // fixed_t deltaviewheight;\n    saveg_write32(str->deltaviewheight);\n\n    // fixed_t bob;\n    saveg_write32(str->bob);\n\n    // int health;\n    saveg_write32(str->health);\n\n    // int armorpoints;\n    saveg_write32(str->armorpoints);\n\n    // int armortype;\n    saveg_write32(str->armortype);\n\n    // int powers[NUMPOWERS];\n    for (i=0; i<NUMPOWERS; ++i)\n    {\n        saveg_write32(str->powers[i]);\n    }\n\n    // boolean cards[NUMCARDS];\n    for (i=0; i<NUMCARDS; ++i)\n    {\n        saveg_write32(str->cards[i]);\n    }\n\n    // boolean backpack;\n    saveg_write32(str->backpack);\n\n    // int frags[MAXPLAYERS];\n    for (i=0; i<MAXPLAYERS; ++i)\n    {\n        saveg_write32(str->frags[i]);\n    }\n\n    // weapontype_t readyweapon;\n    saveg_write_enum(str->readyweapon);\n\n    // weapontype_t pendingweapon;\n    saveg_write_enum(str->pendingweapon);\n\n    // boolean weaponowned[NUMWEAPONS];\n    for (i=0; i<NUMWEAPONS; ++i)\n    {\n        saveg_write32(str->weaponowned[i]);\n    }\n\n    // int ammo[NUMAMMO];\n    for (i=0; i<NUMAMMO; ++i)\n    {\n        saveg_write32(str->ammo[i]);\n    }\n\n    // int maxammo[NUMAMMO];\n    for (i=0; i<NUMAMMO; ++i)\n    {\n        saveg_write32(str->maxammo[i]);\n    }\n\n    // int attackdown;\n    saveg_write32(str->attackdown);\n\n    // int usedown;\n    saveg_write32(str->usedown);\n\n    // int cheats;\n    saveg_write32(str->cheats);\n\n    // int refire;\n    saveg_write32(str->refire);\n\n    // int killcount;\n    saveg_write32(str->killcount);\n\n    // int itemcount;\n    saveg_write32(str->itemcount);\n\n    // int secretcount;\n    saveg_write32(str->secretcount);\n\n    // char* message;\n    saveg_writep(str->message);\n\n    // int damagecount;\n    saveg_write32(str->damagecount);\n\n    // int bonuscount;\n    saveg_write32(str->bonuscount);\n\n    // mobj_t* attacker;\n    saveg_writep(str->attacker);\n\n    // int extralight;\n    saveg_write32(str->extralight);\n\n    // int fixedcolormap;\n    saveg_write32(str->fixedcolormap);\n\n    // int colormap;\n    saveg_write32(str->colormap);\n\n    // pspdef_t psprites[NUMPSPRITES];\n    for (i=0; i<NUMPSPRITES; ++i)\n    {\n        saveg_write_pspdef_t(&str->psprites[i]);\n    }\n\n    // boolean didsecret;\n    saveg_write32(str->didsecret);\n}\n\n\n//\n// ceiling_t\n//\n\nstatic void saveg_read_ceiling_t(ceiling_t *str)\n{\n    int sector;\n\n    // thinker_t thinker;\n    saveg_read_thinker_t(&str->thinker);\n\n    // ceiling_e type;\n    str->type = saveg_read_enum();\n\n    // sector_t* sector;\n    sector = saveg_read32();\n    str->sector = &sectors[sector];\n\n    // fixed_t bottomheight;\n    str->bottomheight = saveg_read32();\n\n    // fixed_t topheight;\n    str->topheight = saveg_read32();\n\n    // fixed_t speed;\n    str->speed = saveg_read32();\n\n    // boolean crush;\n    str->crush = saveg_read32();\n\n    // int direction;\n    str->direction = saveg_read32();\n\n    // int tag;\n    str->tag = saveg_read32();\n\n    // int olddirection;\n    str->olddirection = saveg_read32();\n}\n\nstatic void saveg_write_ceiling_t(ceiling_t *str)\n{\n    // thinker_t thinker;\n    saveg_write_thinker_t(&str->thinker);\n\n    // ceiling_e type;\n    saveg_write_enum(str->type);\n\n    // sector_t* sector;\n    saveg_write32(str->sector - sectors);\n\n    // fixed_t bottomheight;\n    saveg_write32(str->bottomheight);\n\n    // fixed_t topheight;\n    saveg_write32(str->topheight);\n\n    // fixed_t speed;\n    saveg_write32(str->speed);\n\n    // boolean crush;\n    saveg_write32(str->crush);\n\n    // int direction;\n    saveg_write32(str->direction);\n\n    // int tag;\n    saveg_write32(str->tag);\n\n    // int olddirection;\n    saveg_write32(str->olddirection);\n}\n\n//\n// vldoor_t\n//\n\nstatic void saveg_read_vldoor_t(vldoor_t *str)\n{\n    int sector;\n\n    // thinker_t thinker;\n    saveg_read_thinker_t(&str->thinker);\n\n    // vldoor_e type;\n    str->type = saveg_read_enum();\n\n    // sector_t* sector;\n    sector = saveg_read32();\n    str->sector = &sectors[sector];\n\n    // fixed_t topheight;\n    str->topheight = saveg_read32();\n\n    // fixed_t speed;\n    str->speed = saveg_read32();\n\n    // int direction;\n    str->direction = saveg_read32();\n\n    // int topwait;\n    str->topwait = saveg_read32();\n\n    // int topcountdown;\n    str->topcountdown = saveg_read32();\n}\n\nstatic void saveg_write_vldoor_t(vldoor_t *str)\n{\n    // thinker_t thinker;\n    saveg_write_thinker_t(&str->thinker);\n\n    // vldoor_e type;\n    saveg_write_enum(str->type);\n\n    // sector_t* sector;\n    saveg_write32(str->sector - sectors);\n\n    // fixed_t topheight;\n    saveg_write32(str->topheight);\n\n    // fixed_t speed;\n    saveg_write32(str->speed);\n\n    // int direction;\n    saveg_write32(str->direction);\n\n    // int topwait;\n    saveg_write32(str->topwait);\n\n    // int topcountdown;\n    saveg_write32(str->topcountdown);\n}\n\n//\n// floormove_t\n//\n\nstatic void saveg_read_floormove_t(floormove_t *str)\n{\n    int sector;\n\n    // thinker_t thinker;\n    saveg_read_thinker_t(&str->thinker);\n\n    // floor_e type;\n    str->type = saveg_read_enum();\n\n    // boolean crush;\n    str->crush = saveg_read32();\n\n    // sector_t* sector;\n    sector = saveg_read32();\n    str->sector = &sectors[sector];\n\n    // int direction;\n    str->direction = saveg_read32();\n\n    // int newspecial;\n    str->newspecial = saveg_read32();\n\n    // short texture;\n    str->texture = saveg_read16();\n\n    // fixed_t floordestheight;\n    str->floordestheight = saveg_read32();\n\n    // fixed_t speed;\n    str->speed = saveg_read32();\n}\n\nstatic void saveg_write_floormove_t(floormove_t *str)\n{\n    // thinker_t thinker;\n    saveg_write_thinker_t(&str->thinker);\n\n    // floor_e type;\n    saveg_write_enum(str->type);\n\n    // boolean crush;\n    saveg_write32(str->crush);\n\n    // sector_t* sector;\n    saveg_write32(str->sector - sectors);\n\n    // int direction;\n    saveg_write32(str->direction);\n\n    // int newspecial;\n    saveg_write32(str->newspecial);\n\n    // short texture;\n    saveg_write16(str->texture);\n\n    // fixed_t floordestheight;\n    saveg_write32(str->floordestheight);\n\n    // fixed_t speed;\n    saveg_write32(str->speed);\n}\n\n//\n// plat_t\n//\n\nstatic void saveg_read_plat_t(plat_t *str)\n{\n    int sector;\n\n    // thinker_t thinker;\n    saveg_read_thinker_t(&str->thinker);\n\n    // sector_t* sector;\n    sector = saveg_read32();\n    str->sector = &sectors[sector];\n\n    // fixed_t speed;\n    str->speed = saveg_read32();\n\n    // fixed_t low;\n    str->low = saveg_read32();\n\n    // fixed_t high;\n    str->high = saveg_read32();\n\n    // int wait;\n    str->wait = saveg_read32();\n\n    // int count;\n    str->count = saveg_read32();\n\n    // plat_e status;\n    str->status = saveg_read_enum();\n\n    // plat_e oldstatus;\n    str->oldstatus = saveg_read_enum();\n\n    // boolean crush;\n    str->crush = saveg_read32();\n\n    // int tag;\n    str->tag = saveg_read32();\n\n    // plattype_e type;\n    str->type = saveg_read_enum();\n}\n\nstatic void saveg_write_plat_t(plat_t *str)\n{\n    // thinker_t thinker;\n    saveg_write_thinker_t(&str->thinker);\n\n    // sector_t* sector;\n    saveg_write32(str->sector - sectors);\n\n    // fixed_t speed;\n    saveg_write32(str->speed);\n\n    // fixed_t low;\n    saveg_write32(str->low);\n\n    // fixed_t high;\n    saveg_write32(str->high);\n\n    // int wait;\n    saveg_write32(str->wait);\n\n    // int count;\n    saveg_write32(str->count);\n\n    // plat_e status;\n    saveg_write_enum(str->status);\n\n    // plat_e oldstatus;\n    saveg_write_enum(str->oldstatus);\n\n    // boolean crush;\n    saveg_write32(str->crush);\n\n    // int tag;\n    saveg_write32(str->tag);\n\n    // plattype_e type;\n    saveg_write_enum(str->type);\n}\n\n//\n// lightflash_t\n//\n\nstatic void saveg_read_lightflash_t(lightflash_t *str)\n{\n    int sector;\n\n    // thinker_t thinker;\n    saveg_read_thinker_t(&str->thinker);\n\n    // sector_t* sector;\n    sector = saveg_read32();\n    str->sector = &sectors[sector];\n\n    // int count;\n    str->count = saveg_read32();\n\n    // int maxlight;\n    str->maxlight = saveg_read32();\n\n    // int minlight;\n    str->minlight = saveg_read32();\n\n    // int maxtime;\n    str->maxtime = saveg_read32();\n\n    // int mintime;\n    str->mintime = saveg_read32();\n}\n\nstatic void saveg_write_lightflash_t(lightflash_t *str)\n{\n    // thinker_t thinker;\n    saveg_write_thinker_t(&str->thinker);\n\n    // sector_t* sector;\n    saveg_write32(str->sector - sectors);\n\n    // int count;\n    saveg_write32(str->count);\n\n    // int maxlight;\n    saveg_write32(str->maxlight);\n\n    // int minlight;\n    saveg_write32(str->minlight);\n\n    // int maxtime;\n    saveg_write32(str->maxtime);\n\n    // int mintime;\n    saveg_write32(str->mintime);\n}\n\n//\n// strobe_t\n//\n\nstatic void saveg_read_strobe_t(strobe_t *str)\n{\n    int sector;\n\n    // thinker_t thinker;\n    saveg_read_thinker_t(&str->thinker);\n\n    // sector_t* sector;\n    sector = saveg_read32();\n    str->sector = &sectors[sector];\n\n    // int count;\n    str->count = saveg_read32();\n\n    // int minlight;\n    str->minlight = saveg_read32();\n\n    // int maxlight;\n    str->maxlight = saveg_read32();\n\n    // int darktime;\n    str->darktime = saveg_read32();\n\n    // int brighttime;\n    str->brighttime = saveg_read32();\n}\n\nstatic void saveg_write_strobe_t(strobe_t *str)\n{\n    // thinker_t thinker;\n    saveg_write_thinker_t(&str->thinker);\n\n    // sector_t* sector;\n    saveg_write32(str->sector - sectors);\n\n    // int count;\n    saveg_write32(str->count);\n\n    // int minlight;\n    saveg_write32(str->minlight);\n\n    // int maxlight;\n    saveg_write32(str->maxlight);\n\n    // int darktime;\n    saveg_write32(str->darktime);\n\n    // int brighttime;\n    saveg_write32(str->brighttime);\n}\n\n//\n// glow_t\n//\n\nstatic void saveg_read_glow_t(glow_t *str)\n{\n    int sector;\n\n    // thinker_t thinker;\n    saveg_read_thinker_t(&str->thinker);\n\n    // sector_t* sector;\n    sector = saveg_read32();\n    str->sector = &sectors[sector];\n\n    // int minlight;\n    str->minlight = saveg_read32();\n\n    // int maxlight;\n    str->maxlight = saveg_read32();\n\n    // int direction;\n    str->direction = saveg_read32();\n}\n\nstatic void saveg_write_glow_t(glow_t *str)\n{\n    // thinker_t thinker;\n    saveg_write_thinker_t(&str->thinker);\n\n    // sector_t* sector;\n    saveg_write32(str->sector - sectors);\n\n    // int minlight;\n    saveg_write32(str->minlight);\n\n    // int maxlight;\n    saveg_write32(str->maxlight);\n\n    // int direction;\n    saveg_write32(str->direction);\n}\n\n//\n// Write the header for a savegame\n//\n\nvoid P_WriteSaveGameHeader(char *description)\n{\n    char name[VERSIONSIZE]; \n    int i; \n\t\n    for (i=0; description[i] != '\\0'; ++i)\n        saveg_write8(description[i]);\n    for (; i<SAVESTRINGSIZE; ++i)\n        saveg_write8(0);\n\n    memset(name, 0, sizeof(name));\n    M_snprintf(name, sizeof(name), \"version %i\", G_VanillaVersionCode());\n\n    for (i=0; i<VERSIONSIZE; ++i)\n        saveg_write8(name[i]);\n\t \n    saveg_write8(gameskill);\n    saveg_write8(gameepisode);\n    saveg_write8(gamemap);\n\n    for (i=0 ; i<MAXPLAYERS ; i++) \n        saveg_write8(playeringame[i]);\n\n    saveg_write8((leveltime >> 16) & 0xff);\n    saveg_write8((leveltime >> 8) & 0xff);\n    saveg_write8(leveltime & 0xff);\n}\n\n// \n// Read the header for a savegame\n//\n\nboolean P_ReadSaveGameHeader(void)\n{\n    int\t i; \n    byte a, b, c; \n    char vcheck[VERSIONSIZE]; \n    char read_vcheck[VERSIONSIZE];\n\t \n    // skip the description field \n\n    for (i=0; i<SAVESTRINGSIZE; ++i)\n        saveg_read8();\n    \n    for (i=0; i<VERSIONSIZE; ++i)\n        read_vcheck[i] = saveg_read8();\n\n    memset(vcheck, 0, sizeof(vcheck));\n    M_snprintf(vcheck, sizeof(vcheck), \"version %i\", G_VanillaVersionCode());\n    if (strcmp(read_vcheck, vcheck) != 0)\n\treturn false;\t\t\t\t// bad version \n\n    gameskill = saveg_read8();\n    gameepisode = saveg_read8();\n    gamemap = saveg_read8();\n\n    for (i=0 ; i<MAXPLAYERS ; i++) \n\tplayeringame[i] = saveg_read8();\n\n    // get the times \n    a = saveg_read8();\n    b = saveg_read8();\n    c = saveg_read8();\n    leveltime = (a<<16) + (b<<8) + c; \n\n    return true;\n}\n\n//\n// Read the end of file marker.  Returns true if read successfully.\n// \n\nboolean P_ReadSaveGameEOF(void)\n{\n    int value;\n\n    value = saveg_read8();\n\n    return value == SAVEGAME_EOF;\n}\n\n//\n// Write the end of file marker\n//\n\nvoid P_WriteSaveGameEOF(void)\n{\n    saveg_write8(SAVEGAME_EOF);\n}\n\n//\n// P_ArchivePlayers\n//\nvoid P_ArchivePlayers (void)\n{\n    int\t\ti;\n\t\t\n    for (i=0 ; i<MAXPLAYERS ; i++)\n    {\n\tif (!playeringame[i])\n\t    continue;\n\t\n\tsaveg_write_pad();\n\n        saveg_write_player_t(&players[i]);\n    }\n}\n\n\n\n//\n// P_UnArchivePlayers\n//\nvoid P_UnArchivePlayers (void)\n{\n    int\t\ti;\n\t\n    for (i=0 ; i<MAXPLAYERS ; i++)\n    {\n\tif (!playeringame[i])\n\t    continue;\n\t\n\tsaveg_read_pad();\n\n        saveg_read_player_t(&players[i]);\n\t\n\t// will be set when unarc thinker\n\tplayers[i].mo = NULL;\t\n\tplayers[i].message = NULL;\n\tplayers[i].attacker = NULL;\n    }\n}\n\n\n//\n// P_ArchiveWorld\n//\nvoid P_ArchiveWorld (void)\n{\n    int\t\t\ti;\n    int\t\t\tj;\n    sector_t*\t\tsec;\n    line_t*\t\tli;\n    side_t*\t\tsi;\n    \n    // do sectors\n    for (i=0, sec = sectors ; i<numsectors ; i++,sec++)\n    {\n\tsaveg_write16(sec->floorheight >> FRACBITS);\n\tsaveg_write16(sec->ceilingheight >> FRACBITS);\n\tsaveg_write16(sec->floorpic);\n\tsaveg_write16(sec->ceilingpic);\n\tsaveg_write16(sec->lightlevel);\n\tsaveg_write16(sec->special);\t\t// needed?\n\tsaveg_write16(sec->tag);\t\t// needed?\n    }\n\n    \n    // do lines\n    for (i=0, li = lines ; i<numlines ; i++,li++)\n    {\n\tsaveg_write16(li->flags);\n\tsaveg_write16(li->special);\n\tsaveg_write16(li->tag);\n\tfor (j=0 ; j<2 ; j++)\n\t{\n\t    if (li->sidenum[j] == -1)\n\t\tcontinue;\n\t    \n\t    si = &sides[li->sidenum[j]];\n\n\t    saveg_write16(si->textureoffset >> FRACBITS);\n\t    saveg_write16(si->rowoffset >> FRACBITS);\n\t    saveg_write16(si->toptexture);\n\t    saveg_write16(si->bottomtexture);\n\t    saveg_write16(si->midtexture);\t\n\t}\n    }\n}\n\n\n\n//\n// P_UnArchiveWorld\n//\nvoid P_UnArchiveWorld (void)\n{\n    int\t\t\ti;\n    int\t\t\tj;\n    sector_t*\t\tsec;\n    line_t*\t\tli;\n    side_t*\t\tsi;\n    \n    // do sectors\n    for (i=0, sec = sectors ; i<numsectors ; i++,sec++)\n    {\n\tsec->floorheight = saveg_read16() << FRACBITS;\n\tsec->ceilingheight = saveg_read16() << FRACBITS;\n\tsec->floorpic = saveg_read16();\n\tsec->ceilingpic = saveg_read16();\n\tsec->lightlevel = saveg_read16();\n\tsec->special = saveg_read16();\t\t// needed?\n\tsec->tag = saveg_read16();\t\t// needed?\n\tsec->specialdata = 0;\n\tsec->soundtarget = 0;\n    }\n    \n    // do lines\n    for (i=0, li = lines ; i<numlines ; i++,li++)\n    {\n\tli->flags = saveg_read16();\n\tli->special = saveg_read16();\n\tli->tag = saveg_read16();\n\tfor (j=0 ; j<2 ; j++)\n\t{\n\t    if (li->sidenum[j] == -1)\n\t\tcontinue;\n\t    si = &sides[li->sidenum[j]];\n\t    si->textureoffset = saveg_read16() << FRACBITS;\n\t    si->rowoffset = saveg_read16() << FRACBITS;\n\t    si->toptexture = saveg_read16();\n\t    si->bottomtexture = saveg_read16();\n\t    si->midtexture = saveg_read16();\n\t}\n    }\n}\n\n\n\n\n\n//\n// Thinkers\n//\ntypedef enum\n{\n    tc_end,\n    tc_mobj\n\n} thinkerclass_t;\n\n\n//\n// P_ArchiveThinkers\n//\nvoid P_ArchiveThinkers (void)\n{\n    thinker_t*\t\tth;\n\n    // save off the current thinkers\n    for (th = thinkercap.next ; th != &thinkercap ; th=th->next)\n    {\n\tif (th->function.acp1 == (actionf_p1)P_MobjThinker)\n\t{\n            saveg_write8(tc_mobj);\n\t    saveg_write_pad();\n            saveg_write_mobj_t((mobj_t *) th);\n\n\t    continue;\n\t}\n\t\t\n\t// I_Error (\"P_ArchiveThinkers: Unknown thinker function\");\n    }\n\n    // add a terminating marker\n    saveg_write8(tc_end);\n}\n\n\n\n//\n// P_UnArchiveThinkers\n//\nvoid P_UnArchiveThinkers (void)\n{\n    byte\t\ttclass;\n    thinker_t*\t\tcurrentthinker;\n    thinker_t*\t\tnext;\n    mobj_t*\t\tmobj;\n    \n    // remove all the current thinkers\n    currentthinker = thinkercap.next;\n    while (currentthinker != &thinkercap)\n    {\n\tnext = currentthinker->next;\n\t\n\tif (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker)\n\t    P_RemoveMobj ((mobj_t *)currentthinker);\n\telse\n\t    Z_Free (currentthinker);\n\n\tcurrentthinker = next;\n    }\n    P_InitThinkers ();\n    \n    // read in saved thinkers\n    while (1)\n    {\n\ttclass = saveg_read8();\n\tswitch (tclass)\n\t{\n\t  case tc_end:\n\t    return; \t// end of list\n\t\t\t\n\t  case tc_mobj:\n\t    saveg_read_pad();\n\t    mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL);\n            saveg_read_mobj_t(mobj);\n\n\t    mobj->target = NULL;\n            mobj->tracer = NULL;\n\t    P_SetThingPosition (mobj);\n\t    mobj->info = &mobjinfo[mobj->type];\n\t    mobj->floorz = mobj->subsector->sector->floorheight;\n\t    mobj->ceilingz = mobj->subsector->sector->ceilingheight;\n\t    mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker;\n\t    P_AddThinker (&mobj->thinker);\n\t    break;\n\n\t  default:\n\t    I_Error (\"Unknown tclass %i in savegame\",tclass);\n\t}\n\t\n    }\n\n}\n\n\n//\n// P_ArchiveSpecials\n//\nenum\n{\n    tc_ceiling,\n    tc_door,\n    tc_floor,\n    tc_plat,\n    tc_flash,\n    tc_strobe,\n    tc_glow,\n    tc_endspecials\n\n} specials_e;\t\n\n\n\n//\n// Things to handle:\n//\n// T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list\n// T_VerticalDoor, (vldoor_t: sector_t * swizzle),\n// T_MoveFloor, (floormove_t: sector_t * swizzle),\n// T_LightFlash, (lightflash_t: sector_t * swizzle),\n// T_StrobeFlash, (strobe_t: sector_t *),\n// T_Glow, (glow_t: sector_t *),\n// T_PlatRaise, (plat_t: sector_t *), - active list\n//\nvoid P_ArchiveSpecials (void)\n{\n    thinker_t*\t\tth;\n    int\t\t\ti;\n\t\n    // save off the current thinkers\n    for (th = thinkercap.next ; th != &thinkercap ; th=th->next)\n    {\n\tif (th->function.acv == (actionf_v)NULL)\n\t{\n\t    for (i = 0; i < MAXCEILINGS;i++)\n\t\tif (activeceilings[i] == (ceiling_t *)th)\n\t\t    break;\n\t    \n\t    if (i<MAXCEILINGS)\n\t    {\n                saveg_write8(tc_ceiling);\n\t\tsaveg_write_pad();\n                saveg_write_ceiling_t((ceiling_t *) th);\n\t    }\n\t    continue;\n\t}\n\t\t\t\n\tif (th->function.acp1 == (actionf_p1)T_MoveCeiling)\n\t{\n            saveg_write8(tc_ceiling);\n\t    saveg_write_pad();\n            saveg_write_ceiling_t((ceiling_t *) th);\n\t    continue;\n\t}\n\t\t\t\n\tif (th->function.acp1 == (actionf_p1)T_VerticalDoor)\n\t{\n            saveg_write8(tc_door);\n\t    saveg_write_pad();\n            saveg_write_vldoor_t((vldoor_t *) th);\n\t    continue;\n\t}\n\t\t\t\n\tif (th->function.acp1 == (actionf_p1)T_MoveFloor)\n\t{\n            saveg_write8(tc_floor);\n\t    saveg_write_pad();\n            saveg_write_floormove_t((floormove_t *) th);\n\t    continue;\n\t}\n\t\t\t\n\tif (th->function.acp1 == (actionf_p1)T_PlatRaise)\n\t{\n            saveg_write8(tc_plat);\n\t    saveg_write_pad();\n            saveg_write_plat_t((plat_t *) th);\n\t    continue;\n\t}\n\t\t\t\n\tif (th->function.acp1 == (actionf_p1)T_LightFlash)\n\t{\n            saveg_write8(tc_flash);\n\t    saveg_write_pad();\n            saveg_write_lightflash_t((lightflash_t *) th);\n\t    continue;\n\t}\n\t\t\t\n\tif (th->function.acp1 == (actionf_p1)T_StrobeFlash)\n\t{\n            saveg_write8(tc_strobe);\n\t    saveg_write_pad();\n            saveg_write_strobe_t((strobe_t *) th);\n\t    continue;\n\t}\n\t\t\t\n\tif (th->function.acp1 == (actionf_p1)T_Glow)\n\t{\n            saveg_write8(tc_glow);\n\t    saveg_write_pad();\n            saveg_write_glow_t((glow_t *) th);\n\t    continue;\n\t}\n    }\n\t\n    // add a terminating marker\n    saveg_write8(tc_endspecials);\n\n}\n\n\n//\n// P_UnArchiveSpecials\n//\nvoid P_UnArchiveSpecials (void)\n{\n    byte\t\ttclass;\n    ceiling_t*\t\tceiling;\n    vldoor_t*\t\tdoor;\n    floormove_t*\tfloor;\n    plat_t*\t\tplat;\n    lightflash_t*\tflash;\n    strobe_t*\t\tstrobe;\n    glow_t*\t\tglow;\n\t\n\t\n    // read in saved thinkers\n    while (1)\n    {\n\ttclass = saveg_read8();\n\n\tswitch (tclass)\n\t{\n\t  case tc_endspecials:\n\t    return;\t// end of list\n\t\t\t\n\t  case tc_ceiling:\n\t    saveg_read_pad();\n\t    ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVEL, NULL);\n            saveg_read_ceiling_t(ceiling);\n\t    ceiling->sector->specialdata = ceiling;\n\n\t    if (ceiling->thinker.function.acp1)\n\t\tceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling;\n\n\t    P_AddThinker (&ceiling->thinker);\n\t    P_AddActiveCeiling(ceiling);\n\t    break;\n\t\t\t\t\n\t  case tc_door:\n\t    saveg_read_pad();\n\t    door = Z_Malloc (sizeof(*door), PU_LEVEL, NULL);\n            saveg_read_vldoor_t(door);\n\t    door->sector->specialdata = door;\n\t    door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor;\n\t    P_AddThinker (&door->thinker);\n\t    break;\n\t\t\t\t\n\t  case tc_floor:\n\t    saveg_read_pad();\n\t    floor = Z_Malloc (sizeof(*floor), PU_LEVEL, NULL);\n            saveg_read_floormove_t(floor);\n\t    floor->sector->specialdata = floor;\n\t    floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor;\n\t    P_AddThinker (&floor->thinker);\n\t    break;\n\t\t\t\t\n\t  case tc_plat:\n\t    saveg_read_pad();\n\t    plat = Z_Malloc (sizeof(*plat), PU_LEVEL, NULL);\n            saveg_read_plat_t(plat);\n\t    plat->sector->specialdata = plat;\n\n\t    if (plat->thinker.function.acp1)\n\t\tplat->thinker.function.acp1 = (actionf_p1)T_PlatRaise;\n\n\t    P_AddThinker (&plat->thinker);\n\t    P_AddActivePlat(plat);\n\t    break;\n\t\t\t\t\n\t  case tc_flash:\n\t    saveg_read_pad();\n\t    flash = Z_Malloc (sizeof(*flash), PU_LEVEL, NULL);\n            saveg_read_lightflash_t(flash);\n\t    flash->thinker.function.acp1 = (actionf_p1)T_LightFlash;\n\t    P_AddThinker (&flash->thinker);\n\t    break;\n\t\t\t\t\n\t  case tc_strobe:\n\t    saveg_read_pad();\n\t    strobe = Z_Malloc (sizeof(*strobe), PU_LEVEL, NULL);\n            saveg_read_strobe_t(strobe);\n\t    strobe->thinker.function.acp1 = (actionf_p1)T_StrobeFlash;\n\t    P_AddThinker (&strobe->thinker);\n\t    break;\n\t\t\t\t\n\t  case tc_glow:\n\t    saveg_read_pad();\n\t    glow = Z_Malloc (sizeof(*glow), PU_LEVEL, NULL);\n            saveg_read_glow_t(glow);\n\t    glow->thinker.function.acp1 = (actionf_p1)T_Glow;\n\t    P_AddThinker (&glow->thinker);\n\t    break;\n\t\t\t\t\n\t  default:\n\t    I_Error (\"P_UnarchiveSpecials:Unknown tclass %i \"\n\t\t     \"in savegame\",tclass);\n\t}\n\t\n    }\n\n}\n\n"
  },
  {
    "path": "fbdoom/p_saveg.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tSavegame I/O, archiving, persistence.\n//\n\n\n#ifndef __P_SAVEG__\n#define __P_SAVEG__\n\n#include <stdio.h>\n\n// maximum size of a savegame description\n\n#define SAVESTRINGSIZE 24\n\n// temporary filename to use while saving.\n\nchar *P_TempSaveGameFile(void);\n\n// filename to use for a savegame slot\n\nchar *P_SaveGameFile(int slot);\n\n// Savegame file header read/write functions\n\nboolean P_ReadSaveGameHeader(void);\nvoid P_WriteSaveGameHeader(char *description);\n\n// Savegame end-of-file read/write functions\n\nboolean P_ReadSaveGameEOF(void);\nvoid P_WriteSaveGameEOF(void);\n\n// Persistent storage/archiving.\n// These are the load / save game routines.\nvoid P_ArchivePlayers (void);\nvoid P_UnArchivePlayers (void);\nvoid P_ArchiveWorld (void);\nvoid P_UnArchiveWorld (void);\nvoid P_ArchiveThinkers (void);\nvoid P_UnArchiveThinkers (void);\nvoid P_ArchiveSpecials (void);\nvoid P_UnArchiveSpecials (void);\n\nextern FILE *save_stream;\nextern boolean savegame_error;\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/p_setup.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tDo all the WAD I/O, get map description,\n//\tset up initial state and misc. LUTs.\n//\n\n\n\n#include <math.h>\n\n#include \"z_zone.h\"\n\n#include \"deh_main.h\"\n#include \"i_swap.h\"\n#include \"m_argv.h\"\n#include \"m_bbox.h\"\n\n#include \"g_game.h\"\n\n#include \"i_system.h\"\n#include \"w_wad.h\"\n\n#include \"doomdef.h\"\n#include \"p_local.h\"\n\n#include \"s_sound.h\"\n\n#include \"doomstat.h\"\n\n\nvoid\tP_SpawnMapThing (mapthing_t*\tmthing);\n\n\n//\n// MAP related Lookup tables.\n// Store VERTEXES, LINEDEFS, SIDEDEFS, etc.\n//\nint\t\tnumvertexes;\nvertex_t*\tvertexes;\n\nint\t\tnumsegs;\nseg_t*\t\tsegs;\n\nint\t\tnumsectors;\nsector_t*\tsectors;\n\nint\t\tnumsubsectors;\nsubsector_t*\tsubsectors;\n\nint\t\tnumnodes;\nnode_t*\t\tnodes;\n\nint\t\tnumlines;\nline_t*\t\tlines;\n\nint\t\tnumsides;\nside_t*\t\tsides;\n\nstatic int      totallines;\n\n// BLOCKMAP\n// Created from axis aligned bounding box\n// of the map, a rectangular array of\n// blocks of size ...\n// Used to speed up collision detection\n// by spatial subdivision in 2D.\n//\n// Blockmap size.\nint\t\tbmapwidth;\nint\t\tbmapheight;\t// size in mapblocks\nshort*\t\tblockmap;\t// int for larger maps\n// offsets in blockmap are from here\nshort*\t\tblockmaplump;\t\t\n// origin of block map\nfixed_t\t\tbmaporgx;\nfixed_t\t\tbmaporgy;\n// for thing chains\nmobj_t**\tblocklinks;\t\t\n\n\n// REJECT\n// For fast sight rejection.\n// Speeds up enemy AI by skipping detailed\n//  LineOf Sight calculation.\n// Without special effect, this could be\n//  used as a PVS lookup as well.\n//\nbyte*\t\trejectmatrix;\n\n\n// Maintain single and multi player starting spots.\n#define MAX_DEATHMATCH_STARTS\t10\n\nmapthing_t\tdeathmatchstarts[MAX_DEATHMATCH_STARTS];\nmapthing_t*\tdeathmatch_p;\nmapthing_t\tplayerstarts[MAXPLAYERS];\n\n\n\n\n\n//\n// P_LoadVertexes\n//\nvoid P_LoadVertexes (int lump)\n{\n    byte*\t\tdata;\n    int\t\t\ti;\n    mapvertex_t*\tml;\n    vertex_t*\t\tli;\n\n    // Determine number of lumps:\n    //  total lump length / vertex record length.\n    numvertexes = W_LumpLength (lump) / sizeof(mapvertex_t);\n\n    // Allocate zone memory for buffer.\n    vertexes = Z_Malloc (numvertexes*sizeof(vertex_t),PU_LEVEL,0);\t\n\n    // Load data into cache.\n    data = W_CacheLumpNum (lump, PU_STATIC);\n\t\n    ml = (mapvertex_t *)data;\n    li = vertexes;\n\n    // Copy and convert vertex coordinates,\n    // internal representation as fixed.\n    for (i=0 ; i<numvertexes ; i++, li++, ml++)\n    {\n\tli->x = SHORT(ml->x)<<FRACBITS;\n\tli->y = SHORT(ml->y)<<FRACBITS;\n    }\n\n    // Free buffer memory.\n    W_ReleaseLumpNum(lump);\n}\n\n//\n// GetSectorAtNullAddress\n//\nsector_t* GetSectorAtNullAddress(void)\n{\n    static boolean null_sector_is_initialized = false;\n    static sector_t null_sector;\n\n    if (!null_sector_is_initialized)\n    {\n        memset(&null_sector, 0, sizeof(null_sector));\n        I_GetMemoryValue(0, &null_sector.floorheight, 4);\n        I_GetMemoryValue(4, &null_sector.ceilingheight, 4);\n        null_sector_is_initialized = true;\n    }\n\n    return &null_sector;\n}\n\n//\n// P_LoadSegs\n//\nvoid P_LoadSegs (int lump)\n{\n    byte*\t\tdata;\n    int\t\t\ti;\n    mapseg_t*\t\tml;\n    seg_t*\t\tli;\n    line_t*\t\tldef;\n    int\t\t\tlinedef;\n    int\t\t\tside;\n    int                 sidenum;\n\t\n    numsegs = W_LumpLength (lump) / sizeof(mapseg_t);\n    segs = Z_Malloc (numsegs*sizeof(seg_t),PU_LEVEL,0);\t\n    memset (segs, 0, numsegs*sizeof(seg_t));\n    data = W_CacheLumpNum (lump,PU_STATIC);\n\t\n    ml = (mapseg_t *)data;\n    li = segs;\n    for (i=0 ; i<numsegs ; i++, li++, ml++)\n    {\n\tli->v1 = &vertexes[SHORT(ml->v1)];\n\tli->v2 = &vertexes[SHORT(ml->v2)];\n\n\tli->angle = (SHORT(ml->angle))<<16;\n\tli->offset = (SHORT(ml->offset))<<16;\n\tlinedef = SHORT(ml->linedef);\n\tldef = &lines[linedef];\n\tli->linedef = ldef;\n\tside = SHORT(ml->side);\n\tli->sidedef = &sides[ldef->sidenum[side]];\n\tli->frontsector = sides[ldef->sidenum[side]].sector;\n\n        if (ldef-> flags & ML_TWOSIDED)\n        {\n            sidenum = ldef->sidenum[side ^ 1];\n\n            // If the sidenum is out of range, this may be a \"glass hack\"\n            // impassible window.  Point at side #0 (this may not be\n            // the correct Vanilla behavior; however, it seems to work for\n            // OTTAWAU.WAD, which is the one place I've seen this trick\n            // used).\n\n            if (sidenum < 0 || sidenum >= numsides)\n            {\n                li->backsector = GetSectorAtNullAddress();\n            }\n            else\n            {\n                li->backsector = sides[sidenum].sector;\n            }\n        }\n        else\n        {\n\t    li->backsector = 0;\n        }\n    }\n\t\n    W_ReleaseLumpNum(lump);\n}\n\n\n//\n// P_LoadSubsectors\n//\nvoid P_LoadSubsectors (int lump)\n{\n    byte*\t\tdata;\n    int\t\t\ti;\n    mapsubsector_t*\tms;\n    subsector_t*\tss;\n\t\n    numsubsectors = W_LumpLength (lump) / sizeof(mapsubsector_t);\n    subsectors = Z_Malloc (numsubsectors*sizeof(subsector_t),PU_LEVEL,0);\t\n    data = W_CacheLumpNum (lump,PU_STATIC);\n\t\n    ms = (mapsubsector_t *)data;\n    memset (subsectors,0, numsubsectors*sizeof(subsector_t));\n    ss = subsectors;\n    \n    for (i=0 ; i<numsubsectors ; i++, ss++, ms++)\n    {\n\tss->numlines = SHORT(ms->numsegs);\n\tss->firstline = SHORT(ms->firstseg);\n    }\n\t\n    W_ReleaseLumpNum(lump);\n}\n\n\n\n//\n// P_LoadSectors\n//\nvoid P_LoadSectors (int lump)\n{\n    byte*\t\tdata;\n    int\t\t\ti;\n    mapsector_t*\tms;\n    sector_t*\t\tss;\n\t\n    numsectors = W_LumpLength (lump) / sizeof(mapsector_t);\n    sectors = Z_Malloc (numsectors*sizeof(sector_t),PU_LEVEL,0);\t\n    memset (sectors, 0, numsectors*sizeof(sector_t));\n    data = W_CacheLumpNum (lump,PU_STATIC);\n\t\n    ms = (mapsector_t *)data;\n    ss = sectors;\n    for (i=0 ; i<numsectors ; i++, ss++, ms++)\n    {\n\tss->floorheight = SHORT(ms->floorheight)<<FRACBITS;\n\tss->ceilingheight = SHORT(ms->ceilingheight)<<FRACBITS;\n\tss->floorpic = R_FlatNumForName(ms->floorpic);\n\tss->ceilingpic = R_FlatNumForName(ms->ceilingpic);\n\tss->lightlevel = SHORT(ms->lightlevel);\n\tss->special = SHORT(ms->special);\n\tss->tag = SHORT(ms->tag);\n\tss->thinglist = NULL;\n    }\n\t\n    W_ReleaseLumpNum(lump);\n}\n\n\n//\n// P_LoadNodes\n//\nvoid P_LoadNodes (int lump)\n{\n    byte*\tdata;\n    int\t\ti;\n    int\t\tj;\n    int\t\tk;\n    mapnode_t*\tmn;\n    node_t*\tno;\n\t\n    numnodes = W_LumpLength (lump) / sizeof(mapnode_t);\n    nodes = Z_Malloc (numnodes*sizeof(node_t),PU_LEVEL,0);\t\n    data = W_CacheLumpNum (lump,PU_STATIC);\n\t\n    mn = (mapnode_t *)data;\n    no = nodes;\n    \n    for (i=0 ; i<numnodes ; i++, no++, mn++)\n    {\n\tno->x = SHORT(mn->x)<<FRACBITS;\n\tno->y = SHORT(mn->y)<<FRACBITS;\n\tno->dx = SHORT(mn->dx)<<FRACBITS;\n\tno->dy = SHORT(mn->dy)<<FRACBITS;\n\tfor (j=0 ; j<2 ; j++)\n\t{\n\t    no->children[j] = SHORT(mn->children[j]);\n\t    for (k=0 ; k<4 ; k++)\n\t\tno->bbox[j][k] = SHORT(mn->bbox[j][k])<<FRACBITS;\n\t}\n    }\n\t\n    W_ReleaseLumpNum(lump);\n}\n\n\n//\n// P_LoadThings\n//\nvoid P_LoadThings (int lump)\n{\n    byte               *data;\n    int\t\t\ti;\n    mapthing_t         *mt;\n    mapthing_t          spawnthing;\n    int\t\t\tnumthings;\n    boolean\t\tspawn;\n\n    data = W_CacheLumpNum (lump,PU_STATIC);\n    numthings = W_LumpLength (lump) / sizeof(mapthing_t);\n\t\n    mt = (mapthing_t *)data;\n    for (i=0 ; i<numthings ; i++, mt++)\n    {\n\tspawn = true;\n\n\t// Do not spawn cool, new monsters if !commercial\n\tif (gamemode != commercial)\n\t{\n\t    switch (SHORT(mt->type))\n\t    {\n\t      case 68:\t// Arachnotron\n\t      case 64:\t// Archvile\n\t      case 88:\t// Boss Brain\n\t      case 89:\t// Boss Shooter\n\t      case 69:\t// Hell Knight\n\t      case 67:\t// Mancubus\n\t      case 71:\t// Pain Elemental\n\t      case 65:\t// Former Human Commando\n\t      case 66:\t// Revenant\n\t      case 84:\t// Wolf SS\n\t\tspawn = false;\n\t\tbreak;\n\t    }\n\t}\n\tif (spawn == false)\n\t    break;\n\n\t// Do spawn all other stuff. \n\tspawnthing.x = SHORT(mt->x);\n\tspawnthing.y = SHORT(mt->y);\n\tspawnthing.angle = SHORT(mt->angle);\n\tspawnthing.type = SHORT(mt->type);\n\tspawnthing.options = SHORT(mt->options);\n\t\n\tP_SpawnMapThing(&spawnthing);\n    }\n\n    W_ReleaseLumpNum(lump);\n}\n\n\n//\n// P_LoadLineDefs\n// Also counts secret lines for intermissions.\n//\nvoid P_LoadLineDefs (int lump)\n{\n    byte*\t\tdata;\n    int\t\t\ti;\n    maplinedef_t*\tmld;\n    line_t*\t\tld;\n    vertex_t*\t\tv1;\n    vertex_t*\t\tv2;\n\t\n    numlines = W_LumpLength (lump) / sizeof(maplinedef_t);\n    lines = Z_Malloc (numlines*sizeof(line_t),PU_LEVEL,0);\t\n    memset (lines, 0, numlines*sizeof(line_t));\n    data = W_CacheLumpNum (lump,PU_STATIC);\n\t\n    mld = (maplinedef_t *)data;\n    ld = lines;\n    for (i=0 ; i<numlines ; i++, mld++, ld++)\n    {\n\tld->flags = SHORT(mld->flags);\n\tld->special = SHORT(mld->special);\n\tld->tag = SHORT(mld->tag);\n\tv1 = ld->v1 = &vertexes[SHORT(mld->v1)];\n\tv2 = ld->v2 = &vertexes[SHORT(mld->v2)];\n\tld->dx = v2->x - v1->x;\n\tld->dy = v2->y - v1->y;\n\t\n\tif (!ld->dx)\n\t    ld->slopetype = ST_VERTICAL;\n\telse if (!ld->dy)\n\t    ld->slopetype = ST_HORIZONTAL;\n\telse\n\t{\n\t    if (FixedDiv (ld->dy , ld->dx) > 0)\n\t\tld->slopetype = ST_POSITIVE;\n\t    else\n\t\tld->slopetype = ST_NEGATIVE;\n\t}\n\t\t\n\tif (v1->x < v2->x)\n\t{\n\t    ld->bbox[BOXLEFT] = v1->x;\n\t    ld->bbox[BOXRIGHT] = v2->x;\n\t}\n\telse\n\t{\n\t    ld->bbox[BOXLEFT] = v2->x;\n\t    ld->bbox[BOXRIGHT] = v1->x;\n\t}\n\n\tif (v1->y < v2->y)\n\t{\n\t    ld->bbox[BOXBOTTOM] = v1->y;\n\t    ld->bbox[BOXTOP] = v2->y;\n\t}\n\telse\n\t{\n\t    ld->bbox[BOXBOTTOM] = v2->y;\n\t    ld->bbox[BOXTOP] = v1->y;\n\t}\n\n\tld->sidenum[0] = SHORT(mld->sidenum[0]);\n\tld->sidenum[1] = SHORT(mld->sidenum[1]);\n\n\tif (ld->sidenum[0] != -1)\n\t    ld->frontsector = sides[ld->sidenum[0]].sector;\n\telse\n\t    ld->frontsector = 0;\n\n\tif (ld->sidenum[1] != -1)\n\t    ld->backsector = sides[ld->sidenum[1]].sector;\n\telse\n\t    ld->backsector = 0;\n    }\n\n    W_ReleaseLumpNum(lump);\n}\n\n\n//\n// P_LoadSideDefs\n//\nvoid P_LoadSideDefs (int lump)\n{\n    byte*\t\tdata;\n    int\t\t\ti;\n    mapsidedef_t*\tmsd;\n    side_t*\t\tsd;\n\t\n    numsides = W_LumpLength (lump) / sizeof(mapsidedef_t);\n    sides = Z_Malloc (numsides*sizeof(side_t),PU_LEVEL,0);\t\n    memset (sides, 0, numsides*sizeof(side_t));\n    data = W_CacheLumpNum (lump,PU_STATIC);\n\t\n    msd = (mapsidedef_t *)data;\n    sd = sides;\n    for (i=0 ; i<numsides ; i++, msd++, sd++)\n    {\n\tsd->textureoffset = SHORT(msd->textureoffset)<<FRACBITS;\n\tsd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS;\n\tsd->toptexture = R_TextureNumForName(msd->toptexture);\n\tsd->bottomtexture = R_TextureNumForName(msd->bottomtexture);\n\tsd->midtexture = R_TextureNumForName(msd->midtexture);\n\tsd->sector = &sectors[SHORT(msd->sector)];\n    }\n\n    W_ReleaseLumpNum(lump);\n}\n\n\n//\n// P_LoadBlockMap\n//\nvoid P_LoadBlockMap (int lump)\n{\n    int i;\n    int count;\n    int lumplen;\n\n    lumplen = W_LumpLength(lump);\n    count = lumplen / 2;\n\t\n    blockmaplump = Z_Malloc(lumplen, PU_LEVEL, NULL);\n    W_ReadLump(lump, blockmaplump);\n    blockmap = blockmaplump + 4;\n\n    // Swap all short integers to native byte ordering.\n  \n    for (i=0; i<count; i++)\n    {\n\tblockmaplump[i] = SHORT(blockmaplump[i]);\n    }\n\t\t\n    // Read the header\n\n    bmaporgx = blockmaplump[0]<<FRACBITS;\n    bmaporgy = blockmaplump[1]<<FRACBITS;\n    bmapwidth = blockmaplump[2];\n    bmapheight = blockmaplump[3];\n\t\n    // Clear out mobj chains\n\n    count = sizeof(*blocklinks) * bmapwidth * bmapheight;\n    blocklinks = Z_Malloc(count, PU_LEVEL, 0);\n    memset(blocklinks, 0, count);\n}\n\n\n\n//\n// P_GroupLines\n// Builds sector line lists and subsector sector numbers.\n// Finds block bounding boxes for sectors.\n//\nvoid P_GroupLines (void)\n{\n    line_t**\t\tlinebuffer;\n    int\t\t\ti;\n    int\t\t\tj;\n    line_t*\t\tli;\n    sector_t*\t\tsector;\n    subsector_t*\tss;\n    seg_t*\t\tseg;\n    fixed_t\t\tbbox[4];\n    int\t\t\tblock;\n\t\n    // look up sector number for each subsector\n    ss = subsectors;\n    for (i=0 ; i<numsubsectors ; i++, ss++)\n    {\n\tseg = &segs[ss->firstline];\n\tss->sector = seg->sidedef->sector;\n    }\n\n    // count number of lines in each sector\n    li = lines;\n    totallines = 0;\n    for (i=0 ; i<numlines ; i++, li++)\n    {\n\ttotallines++;\n\tli->frontsector->linecount++;\n\n\tif (li->backsector && li->backsector != li->frontsector)\n\t{\n\t    li->backsector->linecount++;\n\t    totallines++;\n\t}\n    }\n\n    // build line tables for each sector\t\n    linebuffer = Z_Malloc (totallines*sizeof(line_t *), PU_LEVEL, 0);\n\n    for (i=0; i<numsectors; ++i)\n    {\n        // Assign the line buffer for this sector\n\n        sectors[i].lines = linebuffer;\n        linebuffer += sectors[i].linecount;\n\n        // Reset linecount to zero so in the next stage we can count\n        // lines into the list.\n\n        sectors[i].linecount = 0;\n    }\n\n    // Assign lines to sectors\n\n    for (i=0; i<numlines; ++i)\n    { \n        li = &lines[i];\n\n        if (li->frontsector != NULL)\n        {\n            sector = li->frontsector;\n\n            sector->lines[sector->linecount] = li;\n            ++sector->linecount;\n        }\n\n        if (li->backsector != NULL && li->frontsector != li->backsector)\n        {\n            sector = li->backsector;\n\n            sector->lines[sector->linecount] = li;\n            ++sector->linecount;\n        }\n    }\n    \n    // Generate bounding boxes for sectors\n\t\n    sector = sectors;\n    for (i=0 ; i<numsectors ; i++, sector++)\n    {\n\tM_ClearBox (bbox);\n\n\tfor (j=0 ; j<sector->linecount; j++)\n\t{\n            li = sector->lines[j];\n\n            M_AddToBox (bbox, li->v1->x, li->v1->y);\n            M_AddToBox (bbox, li->v2->x, li->v2->y);\n\t}\n\n\t// set the degenmobj_t to the middle of the bounding box\n\tsector->soundorg.x = (bbox[BOXRIGHT]+bbox[BOXLEFT])/2;\n\tsector->soundorg.y = (bbox[BOXTOP]+bbox[BOXBOTTOM])/2;\n\t\t\n\t// adjust bounding box to map blocks\n\tblock = (bbox[BOXTOP]-bmaporgy+MAXRADIUS)>>MAPBLOCKSHIFT;\n\tblock = block >= bmapheight ? bmapheight-1 : block;\n\tsector->blockbox[BOXTOP]=block;\n\n\tblock = (bbox[BOXBOTTOM]-bmaporgy-MAXRADIUS)>>MAPBLOCKSHIFT;\n\tblock = block < 0 ? 0 : block;\n\tsector->blockbox[BOXBOTTOM]=block;\n\n\tblock = (bbox[BOXRIGHT]-bmaporgx+MAXRADIUS)>>MAPBLOCKSHIFT;\n\tblock = block >= bmapwidth ? bmapwidth-1 : block;\n\tsector->blockbox[BOXRIGHT]=block;\n\n\tblock = (bbox[BOXLEFT]-bmaporgx-MAXRADIUS)>>MAPBLOCKSHIFT;\n\tblock = block < 0 ? 0 : block;\n\tsector->blockbox[BOXLEFT]=block;\n    }\n\t\n}\n\n// Pad the REJECT lump with extra data when the lump is too small,\n// to simulate a REJECT buffer overflow in Vanilla Doom.\n\nstatic void PadRejectArray(byte *array, unsigned int len)\n{\n    unsigned int i;\n    unsigned int byte_num;\n    byte *dest;\n    unsigned int padvalue;\n\n    // Values to pad the REJECT array with:\n\n    unsigned int rejectpad[4] =\n    {\n        ((totallines * 4 + 3) & ~3) + 24,     // Size\n        0,                                    // Part of z_zone block header\n        50,                                   // PU_LEVEL\n        0x1d4a11                              // DOOM_CONST_ZONEID\n    };\n\n    // Copy values from rejectpad into the destination array.\n\n    dest = array;\n\n    for (i=0; i<len && i<sizeof(rejectpad); ++i)\n    {\n        byte_num = i % 4;\n        *dest = (rejectpad[i / 4] >> (byte_num * 8)) & 0xff;\n        ++dest;\n    }\n\n    // We only have a limited pad size.  Print a warning if the\n    // REJECT lump is too small.\n\n    if (len > sizeof(rejectpad))\n    {\n        fprintf(stderr, \"PadRejectArray: REJECT lump too short to pad! (%i > %i)\\n\",\n                        len, (int) sizeof(rejectpad));\n\n        // Pad remaining space with 0 (or 0xff, if specified on command line).\n\n        if (M_CheckParm(\"-reject_pad_with_ff\"))\n        {\n            padvalue = 0xff;\n        }\n        else\n        {\n            padvalue = 0xf00;\n        }\n\n        memset(array + sizeof(rejectpad), padvalue, len - sizeof(rejectpad));\n    }\n}\n\nstatic void P_LoadReject(int lumpnum)\n{\n    int minlength;\n    int lumplen;\n\n    // Calculate the size that the REJECT lump *should* be.\n\n    minlength = (numsectors * numsectors + 7) / 8;\n\n    // If the lump meets the minimum length, it can be loaded directly.\n    // Otherwise, we need to allocate a buffer of the correct size\n    // and pad it with appropriate data.\n\n    lumplen = W_LumpLength(lumpnum);\n\n    if (lumplen >= minlength)\n    {\n        rejectmatrix = W_CacheLumpNum(lumpnum, PU_LEVEL);\n    }\n    else\n    {\n        rejectmatrix = Z_Malloc(minlength, PU_LEVEL, &rejectmatrix);\n        W_ReadLump(lumpnum, rejectmatrix);\n\n        PadRejectArray(rejectmatrix + lumplen, minlength - lumplen);\n    }\n}\n\n//\n// P_SetupLevel\n//\nvoid\nP_SetupLevel\n( int\t\tepisode,\n  int\t\tmap,\n  int\t\tplayermask,\n  skill_t\tskill)\n{\n    int\t\ti;\n    char\tlumpname[9];\n    int\t\tlumpnum;\n\t\n    totalkills = totalitems = totalsecret = wminfo.maxfrags = 0;\n    wminfo.partime = 180;\n    for (i=0 ; i<MAXPLAYERS ; i++)\n    {\n\tplayers[i].killcount = players[i].secretcount \n\t    = players[i].itemcount = 0;\n    }\n\n    // Initial height of PointOfView\n    // will be set by player think.\n    players[consoleplayer].viewz = 1; \n\n    // Make sure all sounds are stopped before Z_FreeTags.\n    S_Start ();\t\t\t\n\n    Z_FreeTags (PU_LEVEL, PU_PURGELEVEL-1);\n\n    // UNUSED W_Profile ();\n    P_InitThinkers ();\n\t   \n    // find map name\n    if ( gamemode == commercial)\n    {\n\tif (map<10)\n\t    DEH_snprintf(lumpname, 9, \"map0%i\", map);\n\telse\n\t    DEH_snprintf(lumpname, 9, \"map%i\", map);\n    }\n    else\n    {\n\tlumpname[0] = 'E';\n\tlumpname[1] = '0' + episode;\n\tlumpname[2] = 'M';\n\tlumpname[3] = '0' + map;\n\tlumpname[4] = 0;\n    }\n\n    lumpnum = W_GetNumForName (lumpname);\n\t\n    leveltime = 0;\n\t\n    // note: most of this ordering is important\t\n    P_LoadBlockMap (lumpnum+ML_BLOCKMAP);\n    P_LoadVertexes (lumpnum+ML_VERTEXES);\n    P_LoadSectors (lumpnum+ML_SECTORS);\n    P_LoadSideDefs (lumpnum+ML_SIDEDEFS);\n\n    P_LoadLineDefs (lumpnum+ML_LINEDEFS);\n    P_LoadSubsectors (lumpnum+ML_SSECTORS);\n    P_LoadNodes (lumpnum+ML_NODES);\n    P_LoadSegs (lumpnum+ML_SEGS);\n\n    P_GroupLines ();\n    P_LoadReject (lumpnum+ML_REJECT);\n\n    bodyqueslot = 0;\n    deathmatch_p = deathmatchstarts;\n    P_LoadThings (lumpnum+ML_THINGS);\n    \n    // if deathmatch, randomly spawn the active players\n    if (deathmatch)\n    {\n\tfor (i=0 ; i<MAXPLAYERS ; i++)\n\t    if (playeringame[i])\n\t    {\n\t\tplayers[i].mo = NULL;\n\t\tG_DeathMatchSpawnPlayer (i);\n\t    }\n\t\t\t\n    }\n\n    // clear special respawning que\n    iquehead = iquetail = 0;\t\t\n\t\n    // set up world state\n    P_SpawnSpecials ();\n\t\n    // build subsector connect matrix\n    //\tUNUSED P_ConnectSubsectors ();\n\n    // preload graphics\n    if (precache)\n\tR_PrecacheLevel ();\n\n    //printf (\"free memory: 0x%x\\n\", Z_FreeMemory());\n\n}\n\n\n\n//\n// P_Init\n//\nvoid P_Init (void)\n{\n    P_InitSwitchList ();\n    P_InitPicAnims ();\n    R_InitSprites (sprnames);\n}\n\n\n\n"
  },
  {
    "path": "fbdoom/p_setup.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//   Setup a game, startup stuff.\n//\n\n\n#ifndef __P_SETUP__\n#define __P_SETUP__\n\n\n\n\n// NOT called by W_Ticker. Fixme.\nvoid\nP_SetupLevel\n( int\t\tepisode,\n  int\t\tmap,\n  int\t\tplayermask,\n  skill_t\tskill);\n\n// Called by startup code.\nvoid P_Init (void);\n\n#endif\n"
  },
  {
    "path": "fbdoom/p_sight.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tLineOfSight/Visibility checks, uses REJECT Lookup Table.\n//\n\n\n\n#include \"doomdef.h\"\n\n#include \"i_system.h\"\n#include \"p_local.h\"\n\n// State.\n#include \"r_state.h\"\n\n//\n// P_CheckSight\n//\nfixed_t\t\tsightzstart;\t\t// eye z of looker\nfixed_t\t\ttopslope;\nfixed_t\t\tbottomslope;\t\t// slopes to top and bottom of target\n\ndivline_t\tstrace;\t\t\t// from t1 to t2\nfixed_t\t\tt2x;\nfixed_t\t\tt2y;\n\nint\t\tsightcounts[2];\n\n\n//\n// P_DivlineSide\n// Returns side 0 (front), 1 (back), or 2 (on).\n//\nint\nP_DivlineSide\n( fixed_t\tx,\n  fixed_t\ty,\n  divline_t*\tnode )\n{\n    fixed_t\tdx;\n    fixed_t\tdy;\n    fixed_t\tleft;\n    fixed_t\tright;\n\n    if (!node->dx)\n    {\n\tif (x==node->x)\n\t    return 2;\n\t\n\tif (x <= node->x)\n\t    return node->dy > 0;\n\n\treturn node->dy < 0;\n    }\n    \n    if (!node->dy)\n    {\n\tif (x==node->y)\n\t    return 2;\n\n\tif (y <= node->y)\n\t    return node->dx < 0;\n\n\treturn node->dx > 0;\n    }\n\t\n    dx = (x - node->x);\n    dy = (y - node->y);\n\n    left =  (node->dy>>FRACBITS) * (dx>>FRACBITS);\n    right = (dy>>FRACBITS) * (node->dx>>FRACBITS);\n\t\n    if (right < left)\n\treturn 0;\t// front side\n    \n    if (left == right)\n\treturn 2;\n    return 1;\t\t// back side\n}\n\n\n//\n// P_InterceptVector2\n// Returns the fractional intercept point\n// along the first divline.\n// This is only called by the addthings and addlines traversers.\n//\nfixed_t\nP_InterceptVector2\n( divline_t*\tv2,\n  divline_t*\tv1 )\n{\n    fixed_t\tfrac;\n    fixed_t\tnum;\n    fixed_t\tden;\n\t\n    den = FixedMul (v1->dy>>8,v2->dx) - FixedMul(v1->dx>>8,v2->dy);\n\n    if (den == 0)\n\treturn 0;\n    //\tI_Error (\"P_InterceptVector: parallel\");\n    \n    num = FixedMul ( (v1->x - v2->x)>>8 ,v1->dy) + \n\tFixedMul ( (v2->y - v1->y)>>8 , v1->dx);\n    frac = FixedDiv (num , den);\n\n    return frac;\n}\n\n//\n// P_CrossSubsector\n// Returns true\n//  if strace crosses the given subsector successfully.\n//\nboolean P_CrossSubsector (int num)\n{\n    seg_t*\t\tseg;\n    line_t*\t\tline;\n    int\t\t\ts1;\n    int\t\t\ts2;\n    int\t\t\tcount;\n    subsector_t*\tsub;\n    sector_t*\t\tfront;\n    sector_t*\t\tback;\n    fixed_t\t\topentop;\n    fixed_t\t\topenbottom;\n    divline_t\t\tdivl;\n    vertex_t*\t\tv1;\n    vertex_t*\t\tv2;\n    fixed_t\t\tfrac;\n    fixed_t\t\tslope;\n\t\n#ifdef RANGECHECK\n    if (num>=numsubsectors)\n\tI_Error (\"P_CrossSubsector: ss %i with numss = %i\",\n\t\t num,\n\t\t numsubsectors);\n#endif\n\n    sub = &subsectors[num];\n    \n    // check lines\n    count = sub->numlines;\n    seg = &segs[sub->firstline];\n\n    for ( ; count ; seg++, count--)\n    {\n\tline = seg->linedef;\n\n\t// allready checked other side?\n\tif (line->validcount == validcount)\n\t    continue;\n\t\n\tline->validcount = validcount;\n\n\tv1 = line->v1;\n\tv2 = line->v2;\n\ts1 = P_DivlineSide (v1->x,v1->y, &strace);\n\ts2 = P_DivlineSide (v2->x, v2->y, &strace);\n\n\t// line isn't crossed?\n\tif (s1 == s2)\n\t    continue;\n\t\n\tdivl.x = v1->x;\n\tdivl.y = v1->y;\n\tdivl.dx = v2->x - v1->x;\n\tdivl.dy = v2->y - v1->y;\n\ts1 = P_DivlineSide (strace.x, strace.y, &divl);\n\ts2 = P_DivlineSide (t2x, t2y, &divl);\n\n\t// line isn't crossed?\n\tif (s1 == s2)\n\t    continue;\t\n\n        // Backsector may be NULL if this is an \"impassible\n        // glass\" hack line.\n\n        if (line->backsector == NULL)\n        {\n            return false;\n        }\n\n\t// stop because it is not two sided anyway\n\t// might do this after updating validcount?\n\tif ( !(line->flags & ML_TWOSIDED) )\n\t    return false;\n\t\n\t// crosses a two sided line\n\tfront = seg->frontsector;\n\tback = seg->backsector;\n\n\t// no wall to block sight with?\n\tif (front->floorheight == back->floorheight\n\t    && front->ceilingheight == back->ceilingheight)\n\t    continue;\t\n\n\t// possible occluder\n\t// because of ceiling height differences\n\tif (front->ceilingheight < back->ceilingheight)\n\t    opentop = front->ceilingheight;\n\telse\n\t    opentop = back->ceilingheight;\n\n\t// because of ceiling height differences\n\tif (front->floorheight > back->floorheight)\n\t    openbottom = front->floorheight;\n\telse\n\t    openbottom = back->floorheight;\n\t\t\n\t// quick test for totally closed doors\n\tif (openbottom >= opentop)\t\n\t    return false;\t\t// stop\n\t\n\tfrac = P_InterceptVector2 (&strace, &divl);\n\t\t\n\tif (front->floorheight != back->floorheight)\n\t{\n\t    slope = FixedDiv (openbottom - sightzstart , frac);\n\t    if (slope > bottomslope)\n\t\tbottomslope = slope;\n\t}\n\t\t\n\tif (front->ceilingheight != back->ceilingheight)\n\t{\n\t    slope = FixedDiv (opentop - sightzstart , frac);\n\t    if (slope < topslope)\n\t\ttopslope = slope;\n\t}\n\t\t\n\tif (topslope <= bottomslope)\n\t    return false;\t\t// stop\t\t\t\t\n    }\n    // passed the subsector ok\n    return true;\t\t\n}\n\n\n\n//\n// P_CrossBSPNode\n// Returns true\n//  if strace crosses the given node successfully.\n//\nboolean P_CrossBSPNode (int bspnum)\n{\n    node_t*\tbsp;\n    int\t\tside;\n\n    if (bspnum & NF_SUBSECTOR)\n    {\n\tif (bspnum == -1)\n\t    return P_CrossSubsector (0);\n\telse\n\t    return P_CrossSubsector (bspnum&(~NF_SUBSECTOR));\n    }\n\t\t\n    bsp = &nodes[bspnum];\n    \n    // decide which side the start point is on\n    side = P_DivlineSide (strace.x, strace.y, (divline_t *)bsp);\n    if (side == 2)\n\tside = 0;\t// an \"on\" should cross both sides\n\n    // cross the starting side\n    if (!P_CrossBSPNode (bsp->children[side]) )\n\treturn false;\n\t\n    // the partition plane is crossed here\n    if (side == P_DivlineSide (t2x, t2y,(divline_t *)bsp))\n    {\n\t// the line doesn't touch the other side\n\treturn true;\n    }\n    \n    // cross the ending side\t\t\n    return P_CrossBSPNode (bsp->children[side^1]);\n}\n\n\n//\n// P_CheckSight\n// Returns true\n//  if a straight line between t1 and t2 is unobstructed.\n// Uses REJECT.\n//\nboolean\nP_CheckSight\n( mobj_t*\tt1,\n  mobj_t*\tt2 )\n{\n    int\t\ts1;\n    int\t\ts2;\n    int\t\tpnum;\n    int\t\tbytenum;\n    int\t\tbitnum;\n    \n    // First check for trivial rejection.\n\n    // Determine subsector entries in REJECT table.\n    s1 = (t1->subsector->sector - sectors);\n    s2 = (t2->subsector->sector - sectors);\n    pnum = s1*numsectors + s2;\n    bytenum = pnum>>3;\n    bitnum = 1 << (pnum&7);\n\n    // Check in REJECT table.\n    if (rejectmatrix[bytenum]&bitnum)\n    {\n\tsightcounts[0]++;\n\n\t// can't possibly be connected\n\treturn false;\t\n    }\n\n    // An unobstructed LOS is possible.\n    // Now look from eyes of t1 to any part of t2.\n    sightcounts[1]++;\n\n    validcount++;\n\t\n    sightzstart = t1->z + t1->height - (t1->height>>2);\n    topslope = (t2->z+t2->height) - sightzstart;\n    bottomslope = (t2->z) - sightzstart;\n\t\n    strace.x = t1->x;\n    strace.y = t1->y;\n    t2x = t2->x;\n    t2y = t2->y;\n    strace.dx = t2->x - t1->x;\n    strace.dy = t2->y - t1->y;\n\n    // the head node is the last node output\n    return P_CrossBSPNode (numnodes-1);\t\n}\n\n\n"
  },
  {
    "path": "fbdoom/p_spec.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tImplements special effects:\n//\tTexture animation, height or lighting changes\n//\t according to adjacent sectors, respective\n//\t utility functions, etc.\n//\tLine Tag handling. Line and Sector triggers.\n//\n\n\n#include <stdlib.h>\n\n#include \"doomdef.h\"\n#include \"doomstat.h\"\n\n#include \"deh_main.h\"\n#include \"i_system.h\"\n#include \"z_zone.h\"\n#include \"m_argv.h\"\n#include \"m_misc.h\"\n#include \"m_random.h\"\n#include \"w_wad.h\"\n\n#include \"r_local.h\"\n#include \"p_local.h\"\n\n#include \"g_game.h\"\n\n#include \"s_sound.h\"\n\n// State.\n#include \"r_state.h\"\n\n// Data.\n#include \"sounds.h\"\n\n\n//\n// Animating textures and planes\n// There is another anim_t used in wi_stuff, unrelated.\n//\ntypedef struct\n{\n    boolean\tistexture;\n    int\t\tpicnum;\n    int\t\tbasepic;\n    int\t\tnumpics;\n    int\t\tspeed;\n    \n} anim_t;\n\n//\n//      source animation definition\n//\ntypedef struct\n{\n    int \tistexture;\t// if false, it is a flat\n    char\tendname[9];\n    char\tstartname[9];\n    int\t\tspeed;\n} animdef_t;\n\n\n\n#define MAXANIMS                32\n\nextern anim_t\tanims[MAXANIMS];\nextern anim_t*\tlastanim;\n\n//\n// P_InitPicAnims\n//\n\n// Floor/ceiling animation sequences,\n//  defined by first and last frame,\n//  i.e. the flat (64x64 tile) name to\n//  be used.\n// The full animation sequence is given\n//  using all the flats between the start\n//  and end entry, in the order found in\n//  the WAD file.\n//\nanimdef_t\t\tanimdefs[] =\n{\n    {false,\t\"NUKAGE3\",\t\"NUKAGE1\",\t8},\n    {false,\t\"FWATER4\",\t\"FWATER1\",\t8},\n    {false,\t\"SWATER4\",\t\"SWATER1\", \t8},\n    {false,\t\"LAVA4\",\t\"LAVA1\",\t8},\n    {false,\t\"BLOOD3\",\t\"BLOOD1\",\t8},\n\n    // DOOM II flat animations.\n    {false,\t\"RROCK08\",\t\"RROCK05\",\t8},\t\t\n    {false,\t\"SLIME04\",\t\"SLIME01\",\t8},\n    {false,\t\"SLIME08\",\t\"SLIME05\",\t8},\n    {false,\t\"SLIME12\",\t\"SLIME09\",\t8},\n\n    {true,\t\"BLODGR4\",\t\"BLODGR1\",\t8},\n    {true,\t\"SLADRIP3\",\t\"SLADRIP1\",\t8},\n\n    {true,\t\"BLODRIP4\",\t\"BLODRIP1\",\t8},\n    {true,\t\"FIREWALL\",\t\"FIREWALA\",\t8},\n    {true,\t\"GSTFONT3\",\t\"GSTFONT1\",\t8},\n    {true,\t\"FIRELAVA\",\t\"FIRELAV3\",\t8},\n    {true,\t\"FIREMAG3\",\t\"FIREMAG1\",\t8},\n    {true,\t\"FIREBLU2\",\t\"FIREBLU1\",\t8},\n    {true,\t\"ROCKRED3\",\t\"ROCKRED1\",\t8},\n\n    {true,\t\"BFALL4\",\t\"BFALL1\",\t8},\n    {true,\t\"SFALL4\",\t\"SFALL1\",\t8},\n    {true,\t\"WFALL4\",\t\"WFALL1\",\t8},\n    {true,\t\"DBRAIN4\",\t\"DBRAIN1\",\t8},\n\t\n    {-1,        \"\",             \"\",             0},\n};\n\nanim_t\t\tanims[MAXANIMS];\nanim_t*\t\tlastanim;\n\n\n//\n//      Animating line specials\n//\n#define MAXLINEANIMS            64\n\nextern  short\tnumlinespecials;\nextern  line_t*\tlinespeciallist[MAXLINEANIMS];\n\n\n\nvoid P_InitPicAnims (void)\n{\n    int\t\ti;\n\n    \n    //\tInit animation\n    lastanim = anims;\n    for (i=0 ; animdefs[i].istexture != -1 ; i++)\n    {\n        char *startname, *endname;\n\n        startname = DEH_String(animdefs[i].startname);\n        endname = DEH_String(animdefs[i].endname);\n\n\tif (animdefs[i].istexture)\n\t{\n\t    // different episode ?\n\t    if (R_CheckTextureNumForName(startname) == -1)\n\t\tcontinue;\t\n\n\t    lastanim->picnum = R_TextureNumForName(endname);\n\t    lastanim->basepic = R_TextureNumForName(startname);\n\t}\n\telse\n\t{\n\t    if (W_CheckNumForName(startname) == -1)\n\t\tcontinue;\n\n\t    lastanim->picnum = R_FlatNumForName(endname);\n\t    lastanim->basepic = R_FlatNumForName(startname);\n\t}\n\n\tlastanim->istexture = animdefs[i].istexture;\n\tlastanim->numpics = lastanim->picnum - lastanim->basepic + 1;\n\n\tif (lastanim->numpics < 2)\n\t    I_Error (\"P_InitPicAnims: bad cycle from %s to %s\",\n\t\t     startname, endname);\n\t\n\tlastanim->speed = animdefs[i].speed;\n\tlastanim++;\n    }\n\t\n}\n\n\n\n//\n// UTILITIES\n//\n\n\n\n//\n// getSide()\n// Will return a side_t*\n//  given the number of the current sector,\n//  the line number, and the side (0/1) that you want.\n//\nside_t*\ngetSide\n( int\t\tcurrentSector,\n  int\t\tline,\n  int\t\tside )\n{\n    return &sides[ (sectors[currentSector].lines[line])->sidenum[side] ];\n}\n\n\n//\n// getSector()\n// Will return a sector_t*\n//  given the number of the current sector,\n//  the line number and the side (0/1) that you want.\n//\nsector_t*\ngetSector\n( int\t\tcurrentSector,\n  int\t\tline,\n  int\t\tside )\n{\n    return sides[ (sectors[currentSector].lines[line])->sidenum[side] ].sector;\n}\n\n\n//\n// twoSided()\n// Given the sector number and the line number,\n//  it will tell you whether the line is two-sided or not.\n//\nint\ntwoSided\n( int\tsector,\n  int\tline )\n{\n    return (sectors[sector].lines[line])->flags & ML_TWOSIDED;\n}\n\n\n\n\n//\n// getNextSector()\n// Return sector_t * of sector next to current.\n// NULL if not two-sided line\n//\nsector_t*\ngetNextSector\n( line_t*\tline,\n  sector_t*\tsec )\n{\n    if (!(line->flags & ML_TWOSIDED))\n\treturn NULL;\n\t\t\n    if (line->frontsector == sec)\n\treturn line->backsector;\n\t\n    return line->frontsector;\n}\n\n\n\n//\n// P_FindLowestFloorSurrounding()\n// FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS\n//\nfixed_t\tP_FindLowestFloorSurrounding(sector_t* sec)\n{\n    int\t\t\ti;\n    line_t*\t\tcheck;\n    sector_t*\t\tother;\n    fixed_t\t\tfloor = sec->floorheight;\n\t\n    for (i=0 ;i < sec->linecount ; i++)\n    {\n\tcheck = sec->lines[i];\n\tother = getNextSector(check,sec);\n\n\tif (!other)\n\t    continue;\n\t\n\tif (other->floorheight < floor)\n\t    floor = other->floorheight;\n    }\n    return floor;\n}\n\n\n\n//\n// P_FindHighestFloorSurrounding()\n// FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS\n//\nfixed_t\tP_FindHighestFloorSurrounding(sector_t *sec)\n{\n    int\t\t\ti;\n    line_t*\t\tcheck;\n    sector_t*\t\tother;\n    fixed_t\t\tfloor = -500*FRACUNIT;\n\t\n    for (i=0 ;i < sec->linecount ; i++)\n    {\n\tcheck = sec->lines[i];\n\tother = getNextSector(check,sec);\n\t\n\tif (!other)\n\t    continue;\n\t\n\tif (other->floorheight > floor)\n\t    floor = other->floorheight;\n    }\n    return floor;\n}\n\n\n\n//\n// P_FindNextHighestFloor\n// FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS\n// Note: this should be doable w/o a fixed array.\n\n// Thanks to entryway for the Vanilla overflow emulation.\n\n// 20 adjoining sectors max!\n#define MAX_ADJOINING_SECTORS     20\n\nfixed_t\nP_FindNextHighestFloor\n( sector_t* sec,\n  int       currentheight )\n{\n    int         i;\n    int         h;\n    int         min;\n    line_t*     check;\n    sector_t*   other;\n    fixed_t     height = currentheight;\n    fixed_t     heightlist[MAX_ADJOINING_SECTORS + 2];\n\n    for (i=0, h=0; i < sec->linecount; i++)\n    {\n        check = sec->lines[i];\n        other = getNextSector(check,sec);\n\n        if (!other)\n            continue;\n        \n        if (other->floorheight > height)\n        {\n            // Emulation of memory (stack) overflow\n            if (h == MAX_ADJOINING_SECTORS + 1)\n            {\n                height = other->floorheight;\n            }\n            else if (h == MAX_ADJOINING_SECTORS + 2)\n            {\n                // Fatal overflow: game crashes at 22 textures\n                I_Error(\"Sector with more than 22 adjoining sectors. \"\n                        \"Vanilla will crash here\");\n            }\n\n            heightlist[h++] = other->floorheight;\n        }\n    }\n    \n    // Find lowest height in list\n    if (!h)\n    {\n        return currentheight;\n    }\n        \n    min = heightlist[0];\n    \n    // Range checking? \n    for (i = 1; i < h; i++)\n    {\n        if (heightlist[i] < min)\n        {\n            min = heightlist[i];\n        }\n    }\n\n    return min;\n}\n\n//\n// FIND LOWEST CEILING IN THE SURROUNDING SECTORS\n//\nfixed_t\nP_FindLowestCeilingSurrounding(sector_t* sec)\n{\n    int\t\t\ti;\n    line_t*\t\tcheck;\n    sector_t*\t\tother;\n    fixed_t\t\theight = INT_MAX;\n\t\n    for (i=0 ;i < sec->linecount ; i++)\n    {\n\tcheck = sec->lines[i];\n\tother = getNextSector(check,sec);\n\n\tif (!other)\n\t    continue;\n\n\tif (other->ceilingheight < height)\n\t    height = other->ceilingheight;\n    }\n    return height;\n}\n\n\n//\n// FIND HIGHEST CEILING IN THE SURROUNDING SECTORS\n//\nfixed_t\tP_FindHighestCeilingSurrounding(sector_t* sec)\n{\n    int\t\ti;\n    line_t*\tcheck;\n    sector_t*\tother;\n    fixed_t\theight = 0;\n\t\n    for (i=0 ;i < sec->linecount ; i++)\n    {\n\tcheck = sec->lines[i];\n\tother = getNextSector(check,sec);\n\n\tif (!other)\n\t    continue;\n\n\tif (other->ceilingheight > height)\n\t    height = other->ceilingheight;\n    }\n    return height;\n}\n\n\n\n//\n// RETURN NEXT SECTOR # THAT LINE TAG REFERS TO\n//\nint\nP_FindSectorFromLineTag\n( line_t*\tline,\n  int\t\tstart )\n{\n    int\ti;\n\t\n    for (i=start+1;i<numsectors;i++)\n\tif (sectors[i].tag == line->tag)\n\t    return i;\n    \n    return -1;\n}\n\n\n\n\n//\n// Find minimum light from an adjacent sector\n//\nint\nP_FindMinSurroundingLight\n( sector_t*\tsector,\n  int\t\tmax )\n{\n    int\t\ti;\n    int\t\tmin;\n    line_t*\tline;\n    sector_t*\tcheck;\n\t\n    min = max;\n    for (i=0 ; i < sector->linecount ; i++)\n    {\n\tline = sector->lines[i];\n\tcheck = getNextSector(line,sector);\n\n\tif (!check)\n\t    continue;\n\n\tif (check->lightlevel < min)\n\t    min = check->lightlevel;\n    }\n    return min;\n}\n\n\n\n//\n// EVENTS\n// Events are operations triggered by using, crossing,\n// or shooting special lines, or by timed thinkers.\n//\n\n//\n// P_CrossSpecialLine - TRIGGER\n// Called every time a thing origin is about\n//  to cross a line with a non 0 special.\n//\nvoid\nP_CrossSpecialLine\n( int\t\tlinenum,\n  int\t\tside,\n  mobj_t*\tthing )\n{\n    line_t*\tline;\n    int\t\tok;\n\n    line = &lines[linenum];\n    \n    //\tTriggers that other things can activate\n    if (!thing->player)\n    {\n\t// Things that should NOT trigger specials...\n\tswitch(thing->type)\n\t{\n\t  case MT_ROCKET:\n\t  case MT_PLASMA:\n\t  case MT_BFG:\n\t  case MT_TROOPSHOT:\n\t  case MT_HEADSHOT:\n\t  case MT_BRUISERSHOT:\n\t    return;\n\t    break;\n\t    \n\t  default: break;\n\t}\n\t\t\n\tok = 0;\n\tswitch(line->special)\n\t{\n\t  case 39:\t// TELEPORT TRIGGER\n\t  case 97:\t// TELEPORT RETRIGGER\n\t  case 125:\t// TELEPORT MONSTERONLY TRIGGER\n\t  case 126:\t// TELEPORT MONSTERONLY RETRIGGER\n\t  case 4:\t// RAISE DOOR\n\t  case 10:\t// PLAT DOWN-WAIT-UP-STAY TRIGGER\n\t  case 88:\t// PLAT DOWN-WAIT-UP-STAY RETRIGGER\n\t    ok = 1;\n\t    break;\n\t}\n\tif (!ok)\n\t    return;\n    }\n\n    \n    // Note: could use some const's here.\n    switch (line->special)\n    {\n\t// TRIGGERS.\n\t// All from here to RETRIGGERS.\n      case 2:\n\t// Open Door\n\tEV_DoDoor(line,vld_open);\n\tline->special = 0;\n\tbreak;\n\n      case 3:\n\t// Close Door\n\tEV_DoDoor(line,vld_close);\n\tline->special = 0;\n\tbreak;\n\n      case 4:\n\t// Raise Door\n\tEV_DoDoor(line,vld_normal);\n\tline->special = 0;\n\tbreak;\n\t\n      case 5:\n\t// Raise Floor\n\tEV_DoFloor(line,raiseFloor);\n\tline->special = 0;\n\tbreak;\n\t\n      case 6:\n\t// Fast Ceiling Crush & Raise\n\tEV_DoCeiling(line,fastCrushAndRaise);\n\tline->special = 0;\n\tbreak;\n\t\n      case 8:\n\t// Build Stairs\n\tEV_BuildStairs(line,build8);\n\tline->special = 0;\n\tbreak;\n\t\n      case 10:\n\t// PlatDownWaitUp\n\tEV_DoPlat(line,downWaitUpStay,0);\n\tline->special = 0;\n\tbreak;\n\t\n      case 12:\n\t// Light Turn On - brightest near\n\tEV_LightTurnOn(line,0);\n\tline->special = 0;\n\tbreak;\n\t\n      case 13:\n\t// Light Turn On 255\n\tEV_LightTurnOn(line,255);\n\tline->special = 0;\n\tbreak;\n\t\n      case 16:\n\t// Close Door 30\n\tEV_DoDoor(line,vld_close30ThenOpen);\n\tline->special = 0;\n\tbreak;\n\t\n      case 17:\n\t// Start Light Strobing\n\tEV_StartLightStrobing(line);\n\tline->special = 0;\n\tbreak;\n\t\n      case 19:\n\t// Lower Floor\n\tEV_DoFloor(line,lowerFloor);\n\tline->special = 0;\n\tbreak;\n\t\n      case 22:\n\t// Raise floor to nearest height and change texture\n\tEV_DoPlat(line,raiseToNearestAndChange,0);\n\tline->special = 0;\n\tbreak;\n\t\n      case 25:\n\t// Ceiling Crush and Raise\n\tEV_DoCeiling(line,crushAndRaise);\n\tline->special = 0;\n\tbreak;\n\t\n      case 30:\n\t// Raise floor to shortest texture height\n\t//  on either side of lines.\n\tEV_DoFloor(line,raiseToTexture);\n\tline->special = 0;\n\tbreak;\n\t\n      case 35:\n\t// Lights Very Dark\n\tEV_LightTurnOn(line,35);\n\tline->special = 0;\n\tbreak;\n\t\n      case 36:\n\t// Lower Floor (TURBO)\n\tEV_DoFloor(line,turboLower);\n\tline->special = 0;\n\tbreak;\n\t\n      case 37:\n\t// LowerAndChange\n\tEV_DoFloor(line,lowerAndChange);\n\tline->special = 0;\n\tbreak;\n\t\n      case 38:\n\t// Lower Floor To Lowest\n\tEV_DoFloor( line, lowerFloorToLowest );\n\tline->special = 0;\n\tbreak;\n\t\n      case 39:\n\t// TELEPORT!\n\tEV_Teleport( line, side, thing );\n\tline->special = 0;\n\tbreak;\n\n      case 40:\n\t// RaiseCeilingLowerFloor\n\tEV_DoCeiling( line, raiseToHighest );\n\tEV_DoFloor( line, lowerFloorToLowest );\n\tline->special = 0;\n\tbreak;\n\t\n      case 44:\n\t// Ceiling Crush\n\tEV_DoCeiling( line, lowerAndCrush );\n\tline->special = 0;\n\tbreak;\n\t\n      case 52:\n\t// EXIT!\n\tG_ExitLevel ();\n\tbreak;\n\t\n      case 53:\n\t// Perpetual Platform Raise\n\tEV_DoPlat(line,perpetualRaise,0);\n\tline->special = 0;\n\tbreak;\n\t\n      case 54:\n\t// Platform Stop\n\tEV_StopPlat(line);\n\tline->special = 0;\n\tbreak;\n\n      case 56:\n\t// Raise Floor Crush\n\tEV_DoFloor(line,raiseFloorCrush);\n\tline->special = 0;\n\tbreak;\n\n      case 57:\n\t// Ceiling Crush Stop\n\tEV_CeilingCrushStop(line);\n\tline->special = 0;\n\tbreak;\n\t\n      case 58:\n\t// Raise Floor 24\n\tEV_DoFloor(line,raiseFloor24);\n\tline->special = 0;\n\tbreak;\n\n      case 59:\n\t// Raise Floor 24 And Change\n\tEV_DoFloor(line,raiseFloor24AndChange);\n\tline->special = 0;\n\tbreak;\n\t\n      case 104:\n\t// Turn lights off in sector(tag)\n\tEV_TurnTagLightsOff(line);\n\tline->special = 0;\n\tbreak;\n\t\n      case 108:\n\t// Blazing Door Raise (faster than TURBO!)\n\tEV_DoDoor (line,vld_blazeRaise);\n\tline->special = 0;\n\tbreak;\n\t\n      case 109:\n\t// Blazing Door Open (faster than TURBO!)\n\tEV_DoDoor (line,vld_blazeOpen);\n\tline->special = 0;\n\tbreak;\n\t\n      case 100:\n\t// Build Stairs Turbo 16\n\tEV_BuildStairs(line,turbo16);\n\tline->special = 0;\n\tbreak;\n\t\n      case 110:\n\t// Blazing Door Close (faster than TURBO!)\n\tEV_DoDoor (line,vld_blazeClose);\n\tline->special = 0;\n\tbreak;\n\n      case 119:\n\t// Raise floor to nearest surr. floor\n\tEV_DoFloor(line,raiseFloorToNearest);\n\tline->special = 0;\n\tbreak;\n\t\n      case 121:\n\t// Blazing PlatDownWaitUpStay\n\tEV_DoPlat(line,blazeDWUS,0);\n\tline->special = 0;\n\tbreak;\n\t\n      case 124:\n\t// Secret EXIT\n\tG_SecretExitLevel ();\n\tbreak;\n\t\t\n      case 125:\n\t// TELEPORT MonsterONLY\n\tif (!thing->player)\n\t{\n\t    EV_Teleport( line, side, thing );\n\t    line->special = 0;\n\t}\n\tbreak;\n\t\n      case 130:\n\t// Raise Floor Turbo\n\tEV_DoFloor(line,raiseFloorTurbo);\n\tline->special = 0;\n\tbreak;\n\t\n      case 141:\n\t// Silent Ceiling Crush & Raise\n\tEV_DoCeiling(line,silentCrushAndRaise);\n\tline->special = 0;\n\tbreak;\n\t\n\t// RETRIGGERS.  All from here till end.\n      case 72:\n\t// Ceiling Crush\n\tEV_DoCeiling( line, lowerAndCrush );\n\tbreak;\n\n      case 73:\n\t// Ceiling Crush and Raise\n\tEV_DoCeiling(line,crushAndRaise);\n\tbreak;\n\n      case 74:\n\t// Ceiling Crush Stop\n\tEV_CeilingCrushStop(line);\n\tbreak;\n\t\n      case 75:\n\t// Close Door\n\tEV_DoDoor(line,vld_close);\n\tbreak;\n\t\n      case 76:\n\t// Close Door 30\n\tEV_DoDoor(line,vld_close30ThenOpen);\n\tbreak;\n\t\n      case 77:\n\t// Fast Ceiling Crush & Raise\n\tEV_DoCeiling(line,fastCrushAndRaise);\n\tbreak;\n\t\n      case 79:\n\t// Lights Very Dark\n\tEV_LightTurnOn(line,35);\n\tbreak;\n\t\n      case 80:\n\t// Light Turn On - brightest near\n\tEV_LightTurnOn(line,0);\n\tbreak;\n\t\n      case 81:\n\t// Light Turn On 255\n\tEV_LightTurnOn(line,255);\n\tbreak;\n\t\n      case 82:\n\t// Lower Floor To Lowest\n\tEV_DoFloor( line, lowerFloorToLowest );\n\tbreak;\n\t\n      case 83:\n\t// Lower Floor\n\tEV_DoFloor(line,lowerFloor);\n\tbreak;\n\n      case 84:\n\t// LowerAndChange\n\tEV_DoFloor(line,lowerAndChange);\n\tbreak;\n\n      case 86:\n\t// Open Door\n\tEV_DoDoor(line,vld_open);\n\tbreak;\n\t\n      case 87:\n\t// Perpetual Platform Raise\n\tEV_DoPlat(line,perpetualRaise,0);\n\tbreak;\n\t\n      case 88:\n\t// PlatDownWaitUp\n\tEV_DoPlat(line,downWaitUpStay,0);\n\tbreak;\n\t\n      case 89:\n\t// Platform Stop\n\tEV_StopPlat(line);\n\tbreak;\n\t\n      case 90:\n\t// Raise Door\n\tEV_DoDoor(line,vld_normal);\n\tbreak;\n\t\n      case 91:\n\t// Raise Floor\n\tEV_DoFloor(line,raiseFloor);\n\tbreak;\n\t\n      case 92:\n\t// Raise Floor 24\n\tEV_DoFloor(line,raiseFloor24);\n\tbreak;\n\t\n      case 93:\n\t// Raise Floor 24 And Change\n\tEV_DoFloor(line,raiseFloor24AndChange);\n\tbreak;\n\t\n      case 94:\n\t// Raise Floor Crush\n\tEV_DoFloor(line,raiseFloorCrush);\n\tbreak;\n\t\n      case 95:\n\t// Raise floor to nearest height\n\t// and change texture.\n\tEV_DoPlat(line,raiseToNearestAndChange,0);\n\tbreak;\n\t\n      case 96:\n\t// Raise floor to shortest texture height\n\t// on either side of lines.\n\tEV_DoFloor(line,raiseToTexture);\n\tbreak;\n\t\n      case 97:\n\t// TELEPORT!\n\tEV_Teleport( line, side, thing );\n\tbreak;\n\t\n      case 98:\n\t// Lower Floor (TURBO)\n\tEV_DoFloor(line,turboLower);\n\tbreak;\n\n      case 105:\n\t// Blazing Door Raise (faster than TURBO!)\n\tEV_DoDoor (line,vld_blazeRaise);\n\tbreak;\n\t\n      case 106:\n\t// Blazing Door Open (faster than TURBO!)\n\tEV_DoDoor (line,vld_blazeOpen);\n\tbreak;\n\n      case 107:\n\t// Blazing Door Close (faster than TURBO!)\n\tEV_DoDoor (line,vld_blazeClose);\n\tbreak;\n\n      case 120:\n\t// Blazing PlatDownWaitUpStay.\n\tEV_DoPlat(line,blazeDWUS,0);\n\tbreak;\n\t\n      case 126:\n\t// TELEPORT MonsterONLY.\n\tif (!thing->player)\n\t    EV_Teleport( line, side, thing );\n\tbreak;\n\t\n      case 128:\n\t// Raise To Nearest Floor\n\tEV_DoFloor(line,raiseFloorToNearest);\n\tbreak;\n\t\n      case 129:\n\t// Raise Floor Turbo\n\tEV_DoFloor(line,raiseFloorTurbo);\n\tbreak;\n    }\n}\n\n\n\n//\n// P_ShootSpecialLine - IMPACT SPECIALS\n// Called when a thing shoots a special line.\n//\nvoid\nP_ShootSpecialLine\n( mobj_t*\tthing,\n  line_t*\tline )\n{\n    int\t\tok;\n    \n    //\tImpacts that other things can activate.\n    if (!thing->player)\n    {\n\tok = 0;\n\tswitch(line->special)\n\t{\n\t  case 46:\n\t    // OPEN DOOR IMPACT\n\t    ok = 1;\n\t    break;\n\t}\n\tif (!ok)\n\t    return;\n    }\n\n    switch(line->special)\n    {\n      case 24:\n\t// RAISE FLOOR\n\tEV_DoFloor(line,raiseFloor);\n\tP_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 46:\n\t// OPEN DOOR\n\tEV_DoDoor(line,vld_open);\n\tP_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 47:\n\t// RAISE FLOOR NEAR AND CHANGE\n\tEV_DoPlat(line,raiseToNearestAndChange,0);\n\tP_ChangeSwitchTexture(line,0);\n\tbreak;\n    }\n}\n\n\n\n//\n// P_PlayerInSpecialSector\n// Called every tic frame\n//  that the player origin is in a special sector\n//\nvoid P_PlayerInSpecialSector (player_t* player)\n{\n    sector_t*\tsector;\n\t\n    sector = player->mo->subsector->sector;\n\n    // Falling, not all the way down yet?\n    if (player->mo->z != sector->floorheight)\n\treturn;\t\n\n    // Has hitten ground.\n    switch (sector->special)\n    {\n      case 5:\n\t// HELLSLIME DAMAGE\n\tif (!player->powers[pw_ironfeet])\n\t    if (!(leveltime&0x1f))\n\t\tP_DamageMobj (player->mo, NULL, NULL, 10);\n\tbreak;\n\t\n      case 7:\n\t// NUKAGE DAMAGE\n\tif (!player->powers[pw_ironfeet])\n\t    if (!(leveltime&0x1f))\n\t\tP_DamageMobj (player->mo, NULL, NULL, 5);\n\tbreak;\n\t\n      case 16:\n\t// SUPER HELLSLIME DAMAGE\n      case 4:\n\t// STROBE HURT\n\tif (!player->powers[pw_ironfeet]\n\t    || (P_Random()<5) )\n\t{\n\t    if (!(leveltime&0x1f))\n\t\tP_DamageMobj (player->mo, NULL, NULL, 20);\n\t}\n\tbreak;\n\t\t\t\n      case 9:\n\t// SECRET SECTOR\n\tplayer->secretcount++;\n\tsector->special = 0;\n\tbreak;\n\t\t\t\n      case 11:\n\t// EXIT SUPER DAMAGE! (for E1M8 finale)\n\tplayer->cheats &= ~CF_GODMODE;\n\n\tif (!(leveltime&0x1f))\n\t    P_DamageMobj (player->mo, NULL, NULL, 20);\n\n\tif (player->health <= 10)\n\t    G_ExitLevel();\n\tbreak;\n\t\t\t\n      default:\n\tI_Error (\"P_PlayerInSpecialSector: \"\n\t\t \"unknown special %i\",\n\t\t sector->special);\n\tbreak;\n    };\n}\n\n\n\n\n//\n// P_UpdateSpecials\n// Animate planes, scroll walls, etc.\n//\nboolean\t\tlevelTimer;\nint\t\tlevelTimeCount;\n\nvoid P_UpdateSpecials (void)\n{\n    anim_t*\tanim;\n    int\t\tpic;\n    int\t\ti;\n    line_t*\tline;\n\n    \n    //\tLEVEL TIMER\n    if (levelTimer == true)\n    {\n\tlevelTimeCount--;\n\tif (!levelTimeCount)\n\t    G_ExitLevel();\n    }\n    \n    //\tANIMATE FLATS AND TEXTURES GLOBALLY\n    for (anim = anims ; anim < lastanim ; anim++)\n    {\n\tfor (i=anim->basepic ; i<anim->basepic+anim->numpics ; i++)\n\t{\n\t    pic = anim->basepic + ( (leveltime/anim->speed + i)%anim->numpics );\n\t    if (anim->istexture)\n\t\ttexturetranslation[i] = pic;\n\t    else\n\t\tflattranslation[i] = pic;\n\t}\n    }\n\n    \n    //\tANIMATE LINE SPECIALS\n    for (i = 0; i < numlinespecials; i++)\n    {\n\tline = linespeciallist[i];\n\tswitch(line->special)\n\t{\n\t  case 48:\n\t    // EFFECT FIRSTCOL SCROLL +\n\t    sides[line->sidenum[0]].textureoffset += FRACUNIT;\n\t    break;\n\t}\n    }\n\n    \n    //\tDO BUTTONS\n    for (i = 0; i < MAXBUTTONS; i++)\n\tif (buttonlist[i].btimer)\n\t{\n\t    buttonlist[i].btimer--;\n\t    if (!buttonlist[i].btimer)\n\t    {\n\t\tswitch(buttonlist[i].where)\n\t\t{\n\t\t  case top:\n\t\t    sides[buttonlist[i].line->sidenum[0]].toptexture =\n\t\t\tbuttonlist[i].btexture;\n\t\t    break;\n\t\t    \n\t\t  case middle:\n\t\t    sides[buttonlist[i].line->sidenum[0]].midtexture =\n\t\t\tbuttonlist[i].btexture;\n\t\t    break;\n\t\t    \n\t\t  case bottom:\n\t\t    sides[buttonlist[i].line->sidenum[0]].bottomtexture =\n\t\t\tbuttonlist[i].btexture;\n\t\t    break;\n\t\t}\n\t\tS_StartSound(&buttonlist[i].soundorg,sfx_swtchn);\n\t\tmemset(&buttonlist[i],0,sizeof(button_t));\n\t    }\n\t}\n}\n\n\n//\n// Donut overrun emulation\n//\n// Derived from the code from PrBoom+.  Thanks go to Andrey Budko (entryway)\n// as usual :-)\n//\n\n#define DONUT_FLOORHEIGHT_DEFAULT 0x00000000\n#define DONUT_FLOORPIC_DEFAULT 0x16\n\nstatic void DonutOverrun(fixed_t *s3_floorheight, short *s3_floorpic,\n                         line_t *line, sector_t *pillar_sector)\n{\n    static int first = 1;\n    static int tmp_s3_floorheight;\n    static int tmp_s3_floorpic;\n\n    extern int numflats;\n\n    if (first)\n    {\n        int p;\n\n        // This is the first time we have had an overrun.\n        first = 0;\n\n        // Default values\n        tmp_s3_floorheight = DONUT_FLOORHEIGHT_DEFAULT;\n        tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT;\n\n        //!\n        // @category compat\n        // @arg <x> <y>\n        //\n        // Use the specified magic values when emulating behavior caused\n        // by memory overruns from improperly constructed donuts.\n        // In Vanilla Doom this can differ depending on the operating\n        // system.  The default (if this option is not specified) is to\n        // emulate the behavior when running under Windows 98.\n\n        p = M_CheckParmWithArgs(\"-donut\", 2);\n\n        if (p > 0)\n        {\n            // Dump of needed memory: (fixed_t)0000:0000 and (short)0000:0008\n            //\n            // C:\\>debug\n            // -d 0:0\n            //\n            // DOS 6.22:\n            // 0000:0000    (57 92 19 00) F4 06 70 00-(16 00)\n            // DOS 7.1:\n            // 0000:0000    (9E 0F C9 00) 65 04 70 00-(16 00)\n            // Win98:\n            // 0000:0000    (00 00 00 00) 65 04 70 00-(16 00)\n            // DOSBox under XP:\n            // 0000:0000    (00 00 00 F1) ?? ?? ?? 00-(07 00)\n\n            M_StrToInt(myargv[p + 1], &tmp_s3_floorheight);\n            M_StrToInt(myargv[p + 2], &tmp_s3_floorpic);\n\n            if (tmp_s3_floorpic >= numflats)\n            {\n                fprintf(stderr,\n                        \"DonutOverrun: The second parameter for \\\"-donut\\\" \"\n                        \"switch should be greater than 0 and less than number \"\n                        \"of flats (%d). Using default value (%d) instead. \\n\",\n                        numflats, DONUT_FLOORPIC_DEFAULT);\n                tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT;\n            }\n        }\n    }\n\n    /*\n    fprintf(stderr,\n            \"Linedef: %d; Sector: %d; \"\n            \"New floor height: %d; New floor pic: %d\\n\",\n            line->iLineID, pillar_sector->iSectorID,\n            tmp_s3_floorheight >> 16, tmp_s3_floorpic);\n     */\n\n    *s3_floorheight = (fixed_t) tmp_s3_floorheight;\n    *s3_floorpic = (short) tmp_s3_floorpic;\n}\n\n\n//\n// Special Stuff that can not be categorized\n//\nint EV_DoDonut(line_t*\tline)\n{\n    sector_t*\t\ts1;\n    sector_t*\t\ts2;\n    sector_t*\t\ts3;\n    int\t\t\tsecnum;\n    int\t\t\trtn;\n    int\t\t\ti;\n    floormove_t*\tfloor;\n    fixed_t s3_floorheight;\n    short s3_floorpic;\n\n    secnum = -1;\n    rtn = 0;\n    while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)\n    {\n\ts1 = &sectors[secnum];\n\n\t// ALREADY MOVING?  IF SO, KEEP GOING...\n\tif (s1->specialdata)\n\t    continue;\n\n\trtn = 1;\n\ts2 = getNextSector(s1->lines[0],s1);\n\n        // Vanilla Doom does not check if the linedef is one sided.  The\n        // game does not crash, but reads invalid memory and causes the\n        // sector floor to move \"down\" to some unknown height.\n        // DOSbox prints a warning about an invalid memory access.\n        //\n        // I'm not sure exactly what invalid memory is being read.  This\n        // isn't something that should be done, anyway.\n        // Just print a warning and return.\n\n        if (s2 == NULL)\n        {\n            fprintf(stderr,\n                    \"EV_DoDonut: linedef had no second sidedef! \"\n                    \"Unexpected behavior may occur in Vanilla Doom. \\n\");\n\t    break;\n        }\n\n\tfor (i = 0; i < s2->linecount; i++)\n\t{\n\t    s3 = s2->lines[i]->backsector;\n\n\t    if (s3 == s1)\n\t\tcontinue;\n\n            if (s3 == NULL)\n            {\n                // e6y\n                // s3 is NULL, so\n                // s3->floorheight is an int at 0000:0000\n                // s3->floorpic is a short at 0000:0008\n                // Trying to emulate\n\n                fprintf(stderr,\n                        \"EV_DoDonut: WARNING: emulating buffer overrun due to \"\n                        \"NULL back sector. \"\n                        \"Unexpected behavior may occur in Vanilla Doom.\\n\");\n\n                DonutOverrun(&s3_floorheight, &s3_floorpic, line, s1);\n            }\n            else\n            {\n                s3_floorheight = s3->floorheight;\n                s3_floorpic = s3->floorpic;\n            }\n\n\t    //\tSpawn rising slime\n\t    floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);\n\t    P_AddThinker (&floor->thinker);\n\t    s2->specialdata = floor;\n\t    floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;\n\t    floor->type = donutRaise;\n\t    floor->crush = false;\n\t    floor->direction = 1;\n\t    floor->sector = s2;\n\t    floor->speed = FLOORSPEED / 2;\n\t    floor->texture = s3_floorpic;\n\t    floor->newspecial = 0;\n\t    floor->floordestheight = s3_floorheight;\n\t    \n\t    //\tSpawn lowering donut-hole\n\t    floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);\n\t    P_AddThinker (&floor->thinker);\n\t    s1->specialdata = floor;\n\t    floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;\n\t    floor->type = lowerFloor;\n\t    floor->crush = false;\n\t    floor->direction = -1;\n\t    floor->sector = s1;\n\t    floor->speed = FLOORSPEED / 2;\n\t    floor->floordestheight = s3_floorheight;\n\t    break;\n\t}\n    }\n    return rtn;\n}\n\n\n\n//\n// SPECIAL SPAWNING\n//\n\n//\n// P_SpawnSpecials\n// After the map has been loaded, scan for specials\n//  that spawn thinkers\n//\nshort\t\tnumlinespecials;\nline_t*\t\tlinespeciallist[MAXLINEANIMS];\n\n\n// Parses command line parameters.\nvoid P_SpawnSpecials (void)\n{\n    sector_t*\tsector;\n    int\t\ti;\n\n    // See if -TIMER was specified.\n\n    if (timelimit > 0 && deathmatch)\n    {\n        levelTimer = true;\n        levelTimeCount = timelimit * 60 * TICRATE;\n    }\n    else\n    {\n\tlevelTimer = false;\n    }\n\n    //\tInit special SECTORs.\n    sector = sectors;\n    for (i=0 ; i<numsectors ; i++, sector++)\n    {\n\tif (!sector->special)\n\t    continue;\n\t\n\tswitch (sector->special)\n\t{\n\t  case 1:\n\t    // FLICKERING LIGHTS\n\t    P_SpawnLightFlash (sector);\n\t    break;\n\n\t  case 2:\n\t    // STROBE FAST\n\t    P_SpawnStrobeFlash(sector,FASTDARK,0);\n\t    break;\n\t    \n\t  case 3:\n\t    // STROBE SLOW\n\t    P_SpawnStrobeFlash(sector,SLOWDARK,0);\n\t    break;\n\t    \n\t  case 4:\n\t    // STROBE FAST/DEATH SLIME\n\t    P_SpawnStrobeFlash(sector,FASTDARK,0);\n\t    sector->special = 4;\n\t    break;\n\t    \n\t  case 8:\n\t    // GLOWING LIGHT\n\t    P_SpawnGlowingLight(sector);\n\t    break;\n\t  case 9:\n\t    // SECRET SECTOR\n\t    totalsecret++;\n\t    break;\n\t    \n\t  case 10:\n\t    // DOOR CLOSE IN 30 SECONDS\n\t    P_SpawnDoorCloseIn30 (sector);\n\t    break;\n\t    \n\t  case 12:\n\t    // SYNC STROBE SLOW\n\t    P_SpawnStrobeFlash (sector, SLOWDARK, 1);\n\t    break;\n\n\t  case 13:\n\t    // SYNC STROBE FAST\n\t    P_SpawnStrobeFlash (sector, FASTDARK, 1);\n\t    break;\n\n\t  case 14:\n\t    // DOOR RAISE IN 5 MINUTES\n\t    P_SpawnDoorRaiseIn5Mins (sector, i);\n\t    break;\n\t    \n\t  case 17:\n\t    P_SpawnFireFlicker(sector);\n\t    break;\n\t}\n    }\n\n    \n    //\tInit line EFFECTs\n    numlinespecials = 0;\n    for (i = 0;i < numlines; i++)\n    {\n\tswitch(lines[i].special)\n\t{\n\t  case 48:\n            if (numlinespecials >= MAXLINEANIMS)\n            {\n                I_Error(\"Too many scrolling wall linedefs! \"\n                        \"(Vanilla limit is 64)\");\n            }\n\t    // EFFECT FIRSTCOL SCROLL+\n\t    linespeciallist[numlinespecials] = &lines[i];\n\t    numlinespecials++;\n\t    break;\n\t}\n    }\n\n    \n    //\tInit other misc stuff\n    for (i = 0;i < MAXCEILINGS;i++)\n\tactiveceilings[i] = NULL;\n\n    for (i = 0;i < MAXPLATS;i++)\n\tactiveplats[i] = NULL;\n    \n    for (i = 0;i < MAXBUTTONS;i++)\n\tmemset(&buttonlist[i],0,sizeof(button_t));\n\n    // UNUSED: no horizonal sliders.\n    //\tP_InitSlidingDoorFrames();\n}\n"
  },
  {
    "path": "fbdoom/p_spec.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:  none\n//\tImplements special effects:\n//\tTexture animation, height or lighting changes\n//\t according to adjacent sectors, respective\n//\t utility functions, etc.\n//\n\n\n#ifndef __P_SPEC__\n#define __P_SPEC__\n\n\n//\n// End-level timer (-TIMER option)\n//\nextern\tboolean levelTimer;\nextern\tint\tlevelTimeCount;\n\n\n//      Define values for map objects\n#define MO_TELEPORTMAN          14\n\n\n// at game start\nvoid    P_InitPicAnims (void);\n\n// at map load\nvoid    P_SpawnSpecials (void);\n\n// every tic\nvoid    P_UpdateSpecials (void);\n\n// when needed\nboolean\nP_UseSpecialLine\n( mobj_t*\tthing,\n  line_t*\tline,\n  int\t\tside );\n\nvoid\nP_ShootSpecialLine\n( mobj_t*\tthing,\n  line_t*\tline );\n\nvoid\nP_CrossSpecialLine\n( int\t\tlinenum,\n  int\t\tside,\n  mobj_t*\tthing );\n\nvoid    P_PlayerInSpecialSector (player_t* player);\n\nint\ntwoSided\n( int\t\tsector,\n  int\t\tline );\n\nsector_t*\ngetSector\n( int\t\tcurrentSector,\n  int\t\tline,\n  int\t\tside );\n\nside_t*\ngetSide\n( int\t\tcurrentSector,\n  int\t\tline,\n  int\t\tside );\n\nfixed_t P_FindLowestFloorSurrounding(sector_t* sec);\nfixed_t P_FindHighestFloorSurrounding(sector_t* sec);\n\nfixed_t\nP_FindNextHighestFloor\n( sector_t*\tsec,\n  int\t\tcurrentheight );\n\nfixed_t P_FindLowestCeilingSurrounding(sector_t* sec);\nfixed_t P_FindHighestCeilingSurrounding(sector_t* sec);\n\nint\nP_FindSectorFromLineTag\n( line_t*\tline,\n  int\t\tstart );\n\nint\nP_FindMinSurroundingLight\n( sector_t*\tsector,\n  int\t\tmax );\n\nsector_t*\ngetNextSector\n( line_t*\tline,\n  sector_t*\tsec );\n\n\n//\n// SPECIAL\n//\nint EV_DoDonut(line_t* line);\n\n\n\n//\n// P_LIGHTS\n//\ntypedef struct\n{\n    thinker_t\tthinker;\n    sector_t*\tsector;\n    int\t\tcount;\n    int\t\tmaxlight;\n    int\t\tminlight;\n    \n} fireflicker_t;\n\n\n\ntypedef struct\n{\n    thinker_t\tthinker;\n    sector_t*\tsector;\n    int\t\tcount;\n    int\t\tmaxlight;\n    int\t\tminlight;\n    int\t\tmaxtime;\n    int\t\tmintime;\n    \n} lightflash_t;\n\n\n\ntypedef struct\n{\n    thinker_t\tthinker;\n    sector_t*\tsector;\n    int\t\tcount;\n    int\t\tminlight;\n    int\t\tmaxlight;\n    int\t\tdarktime;\n    int\t\tbrighttime;\n    \n} strobe_t;\n\n\n\n\ntypedef struct\n{\n    thinker_t\tthinker;\n    sector_t*\tsector;\n    int\t\tminlight;\n    int\t\tmaxlight;\n    int\t\tdirection;\n\n} glow_t;\n\n\n#define GLOWSPEED\t\t\t8\n#define STROBEBRIGHT\t\t5\n#define FASTDARK\t\t\t15\n#define SLOWDARK\t\t\t35\n\nvoid    P_SpawnFireFlicker (sector_t* sector);\nvoid    T_LightFlash (lightflash_t* flash);\nvoid    P_SpawnLightFlash (sector_t* sector);\nvoid    T_StrobeFlash (strobe_t* flash);\n\nvoid\nP_SpawnStrobeFlash\n( sector_t*\tsector,\n  int\t\tfastOrSlow,\n  int\t\tinSync );\n\nvoid    EV_StartLightStrobing(line_t* line);\nvoid    EV_TurnTagLightsOff(line_t* line);\n\nvoid\nEV_LightTurnOn\n( line_t*\tline,\n  int\t\tbright );\n\nvoid    T_Glow(glow_t* g);\nvoid    P_SpawnGlowingLight(sector_t* sector);\n\n\n\n\n//\n// P_SWITCH\n//\ntypedef struct\n{\n    char\tname1[9];\n    char\tname2[9];\n    short\tepisode;\n    \n} switchlist_t;\n\n\ntypedef enum\n{\n    top,\n    middle,\n    bottom\n\n} bwhere_e;\n\n\ntypedef struct\n{\n    line_t*\tline;\n    bwhere_e\twhere;\n    int\t\tbtexture;\n    int\t\tbtimer;\n    degenmobj_t *soundorg;\n\n} button_t;\n\n\n\n\n // max # of wall switches in a level\n#define MAXSWITCHES\t\t50\n\n // 4 players, 4 buttons each at once, max.\n#define MAXBUTTONS\t\t16\n\n // 1 second, in ticks. \n#define BUTTONTIME      35             \n\nextern button_t\tbuttonlist[MAXBUTTONS]; \n\nvoid\nP_ChangeSwitchTexture\n( line_t*\tline,\n  int\t\tuseAgain );\n\nvoid P_InitSwitchList(void);\n\n\n//\n// P_PLATS\n//\ntypedef enum\n{\n    up,\n    down,\n    waiting,\n    in_stasis\n\n} plat_e;\n\n\n\ntypedef enum\n{\n    perpetualRaise,\n    downWaitUpStay,\n    raiseAndChange,\n    raiseToNearestAndChange,\n    blazeDWUS\n\n} plattype_e;\n\n\n\ntypedef struct\n{\n    thinker_t\tthinker;\n    sector_t*\tsector;\n    fixed_t\tspeed;\n    fixed_t\tlow;\n    fixed_t\thigh;\n    int\t\twait;\n    int\t\tcount;\n    plat_e\tstatus;\n    plat_e\toldstatus;\n    boolean\tcrush;\n    int\t\ttag;\n    plattype_e\ttype;\n    \n} plat_t;\n\n\n\n#define PLATWAIT\t\t3\n#define PLATSPEED\t\tFRACUNIT\n#define MAXPLATS\t\t30\n\n\nextern plat_t*\tactiveplats[MAXPLATS];\n\nvoid    T_PlatRaise(plat_t*\tplat);\n\nint\nEV_DoPlat\n( line_t*\tline,\n  plattype_e\ttype,\n  int\t\tamount );\n\nvoid    P_AddActivePlat(plat_t* plat);\nvoid    P_RemoveActivePlat(plat_t* plat);\nvoid    EV_StopPlat(line_t* line);\nvoid    P_ActivateInStasis(int tag);\n\n\n//\n// P_DOORS\n//\ntypedef enum\n{\n    vld_normal,\n    vld_close30ThenOpen,\n    vld_close,\n    vld_open,\n    vld_raiseIn5Mins,\n    vld_blazeRaise,\n    vld_blazeOpen,\n    vld_blazeClose\n\n} vldoor_e;\n\n\n\ntypedef struct\n{\n    thinker_t\tthinker;\n    vldoor_e\ttype;\n    sector_t*\tsector;\n    fixed_t\ttopheight;\n    fixed_t\tspeed;\n\n    // 1 = up, 0 = waiting at top, -1 = down\n    int             direction;\n    \n    // tics to wait at the top\n    int             topwait;\n    // (keep in case a door going down is reset)\n    // when it reaches 0, start going down\n    int             topcountdown;\n    \n} vldoor_t;\n\n\n\n#define VDOORSPEED\t\tFRACUNIT*2\n#define VDOORWAIT\t\t150\n\nvoid\nEV_VerticalDoor\n( line_t*\tline,\n  mobj_t*\tthing );\n\nint\nEV_DoDoor\n( line_t*\tline,\n  vldoor_e\ttype );\n\nint\nEV_DoLockedDoor\n( line_t*\tline,\n  vldoor_e\ttype,\n  mobj_t*\tthing );\n\nvoid    T_VerticalDoor (vldoor_t* door);\nvoid    P_SpawnDoorCloseIn30 (sector_t* sec);\n\nvoid\nP_SpawnDoorRaiseIn5Mins\n( sector_t*\tsec,\n  int\t\tsecnum );\n\n\n\n#if 0 // UNUSED\n//\n//      Sliding doors...\n//\ntypedef enum\n{\n    sd_opening,\n    sd_waiting,\n    sd_closing\n\n} sd_e;\n\n\n\ntypedef enum\n{\n    sdt_openOnly,\n    sdt_closeOnly,\n    sdt_openAndClose\n\n} sdt_e;\n\n\n\n\ntypedef struct\n{\n    thinker_t\tthinker;\n    sdt_e\ttype;\n    line_t*\tline;\n    int\t\tframe;\n    int\t\twhichDoorIndex;\n    int\t\ttimer;\n    sector_t*\tfrontsector;\n    sector_t*\tbacksector;\n    sd_e\t status;\n\n} slidedoor_t;\n\n\n\ntypedef struct\n{\n    char\tfrontFrame1[9];\n    char\tfrontFrame2[9];\n    char\tfrontFrame3[9];\n    char\tfrontFrame4[9];\n    char\tbackFrame1[9];\n    char\tbackFrame2[9];\n    char\tbackFrame3[9];\n    char\tbackFrame4[9];\n    \n} slidename_t;\n\n\n\ntypedef struct\n{\n    int             frontFrames[4];\n    int             backFrames[4];\n\n} slideframe_t;\n\n\n\n// how many frames of animation\n#define SNUMFRAMES\t\t4\n\n#define SDOORWAIT\t\t35*3\n#define SWAITTICS\t\t4\n\n// how many diff. types of anims\n#define MAXSLIDEDOORS\t5                            \n\nvoid P_InitSlidingDoorFrames(void);\n\nvoid\nEV_SlidingDoor\n( line_t*\tline,\n  mobj_t*\tthing );\n#endif\n\n\n\n//\n// P_CEILNG\n//\ntypedef enum\n{\n    lowerToFloor,\n    raiseToHighest,\n    lowerAndCrush,\n    crushAndRaise,\n    fastCrushAndRaise,\n    silentCrushAndRaise\n\n} ceiling_e;\n\n\n\ntypedef struct\n{\n    thinker_t\tthinker;\n    ceiling_e\ttype;\n    sector_t*\tsector;\n    fixed_t\tbottomheight;\n    fixed_t\ttopheight;\n    fixed_t\tspeed;\n    boolean\tcrush;\n\n    // 1 = up, 0 = waiting, -1 = down\n    int\t\tdirection;\n\n    // ID\n    int\t\ttag;                   \n    int\t\tolddirection;\n    \n} ceiling_t;\n\n\n\n\n\n#define CEILSPEED\t\tFRACUNIT\n#define CEILWAIT\t\t150\n#define MAXCEILINGS\t\t30\n\nextern ceiling_t*\tactiveceilings[MAXCEILINGS];\n\nint\nEV_DoCeiling\n( line_t*\tline,\n  ceiling_e\ttype );\n\nvoid    T_MoveCeiling (ceiling_t* ceiling);\nvoid    P_AddActiveCeiling(ceiling_t* c);\nvoid    P_RemoveActiveCeiling(ceiling_t* c);\nint\tEV_CeilingCrushStop(line_t* line);\nvoid    P_ActivateInStasisCeiling(line_t* line);\n\n\n//\n// P_FLOOR\n//\ntypedef enum\n{\n    // lower floor to highest surrounding floor\n    lowerFloor,\n    \n    // lower floor to lowest surrounding floor\n    lowerFloorToLowest,\n    \n    // lower floor to highest surrounding floor VERY FAST\n    turboLower,\n    \n    // raise floor to lowest surrounding CEILING\n    raiseFloor,\n    \n    // raise floor to next highest surrounding floor\n    raiseFloorToNearest,\n\n    // raise floor to shortest height texture around it\n    raiseToTexture,\n    \n    // lower floor to lowest surrounding floor\n    //  and change floorpic\n    lowerAndChange,\n  \n    raiseFloor24,\n    raiseFloor24AndChange,\n    raiseFloorCrush,\n\n     // raise to next highest floor, turbo-speed\n    raiseFloorTurbo,       \n    donutRaise,\n    raiseFloor512\n    \n} floor_e;\n\n\n\n\ntypedef enum\n{\n    build8,\t// slowly build by 8\n    turbo16\t// quickly build by 16\n    \n} stair_e;\n\n\n\ntypedef struct\n{\n    thinker_t\tthinker;\n    floor_e\ttype;\n    boolean\tcrush;\n    sector_t*\tsector;\n    int\t\tdirection;\n    int\t\tnewspecial;\n    short\ttexture;\n    fixed_t\tfloordestheight;\n    fixed_t\tspeed;\n\n} floormove_t;\n\n\n\n#define FLOORSPEED\t\tFRACUNIT\n\ntypedef enum\n{\n    ok,\n    crushed,\n    pastdest\n    \n} result_e;\n\nresult_e\nT_MovePlane\n( sector_t*\tsector,\n  fixed_t\tspeed,\n  fixed_t\tdest,\n  boolean\tcrush,\n  int\t\tfloorOrCeiling,\n  int\t\tdirection );\n\nint\nEV_BuildStairs\n( line_t*\tline,\n  stair_e\ttype );\n\nint\nEV_DoFloor\n( line_t*\tline,\n  floor_e\tfloortype );\n\nvoid T_MoveFloor( floormove_t* floor);\n\n//\n// P_TELEPT\n//\nint\nEV_Teleport\n( line_t*\tline,\n  int\t\tside,\n  mobj_t*\tthing );\n\n#endif\n"
  },
  {
    "path": "fbdoom/p_switch.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n//\n// DESCRIPTION:\n//\tSwitches, buttons. Two-state animation. Exits.\n//\n\n#include <stdio.h>\n\n#include \"i_system.h\"\n#include \"deh_main.h\"\n#include \"doomdef.h\"\n#include \"p_local.h\"\n\n#include \"g_game.h\"\n\n#include \"s_sound.h\"\n\n// Data.\n#include \"sounds.h\"\n\n// State.\n#include \"doomstat.h\"\n#include \"r_state.h\"\n\n\n//\n// CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE\n//\nswitchlist_t alphSwitchList[] =\n{\n    // Doom shareware episode 1 switches\n    {\"SW1BRCOM\",\t\"SW2BRCOM\",\t1},\n    {\"SW1BRN1\",\t\"SW2BRN1\",\t1},\n    {\"SW1BRN2\",\t\"SW2BRN2\",\t1},\n    {\"SW1BRNGN\",\t\"SW2BRNGN\",\t1},\n    {\"SW1BROWN\",\t\"SW2BROWN\",\t1},\n    {\"SW1COMM\",\t\"SW2COMM\",\t1},\n    {\"SW1COMP\",\t\"SW2COMP\",\t1},\n    {\"SW1DIRT\",\t\"SW2DIRT\",\t1},\n    {\"SW1EXIT\",\t\"SW2EXIT\",\t1},\n    {\"SW1GRAY\",\t\"SW2GRAY\",\t1},\n    {\"SW1GRAY1\",\t\"SW2GRAY1\",\t1},\n    {\"SW1METAL\",\t\"SW2METAL\",\t1},\n    {\"SW1PIPE\",\t\"SW2PIPE\",\t1},\n    {\"SW1SLAD\",\t\"SW2SLAD\",\t1},\n    {\"SW1STARG\",\t\"SW2STARG\",\t1},\n    {\"SW1STON1\",\t\"SW2STON1\",\t1},\n    {\"SW1STON2\",\t\"SW2STON2\",\t1},\n    {\"SW1STONE\",\t\"SW2STONE\",\t1},\n    {\"SW1STRTN\",\t\"SW2STRTN\",\t1},\n    \n    // Doom registered episodes 2&3 switches\n    {\"SW1BLUE\",\t\"SW2BLUE\",\t2},\n    {\"SW1CMT\",\t\t\"SW2CMT\",\t2},\n    {\"SW1GARG\",\t\"SW2GARG\",\t2},\n    {\"SW1GSTON\",\t\"SW2GSTON\",\t2},\n    {\"SW1HOT\",\t\t\"SW2HOT\",\t2},\n    {\"SW1LION\",\t\"SW2LION\",\t2},\n    {\"SW1SATYR\",\t\"SW2SATYR\",\t2},\n    {\"SW1SKIN\",\t\"SW2SKIN\",\t2},\n    {\"SW1VINE\",\t\"SW2VINE\",\t2},\n    {\"SW1WOOD\",\t\"SW2WOOD\",\t2},\n    \n    // Doom II switches\n    {\"SW1PANEL\",\t\"SW2PANEL\",\t3},\n    {\"SW1ROCK\",\t\"SW2ROCK\",\t3},\n    {\"SW1MET2\",\t\"SW2MET2\",\t3},\n    {\"SW1WDMET\",\t\"SW2WDMET\",\t3},\n    {\"SW1BRIK\",\t\"SW2BRIK\",\t3},\n    {\"SW1MOD1\",\t\"SW2MOD1\",\t3},\n    {\"SW1ZIM\",\t\t\"SW2ZIM\",\t3},\n    {\"SW1STON6\",\t\"SW2STON6\",\t3},\n    {\"SW1TEK\",\t\t\"SW2TEK\",\t3},\n    {\"SW1MARB\",\t\"SW2MARB\",\t3},\n    {\"SW1SKULL\",\t\"SW2SKULL\",\t3},\n\t\n    {\"\\0\",\t\t\"\\0\",\t\t0}\n};\n\nint\t\tswitchlist[MAXSWITCHES * 2];\nint\t\tnumswitches;\nbutton_t        buttonlist[MAXBUTTONS];\n\n//\n// P_InitSwitchList\n// Only called at game initialization.\n//\nvoid P_InitSwitchList(void)\n{\n    int\t\ti;\n    int\t\tindex;\n    int\t\tepisode;\n\t\n    episode = 1;\n\n    if (gamemode == registered || gamemode == retail)\n\tepisode = 2;\n    else\n\tif ( gamemode == commercial )\n\t    episode = 3;\n\t\t\n    for (index = 0,i = 0;i < MAXSWITCHES;i++)\n    {\n\tif (!alphSwitchList[i].episode)\n\t{\n\t    numswitches = index/2;\n\t    switchlist[index] = -1;\n\t    break;\n\t}\n\t\t\n\tif (alphSwitchList[i].episode <= episode)\n\t{\n#if 0\t// UNUSED - debug?\n\t    int\t\tvalue;\n\t\t\t\n\t    if (R_CheckTextureNumForName(alphSwitchList[i].name1) < 0)\n\t    {\n\t\tI_Error(\"Can't find switch texture '%s'!\",\n\t\t\talphSwitchList[i].name1);\n\t\tcontinue;\n\t    }\n\t    \n\t    value = R_TextureNumForName(alphSwitchList[i].name1);\n#endif\n\t    switchlist[index++] = R_TextureNumForName(DEH_String(alphSwitchList[i].name1));\n\t    switchlist[index++] = R_TextureNumForName(DEH_String(alphSwitchList[i].name2));\n\t}\n    }\n}\n\n\n//\n// Start a button counting down till it turns off.\n//\nvoid\nP_StartButton\n( line_t*\tline,\n  bwhere_e\tw,\n  int\t\ttexture,\n  int\t\ttime )\n{\n    int\t\ti;\n    \n    // See if button is already pressed\n    for (i = 0;i < MAXBUTTONS;i++)\n    {\n\tif (buttonlist[i].btimer\n\t    && buttonlist[i].line == line)\n\t{\n\t    \n\t    return;\n\t}\n    }\n    \n\n    \n    for (i = 0;i < MAXBUTTONS;i++)\n    {\n\tif (!buttonlist[i].btimer)\n\t{\n\t    buttonlist[i].line = line;\n\t    buttonlist[i].where = w;\n\t    buttonlist[i].btexture = texture;\n\t    buttonlist[i].btimer = time;\n\t    buttonlist[i].soundorg = &line->frontsector->soundorg;\n\t    return;\n\t}\n    }\n    \n    I_Error(\"P_StartButton: no button slots left!\");\n}\n\n\n\n\n\n//\n// Function that changes wall texture.\n// Tell it if switch is ok to use again (1=yes, it's a button).\n//\nvoid\nP_ChangeSwitchTexture\n( line_t*\tline,\n  int \t\tuseAgain )\n{\n    int     texTop;\n    int     texMid;\n    int     texBot;\n    int     i;\n    int     sound;\n\t\n    if (!useAgain)\n\tline->special = 0;\n\n    texTop = sides[line->sidenum[0]].toptexture;\n    texMid = sides[line->sidenum[0]].midtexture;\n    texBot = sides[line->sidenum[0]].bottomtexture;\n\t\n    sound = sfx_swtchn;\n\n    // EXIT SWITCH?\n    if (line->special == 11)                \n\tsound = sfx_swtchx;\n\t\n    for (i = 0;i < numswitches*2;i++)\n    {\n\tif (switchlist[i] == texTop)\n\t{\n\t    S_StartSound(buttonlist->soundorg,sound);\n\t    sides[line->sidenum[0]].toptexture = switchlist[i^1];\n\n\t    if (useAgain)\n\t\tP_StartButton(line,top,switchlist[i],BUTTONTIME);\n\n\t    return;\n\t}\n\telse\n\t{\n\t    if (switchlist[i] == texMid)\n\t    {\n\t\tS_StartSound(buttonlist->soundorg,sound);\n\t\tsides[line->sidenum[0]].midtexture = switchlist[i^1];\n\n\t\tif (useAgain)\n\t\t    P_StartButton(line, middle,switchlist[i],BUTTONTIME);\n\n\t\treturn;\n\t    }\n\t    else\n\t    {\n\t\tif (switchlist[i] == texBot)\n\t\t{\n\t\t    S_StartSound(buttonlist->soundorg,sound);\n\t\t    sides[line->sidenum[0]].bottomtexture = switchlist[i^1];\n\n\t\t    if (useAgain)\n\t\t\tP_StartButton(line, bottom,switchlist[i],BUTTONTIME);\n\n\t\t    return;\n\t\t}\n\t    }\n\t}\n    }\n}\n\n\n\n\n\n\n//\n// P_UseSpecialLine\n// Called when a thing uses a special line.\n// Only the front sides of lines are usable.\n//\nboolean\nP_UseSpecialLine\n( mobj_t*\tthing,\n  line_t*\tline,\n  int\t\tside )\n{               \n\n    // Err...\n    // Use the back sides of VERY SPECIAL lines...\n    if (side)\n    {\n\tswitch(line->special)\n\t{\n\t  case 124:\n\t    // Sliding door open&close\n\t    // UNUSED?\n\t    break;\n\n\t  default:\n\t    return false;\n\t    break;\n\t}\n    }\n\n    \n    // Switches that other things can activate.\n    if (!thing->player)\n    {\n\t// never open secret doors\n\tif (line->flags & ML_SECRET)\n\t    return false;\n\t\n\tswitch(line->special)\n\t{\n\t  case 1: \t// MANUAL DOOR RAISE\n\t  case 32:\t// MANUAL BLUE\n\t  case 33:\t// MANUAL RED\n\t  case 34:\t// MANUAL YELLOW\n\t    break;\n\t    \n\t  default:\n\t    return false;\n\t    break;\n\t}\n    }\n\n    \n    // do something  \n    switch (line->special)\n    {\n\t// MANUALS\n      case 1:\t\t// Vertical Door\n      case 26:\t\t// Blue Door/Locked\n      case 27:\t\t// Yellow Door /Locked\n      case 28:\t\t// Red Door /Locked\n\n      case 31:\t\t// Manual door open\n      case 32:\t\t// Blue locked door open\n      case 33:\t\t// Red locked door open\n      case 34:\t\t// Yellow locked door open\n\n      case 117:\t\t// Blazing door raise\n      case 118:\t\t// Blazing door open\n\tEV_VerticalDoor (line, thing);\n\tbreak;\n\t\n\t//UNUSED - Door Slide Open&Close\n\t// case 124:\n\t// EV_SlidingDoor (line, thing);\n\t// break;\n\n\t// SWITCHES\n      case 7:\n\t// Build Stairs\n\tif (EV_BuildStairs(line,build8))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\n      case 9:\n\t// Change Donut\n\tif (EV_DoDonut(line))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 11:\n\t// Exit level\n\tP_ChangeSwitchTexture(line,0);\n\tG_ExitLevel ();\n\tbreak;\n\t\n      case 14:\n\t// Raise Floor 32 and change texture\n\tif (EV_DoPlat(line,raiseAndChange,32))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 15:\n\t// Raise Floor 24 and change texture\n\tif (EV_DoPlat(line,raiseAndChange,24))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 18:\n\t// Raise Floor to next highest floor\n\tif (EV_DoFloor(line, raiseFloorToNearest))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 20:\n\t// Raise Plat next highest floor and change texture\n\tif (EV_DoPlat(line,raiseToNearestAndChange,0))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 21:\n\t// PlatDownWaitUpStay\n\tif (EV_DoPlat(line,downWaitUpStay,0))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 23:\n\t// Lower Floor to Lowest\n\tif (EV_DoFloor(line,lowerFloorToLowest))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 29:\n\t// Raise Door\n\tif (EV_DoDoor(line,vld_normal))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 41:\n\t// Lower Ceiling to Floor\n\tif (EV_DoCeiling(line,lowerToFloor))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 71:\n\t// Turbo Lower Floor\n\tif (EV_DoFloor(line,turboLower))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 49:\n\t// Ceiling Crush And Raise\n\tif (EV_DoCeiling(line,crushAndRaise))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 50:\n\t// Close Door\n\tif (EV_DoDoor(line,vld_close))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 51:\n\t// Secret EXIT\n\tP_ChangeSwitchTexture(line,0);\n\tG_SecretExitLevel ();\n\tbreak;\n\t\n      case 55:\n\t// Raise Floor Crush\n\tif (EV_DoFloor(line,raiseFloorCrush))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 101:\n\t// Raise Floor\n\tif (EV_DoFloor(line,raiseFloor))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 102:\n\t// Lower Floor to Surrounding floor height\n\tif (EV_DoFloor(line,lowerFloor))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 103:\n\t// Open Door\n\tif (EV_DoDoor(line,vld_open))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 111:\n\t// Blazing Door Raise (faster than TURBO!)\n\tif (EV_DoDoor (line,vld_blazeRaise))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 112:\n\t// Blazing Door Open (faster than TURBO!)\n\tif (EV_DoDoor (line,vld_blazeOpen))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 113:\n\t// Blazing Door Close (faster than TURBO!)\n\tif (EV_DoDoor (line,vld_blazeClose))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 122:\n\t// Blazing PlatDownWaitUpStay\n\tif (EV_DoPlat(line,blazeDWUS,0))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 127:\n\t// Build Stairs Turbo 16\n\tif (EV_BuildStairs(line,turbo16))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 131:\n\t// Raise Floor Turbo\n\tif (EV_DoFloor(line,raiseFloorTurbo))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 133:\n\t// BlzOpenDoor BLUE\n      case 135:\n\t// BlzOpenDoor RED\n      case 137:\n\t// BlzOpenDoor YELLOW\n\tif (EV_DoLockedDoor (line,vld_blazeOpen,thing))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n      case 140:\n\t// Raise Floor 512\n\tif (EV_DoFloor(line,raiseFloor512))\n\t    P_ChangeSwitchTexture(line,0);\n\tbreak;\n\t\n\t// BUTTONS\n      case 42:\n\t// Close Door\n\tif (EV_DoDoor(line,vld_close))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 43:\n\t// Lower Ceiling to Floor\n\tif (EV_DoCeiling(line,lowerToFloor))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 45:\n\t// Lower Floor to Surrounding floor height\n\tif (EV_DoFloor(line,lowerFloor))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 60:\n\t// Lower Floor to Lowest\n\tif (EV_DoFloor(line,lowerFloorToLowest))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 61:\n\t// Open Door\n\tif (EV_DoDoor(line,vld_open))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 62:\n\t// PlatDownWaitUpStay\n\tif (EV_DoPlat(line,downWaitUpStay,1))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 63:\n\t// Raise Door\n\tif (EV_DoDoor(line,vld_normal))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 64:\n\t// Raise Floor to ceiling\n\tif (EV_DoFloor(line,raiseFloor))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 66:\n\t// Raise Floor 24 and change texture\n\tif (EV_DoPlat(line,raiseAndChange,24))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 67:\n\t// Raise Floor 32 and change texture\n\tif (EV_DoPlat(line,raiseAndChange,32))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 65:\n\t// Raise Floor Crush\n\tif (EV_DoFloor(line,raiseFloorCrush))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 68:\n\t// Raise Plat to next highest floor and change texture\n\tif (EV_DoPlat(line,raiseToNearestAndChange,0))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 69:\n\t// Raise Floor to next highest floor\n\tif (EV_DoFloor(line, raiseFloorToNearest))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 70:\n\t// Turbo Lower Floor\n\tif (EV_DoFloor(line,turboLower))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 114:\n\t// Blazing Door Raise (faster than TURBO!)\n\tif (EV_DoDoor (line,vld_blazeRaise))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 115:\n\t// Blazing Door Open (faster than TURBO!)\n\tif (EV_DoDoor (line,vld_blazeOpen))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 116:\n\t// Blazing Door Close (faster than TURBO!)\n\tif (EV_DoDoor (line,vld_blazeClose))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 123:\n\t// Blazing PlatDownWaitUpStay\n\tif (EV_DoPlat(line,blazeDWUS,0))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 132:\n\t// Raise Floor Turbo\n\tif (EV_DoFloor(line,raiseFloorTurbo))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 99:\n\t// BlzOpenDoor BLUE\n      case 134:\n\t// BlzOpenDoor RED\n      case 136:\n\t// BlzOpenDoor YELLOW\n\tif (EV_DoLockedDoor (line,vld_blazeOpen,thing))\n\t    P_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 138:\n\t// Light Turn On\n\tEV_LightTurnOn(line,255);\n\tP_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\n      case 139:\n\t// Light Turn Off\n\tEV_LightTurnOn(line,35);\n\tP_ChangeSwitchTexture(line,1);\n\tbreak;\n\t\t\t\n    }\n\t\n    return true;\n}\n\n"
  },
  {
    "path": "fbdoom/p_telept.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tTeleportation.\n//\n\n\n\n\n#include \"doomdef.h\"\n#include \"doomstat.h\"\n\n#include \"s_sound.h\"\n\n#include \"p_local.h\"\n\n\n// Data.\n#include \"sounds.h\"\n\n// State.\n#include \"r_state.h\"\n\n\n\n//\n// TELEPORTATION\n//\nint\nEV_Teleport\n( line_t*\tline,\n  int\t\tside,\n  mobj_t*\tthing )\n{\n    int\t\ti;\n    int\t\ttag;\n    mobj_t*\tm;\n    mobj_t*\tfog;\n    unsigned\tan;\n    thinker_t*\tthinker;\n    sector_t*\tsector;\n    fixed_t\toldx;\n    fixed_t\toldy;\n    fixed_t\toldz;\n\n    // don't teleport missiles\n    if (thing->flags & MF_MISSILE)\n\treturn 0;\t\t\n\n    // Don't teleport if hit back of line,\n    //  so you can get out of teleporter.\n    if (side == 1)\t\t\n\treturn 0;\t\n\n    \n    tag = line->tag;\n    for (i = 0; i < numsectors; i++)\n    {\n\tif (sectors[ i ].tag == tag )\n\t{\n\t    thinker = thinkercap.next;\n\t    for (thinker = thinkercap.next;\n\t\t thinker != &thinkercap;\n\t\t thinker = thinker->next)\n\t    {\n\t\t// not a mobj\n\t\tif (thinker->function.acp1 != (actionf_p1)P_MobjThinker)\n\t\t    continue;\t\n\n\t\tm = (mobj_t *)thinker;\n\t\t\n\t\t// not a teleportman\n\t\tif (m->type != MT_TELEPORTMAN )\n\t\t    continue;\t\t\n\n\t\tsector = m->subsector->sector;\n\t\t// wrong sector\n\t\tif (sector-sectors != i )\n\t\t    continue;\t\n\n\t\toldx = thing->x;\n\t\toldy = thing->y;\n\t\toldz = thing->z;\n\t\t\t\t\n\t\tif (!P_TeleportMove (thing, m->x, m->y))\n\t\t    return 0;\n\n                // The first Final Doom executable does not set thing->z\n                // when teleporting. This quirk is unique to this\n                // particular version; the later version included in\n                // some versions of the Id Anthology fixed this.\n\n                if (gameversion != exe_final)\n\t\t    thing->z = thing->floorz;\n\n\t\tif (thing->player)\n\t\t    thing->player->viewz = thing->z+thing->player->viewheight;\n\n\t\t// spawn teleport fog at source and destination\n\t\tfog = P_SpawnMobj (oldx, oldy, oldz, MT_TFOG);\n\t\tS_StartSound (fog, sfx_telept);\n\t\tan = m->angle >> ANGLETOFINESHIFT;\n\t\tfog = P_SpawnMobj (m->x+20*finecosine[an], m->y+20*finesine[an]\n\t\t\t\t   , thing->z, MT_TFOG);\n\n\t\t// emit sound, where?\n\t\tS_StartSound (fog, sfx_telept);\n\t\t\n\t\t// don't move for a bit\n\t\tif (thing->player)\n\t\t    thing->reactiontime = 18;\t\n\n\t\tthing->angle = m->angle;\n\t\tthing->momx = thing->momy = thing->momz = 0;\n\t\treturn 1;\n\t    }\t\n\t}\n    }\n    return 0;\n}\n\n"
  },
  {
    "path": "fbdoom/p_tick.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tArchiving: SaveGame I/O.\n//\tThinker, Ticker.\n//\n\n\n#include \"z_zone.h\"\n#include \"p_local.h\"\n\n#include \"doomstat.h\"\n\n\nint\tleveltime;\n\n//\n// THINKERS\n// All thinkers should be allocated by Z_Malloc\n// so they can be operated on uniformly.\n// The actual structures will vary in size,\n// but the first element must be thinker_t.\n//\n\n\n\n// Both the head and tail of the thinker list.\nthinker_t\tthinkercap;\n\n\n//\n// P_InitThinkers\n//\nvoid P_InitThinkers (void)\n{\n    thinkercap.prev = thinkercap.next  = &thinkercap;\n}\n\n\n\n\n//\n// P_AddThinker\n// Adds a new thinker at the end of the list.\n//\nvoid P_AddThinker (thinker_t* thinker)\n{\n    thinkercap.prev->next = thinker;\n    thinker->next = &thinkercap;\n    thinker->prev = thinkercap.prev;\n    thinkercap.prev = thinker;\n}\n\n\n\n//\n// P_RemoveThinker\n// Deallocation is lazy -- it will not actually be freed\n// until its thinking turn comes up.\n//\nvoid P_RemoveThinker (thinker_t* thinker)\n{\n  // FIXME: NOP.\n  thinker->function.acv = (actionf_v)(-1);\n}\n\n\n\n//\n// P_AllocateThinker\n// Allocates memory and adds a new thinker at the end of the list.\n//\nvoid P_AllocateThinker (thinker_t*\tthinker)\n{\n}\n\n\n\n//\n// P_RunThinkers\n//\nvoid P_RunThinkers (void)\n{\n    thinker_t*\tcurrentthinker;\n\n    currentthinker = thinkercap.next;\n    while (currentthinker != &thinkercap)\n    {\n\tif ( currentthinker->function.acv == (actionf_v)(-1) )\n\t{\n\t    // time to remove it\n\t    currentthinker->next->prev = currentthinker->prev;\n\t    currentthinker->prev->next = currentthinker->next;\n\t    Z_Free (currentthinker);\n\t}\n\telse\n\t{\n\t    if (currentthinker->function.acp1)\n\t\tcurrentthinker->function.acp1 (currentthinker);\n\t}\n\tcurrentthinker = currentthinker->next;\n    }\n}\n\n\n\n//\n// P_Ticker\n//\n\nvoid P_Ticker (void)\n{\n    int\t\ti;\n    \n    // run the tic\n    if (paused)\n\treturn;\n\t\t\n    // pause if in menu and at least one tic has been run\n    if ( !netgame\n\t && menuactive\n\t && !demoplayback\n\t && players[consoleplayer].viewz != 1)\n    {\n\treturn;\n    }\n    \n\t\t\n    for (i=0 ; i<MAXPLAYERS ; i++)\n\tif (playeringame[i])\n\t    P_PlayerThink (&players[i]);\n\t\t\t\n    P_RunThinkers ();\n    P_UpdateSpecials ();\n    P_RespawnSpecials ();\n\n    // for par times\n    leveltime++;\t\n}\n"
  },
  {
    "path": "fbdoom/p_tick.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\t?\n//\n\n\n#ifndef __P_TICK__\n#define __P_TICK__\n\n\n\n\n// Called by C_Ticker,\n// can call G_PlayerExited.\n// Carries out all thinking of monsters and players.\nvoid P_Ticker (void);\n\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/p_user.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tPlayer related stuff.\n//\tBobbing POV/weapon, movement.\n//\tPending weapon.\n//\n\n\n\n\n#include \"doomdef.h\"\n#include \"d_event.h\"\n\n#include \"p_local.h\"\n\n#include \"doomstat.h\"\n\n\n\n// Index of the special effects (INVUL inverse) map.\n#define INVERSECOLORMAP\t\t32\n\n\n//\n// Movement.\n//\n\n// 16 pixels of bob\n#define MAXBOB\t0x100000\t\n\nboolean\t\tonground;\n\n\n//\n// P_Thrust\n// Moves the given origin along a given angle.\n//\nvoid\nP_Thrust\n( player_t*\tplayer,\n  angle_t\tangle,\n  fixed_t\tmove ) \n{\n    angle >>= ANGLETOFINESHIFT;\n    \n    player->mo->momx += FixedMul(move,finecosine[angle]); \n    player->mo->momy += FixedMul(move,finesine[angle]);\n}\n\n\n\n\n//\n// P_CalcHeight\n// Calculate the walking / running height adjustment\n//\nvoid P_CalcHeight (player_t* player) \n{\n    int\t\tangle;\n    fixed_t\tbob;\n    \n    // Regular movement bobbing\n    // (needs to be calculated for gun swing\n    // even if not on ground)\n    // OPTIMIZE: tablify angle\n    // Note: a LUT allows for effects\n    //  like a ramp with low health.\n    player->bob =\n\tFixedMul (player->mo->momx, player->mo->momx)\n\t+ FixedMul (player->mo->momy,player->mo->momy);\n    \n    player->bob >>= 2;\n\n    if (player->bob>MAXBOB)\n\tplayer->bob = MAXBOB;\n\n    if ((player->cheats & CF_NOMOMENTUM) || !onground)\n    {\n\tplayer->viewz = player->mo->z + VIEWHEIGHT;\n\n\tif (player->viewz > player->mo->ceilingz-4*FRACUNIT)\n\t    player->viewz = player->mo->ceilingz-4*FRACUNIT;\n\n\tplayer->viewz = player->mo->z + player->viewheight;\n\treturn;\n    }\n\t\t\n    angle = (FINEANGLES/20*leveltime)&FINEMASK;\n    bob = FixedMul ( player->bob/2, finesine[angle]);\n\n    \n    // move viewheight\n    if (player->playerstate == PST_LIVE)\n    {\n\tplayer->viewheight += player->deltaviewheight;\n\n\tif (player->viewheight > VIEWHEIGHT)\n\t{\n\t    player->viewheight = VIEWHEIGHT;\n\t    player->deltaviewheight = 0;\n\t}\n\n\tif (player->viewheight < VIEWHEIGHT/2)\n\t{\n\t    player->viewheight = VIEWHEIGHT/2;\n\t    if (player->deltaviewheight <= 0)\n\t\tplayer->deltaviewheight = 1;\n\t}\n\t\n\tif (player->deltaviewheight)\t\n\t{\n\t    player->deltaviewheight += FRACUNIT/4;\n\t    if (!player->deltaviewheight)\n\t\tplayer->deltaviewheight = 1;\n\t}\n    }\n    player->viewz = player->mo->z + player->viewheight + bob;\n\n    if (player->viewz > player->mo->ceilingz-4*FRACUNIT)\n\tplayer->viewz = player->mo->ceilingz-4*FRACUNIT;\n}\n\n\n\n//\n// P_MovePlayer\n//\nvoid P_MovePlayer (player_t* player)\n{\n    ticcmd_t*\t\tcmd;\n\t\n    cmd = &player->cmd;\n\t\n    player->mo->angle += (cmd->angleturn<<16);\n\n    // Do not let the player control movement\n    //  if not onground.\n    onground = (player->mo->z <= player->mo->floorz);\n\t\n    if (cmd->forwardmove && onground)\n\tP_Thrust (player, player->mo->angle, cmd->forwardmove*2048);\n    \n    if (cmd->sidemove && onground)\n\tP_Thrust (player, player->mo->angle-ANG90, cmd->sidemove*2048);\n\n    if ( (cmd->forwardmove || cmd->sidemove) \n\t && player->mo->state == &states[S_PLAY] )\n    {\n\tP_SetMobjState (player->mo, S_PLAY_RUN1);\n    }\n}\t\n\n\n\n//\n// P_DeathThink\n// Fall on your face when dying.\n// Decrease POV height to floor height.\n//\n#define ANG5   \t(ANG90/18)\n\nvoid P_DeathThink (player_t* player)\n{\n    angle_t\t\tangle;\n    angle_t\t\tdelta;\n\n    P_MovePsprites (player);\n\t\n    // fall to the ground\n    if (player->viewheight > 6*FRACUNIT)\n\tplayer->viewheight -= FRACUNIT;\n\n    if (player->viewheight < 6*FRACUNIT)\n\tplayer->viewheight = 6*FRACUNIT;\n\n    player->deltaviewheight = 0;\n    onground = (player->mo->z <= player->mo->floorz);\n    P_CalcHeight (player);\n\t\n    if (player->attacker && player->attacker != player->mo)\n    {\n\tangle = R_PointToAngle2 (player->mo->x,\n\t\t\t\t player->mo->y,\n\t\t\t\t player->attacker->x,\n\t\t\t\t player->attacker->y);\n\t\n\tdelta = angle - player->mo->angle;\n\t\n\tif (delta < ANG5 || delta > (unsigned)-ANG5)\n\t{\n\t    // Looking at killer,\n\t    //  so fade damage flash down.\n\t    player->mo->angle = angle;\n\n\t    if (player->damagecount)\n\t\tplayer->damagecount--;\n\t}\n\telse if (delta < ANG180)\n\t    player->mo->angle += ANG5;\n\telse\n\t    player->mo->angle -= ANG5;\n    }\n    else if (player->damagecount)\n\tplayer->damagecount--;\n\t\n\n    if (player->cmd.buttons & BT_USE)\n\tplayer->playerstate = PST_REBORN;\n}\n\n\n\n//\n// P_PlayerThink\n//\nvoid P_PlayerThink (player_t* player)\n{\n    ticcmd_t*\t\tcmd;\n    weapontype_t\tnewweapon;\n\t\n    // fixme: do this in the cheat code\n    if (player->cheats & CF_NOCLIP)\n\tplayer->mo->flags |= MF_NOCLIP;\n    else\n\tplayer->mo->flags &= ~MF_NOCLIP;\n    \n    // chain saw run forward\n    cmd = &player->cmd;\n    if (player->mo->flags & MF_JUSTATTACKED)\n    {\n\tcmd->angleturn = 0;\n\tcmd->forwardmove = 0xc800/512;\n\tcmd->sidemove = 0;\n\tplayer->mo->flags &= ~MF_JUSTATTACKED;\n    }\n\t\t\t\n\t\n    if (player->playerstate == PST_DEAD)\n    {\n\tP_DeathThink (player);\n\treturn;\n    }\n    \n    // Move around.\n    // Reactiontime is used to prevent movement\n    //  for a bit after a teleport.\n    if (player->mo->reactiontime)\n\tplayer->mo->reactiontime--;\n    else\n\tP_MovePlayer (player);\n    \n    P_CalcHeight (player);\n\n    if (player->mo->subsector->sector->special)\n\tP_PlayerInSpecialSector (player);\n    \n    // Check for weapon change.\n\n    // A special event has no other buttons.\n    if (cmd->buttons & BT_SPECIAL)\n\tcmd->buttons = 0;\t\t\t\n\t\t\n    if (cmd->buttons & BT_CHANGE)\n    {\n\t// The actual changing of the weapon is done\n\t//  when the weapon psprite can do it\n\t//  (read: not in the middle of an attack).\n\tnewweapon = (cmd->buttons&BT_WEAPONMASK)>>BT_WEAPONSHIFT;\n\t\n\tif (newweapon == wp_fist\n\t    && player->weaponowned[wp_chainsaw]\n\t    && !(player->readyweapon == wp_chainsaw\n\t\t && player->powers[pw_strength]))\n\t{\n\t    newweapon = wp_chainsaw;\n\t}\n\t\n\tif ( (gamemode == commercial)\n\t    && newweapon == wp_shotgun \n\t    && player->weaponowned[wp_supershotgun]\n\t    && player->readyweapon != wp_supershotgun)\n\t{\n\t    newweapon = wp_supershotgun;\n\t}\n\t\n\n\tif (player->weaponowned[newweapon]\n\t    && newweapon != player->readyweapon)\n\t{\n\t    // Do not go to plasma or BFG in shareware,\n\t    //  even if cheated.\n\t    if ((newweapon != wp_plasma\n\t\t && newweapon != wp_bfg)\n\t\t|| (gamemode != shareware) )\n\t    {\n\t\tplayer->pendingweapon = newweapon;\n\t    }\n\t}\n    }\n    \n    // check for use\n    if (cmd->buttons & BT_USE)\n    {\n\tif (!player->usedown)\n\t{\n\t    P_UseLines (player);\n\t    player->usedown = true;\n\t}\n    }\n    else\n\tplayer->usedown = false;\n    \n    // cycle psprites\n    P_MovePsprites (player);\n    \n    // Counters, time dependend power ups.\n\n    // Strength counts up to diminish fade.\n    if (player->powers[pw_strength])\n\tplayer->powers[pw_strength]++;\t\n\t\t\n    if (player->powers[pw_invulnerability])\n\tplayer->powers[pw_invulnerability]--;\n\n    if (player->powers[pw_invisibility])\n\tif (! --player->powers[pw_invisibility] )\n\t    player->mo->flags &= ~MF_SHADOW;\n\t\t\t\n    if (player->powers[pw_infrared])\n\tplayer->powers[pw_infrared]--;\n\t\t\n    if (player->powers[pw_ironfeet])\n\tplayer->powers[pw_ironfeet]--;\n\t\t\n    if (player->damagecount)\n\tplayer->damagecount--;\n\t\t\n    if (player->bonuscount)\n\tplayer->bonuscount--;\n\n    \n    // Handling colormaps.\n    if (player->powers[pw_invulnerability])\n    {\n\tif (player->powers[pw_invulnerability] > 4*32\n\t    || (player->powers[pw_invulnerability]&8) )\n\t    player->fixedcolormap = INVERSECOLORMAP;\n\telse\n\t    player->fixedcolormap = 0;\n    }\n    else if (player->powers[pw_infrared])\t\n    {\n\tif (player->powers[pw_infrared] > 4*32\n\t    || (player->powers[pw_infrared]&8) )\n\t{\n\t    // almost full bright\n\t    player->fixedcolormap = 1;\n\t}\n\telse\n\t    player->fixedcolormap = 0;\n    }\n    else\n\tplayer->fixedcolormap = 0;\n}\n\n\n"
  },
  {
    "path": "fbdoom/r_bsp.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tBSP traversal, handling of LineSegs for rendering.\n//\n\n\n\n\n#include \"doomdef.h\"\n\n#include \"m_bbox.h\"\n\n#include \"i_system.h\"\n\n#include \"r_main.h\"\n#include \"r_plane.h\"\n#include \"r_things.h\"\n\n// State.\n#include \"doomstat.h\"\n#include \"r_state.h\"\n\n//#include \"r_local.h\"\n\n\n\nseg_t*\t\tcurline;\nside_t*\t\tsidedef;\nline_t*\t\tlinedef;\nsector_t*\tfrontsector;\nsector_t*\tbacksector;\n\ndrawseg_t\tdrawsegs[MAXDRAWSEGS];\ndrawseg_t*\tds_p;\n\n\nvoid\nR_StoreWallRange\n( int\tstart,\n  int\tstop );\n\n\n\n\n//\n// R_ClearDrawSegs\n//\nvoid R_ClearDrawSegs (void)\n{\n    ds_p = drawsegs;\n}\n\n\n\n//\n// ClipWallSegment\n// Clips the given range of columns\n// and includes it in the new clip list.\n//\ntypedef\tstruct\n{\n    int\tfirst;\n    int last;\n    \n} cliprange_t;\n\n\n#define MAXSEGS\t\t32\n\n// newend is one past the last valid seg\ncliprange_t*\tnewend;\ncliprange_t\tsolidsegs[MAXSEGS];\n\n\n\n\n//\n// R_ClipSolidWallSegment\n// Does handle solid walls,\n//  e.g. single sided LineDefs (middle texture)\n//  that entirely block the view.\n// \nvoid\nR_ClipSolidWallSegment\n( int\t\t\tfirst,\n  int\t\t\tlast )\n{\n    cliprange_t*\tnext;\n    cliprange_t*\tstart;\n\n    // Find the first range that touches the range\n    //  (adjacent pixels are touching).\n    start = solidsegs;\n    while (start->last < first-1)\n\tstart++;\n\n    if (first < start->first)\n    {\n\tif (last < start->first-1)\n\t{\n\t    // Post is entirely visible (above start),\n\t    //  so insert a new clippost.\n\t    R_StoreWallRange (first, last);\n\t    next = newend;\n\t    newend++;\n\t    \n\t    while (next != start)\n\t    {\n\t\t*next = *(next-1);\n\t\tnext--;\n\t    }\n\t    next->first = first;\n\t    next->last = last;\n\t    return;\n\t}\n\t\t\n\t// There is a fragment above *start.\n\tR_StoreWallRange (first, start->first - 1);\n\t// Now adjust the clip size.\n\tstart->first = first;\t\n    }\n\n    // Bottom contained in start?\n    if (last <= start->last)\n\treturn;\t\t\t\n\t\t\n    next = start;\n    while (last >= (next+1)->first-1)\n    {\n\t// There is a fragment between two posts.\n\tR_StoreWallRange (next->last + 1, (next+1)->first - 1);\n\tnext++;\n\t\n\tif (last <= next->last)\n\t{\n\t    // Bottom is contained in next.\n\t    // Adjust the clip size.\n\t    start->last = next->last;\t\n\t    goto crunch;\n\t}\n    }\n\t\n    // There is a fragment after *next.\n    R_StoreWallRange (next->last + 1, last);\n    // Adjust the clip size.\n    start->last = last;\n\t\n    // Remove start+1 to next from the clip list,\n    // because start now covers their area.\n  crunch:\n    if (next == start)\n    {\n\t// Post just extended past the bottom of one post.\n\treturn;\n    }\n    \n\n    while (next++ != newend)\n    {\n\t// Remove a post.\n\t*++start = *next;\n    }\n\n    newend = start+1;\n}\n\n\n\n//\n// R_ClipPassWallSegment\n// Clips the given range of columns,\n//  but does not includes it in the clip list.\n// Does handle windows,\n//  e.g. LineDefs with upper and lower texture.\n//\nvoid\nR_ClipPassWallSegment\n( int\tfirst,\n  int\tlast )\n{\n    cliprange_t*\tstart;\n\n    // Find the first range that touches the range\n    //  (adjacent pixels are touching).\n    start = solidsegs;\n    while (start->last < first-1)\n\tstart++;\n\n    if (first < start->first)\n    {\n\tif (last < start->first-1)\n\t{\n\t    // Post is entirely visible (above start).\n\t    R_StoreWallRange (first, last);\n\t    return;\n\t}\n\t\t\n\t// There is a fragment above *start.\n\tR_StoreWallRange (first, start->first - 1);\n    }\n\n    // Bottom contained in start?\n    if (last <= start->last)\n\treturn;\t\t\t\n\t\t\n    while (last >= (start+1)->first-1)\n    {\n\t// There is a fragment between two posts.\n\tR_StoreWallRange (start->last + 1, (start+1)->first - 1);\n\tstart++;\n\t\n\tif (last <= start->last)\n\t    return;\n    }\n\t\n    // There is a fragment after *next.\n    R_StoreWallRange (start->last + 1, last);\n}\n\n\n\n//\n// R_ClearClipSegs\n//\nvoid R_ClearClipSegs (void)\n{\n    solidsegs[0].first = -0x7fffffff;\n    solidsegs[0].last = -1;\n    solidsegs[1].first = viewwidth;\n    solidsegs[1].last = 0x7fffffff;\n    newend = solidsegs+2;\n}\n\n//\n// R_AddLine\n// Clips the given segment\n// and adds any visible pieces to the line list.\n//\nvoid R_AddLine (seg_t*\tline)\n{\n    int\t\t\tx1;\n    int\t\t\tx2;\n    angle_t\t\tangle1;\n    angle_t\t\tangle2;\n    angle_t\t\tspan;\n    angle_t\t\ttspan;\n    \n    curline = line;\n\n    // OPTIMIZE: quickly reject orthogonal back sides.\n    angle1 = R_PointToAngle (line->v1->x, line->v1->y);\n    angle2 = R_PointToAngle (line->v2->x, line->v2->y);\n    \n    // Clip to view edges.\n    // OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW).\n    span = angle1 - angle2;\n    \n    // Back side? I.e. backface culling?\n    if (span >= ANG180)\n\treturn;\t\t\n\n    // Global angle needed by segcalc.\n    rw_angle1 = angle1;\n    angle1 -= viewangle;\n    angle2 -= viewangle;\n\t\n    tspan = angle1 + clipangle;\n    if (tspan > 2*clipangle)\n    {\n\ttspan -= 2*clipangle;\n\n\t// Totally off the left edge?\n\tif (tspan >= span)\n\t    return;\n\t\n\tangle1 = clipangle;\n    }\n    tspan = clipangle - angle2;\n    if (tspan > 2*clipangle)\n    {\n\ttspan -= 2*clipangle;\n\n\t// Totally off the left edge?\n\tif (tspan >= span)\n\t    return;\t\n\tangle2 = -clipangle;\n    }\n    \n    // The seg is in the view range,\n    // but not necessarily visible.\n    angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT;\n    angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT;\n    x1 = viewangletox[angle1];\n    x2 = viewangletox[angle2];\n\n    // Does not cross a pixel?\n    if (x1 == x2)\n\treturn;\t\t\t\t\n\t\n    backsector = line->backsector;\n\n    // Single sided line?\n    if (!backsector)\n\tgoto clipsolid;\t\t\n\n    // Closed door.\n    if (backsector->ceilingheight <= frontsector->floorheight\n\t|| backsector->floorheight >= frontsector->ceilingheight)\n\tgoto clipsolid;\t\t\n\n    // Window.\n    if (backsector->ceilingheight != frontsector->ceilingheight\n\t|| backsector->floorheight != frontsector->floorheight)\n\tgoto clippass;\t\n\t\t\n    // Reject empty lines used for triggers\n    //  and special events.\n    // Identical floor and ceiling on both sides,\n    // identical light levels on both sides,\n    // and no middle texture.\n    if (backsector->ceilingpic == frontsector->ceilingpic\n\t&& backsector->floorpic == frontsector->floorpic\n\t&& backsector->lightlevel == frontsector->lightlevel\n\t&& curline->sidedef->midtexture == 0)\n    {\n\treturn;\n    }\n    \n\t\t\t\t\n  clippass:\n    R_ClipPassWallSegment (x1, x2-1);\t\n    return;\n\t\t\n  clipsolid:\n    R_ClipSolidWallSegment (x1, x2-1);\n}\n\n\n//\n// R_CheckBBox\n// Checks BSP node/subtree bounding box.\n// Returns true\n//  if some part of the bbox might be visible.\n//\nint\tcheckcoord[12][4] =\n{\n    {3,0,2,1},\n    {3,0,2,0},\n    {3,1,2,0},\n    {0},\n    {2,0,2,1},\n    {0,0,0,0},\n    {3,1,3,0},\n    {0},\n    {2,0,3,1},\n    {2,1,3,1},\n    {2,1,3,0}\n};\n\n\nboolean R_CheckBBox (fixed_t*\tbspcoord)\n{\n    int\t\t\tboxx;\n    int\t\t\tboxy;\n    int\t\t\tboxpos;\n\n    fixed_t\t\tx1;\n    fixed_t\t\ty1;\n    fixed_t\t\tx2;\n    fixed_t\t\ty2;\n    \n    angle_t\t\tangle1;\n    angle_t\t\tangle2;\n    angle_t\t\tspan;\n    angle_t\t\ttspan;\n    \n    cliprange_t*\tstart;\n\n    int\t\t\tsx1;\n    int\t\t\tsx2;\n    \n    // Find the corners of the box\n    // that define the edges from current viewpoint.\n    if (viewx <= bspcoord[BOXLEFT])\n\tboxx = 0;\n    else if (viewx < bspcoord[BOXRIGHT])\n\tboxx = 1;\n    else\n\tboxx = 2;\n\t\t\n    if (viewy >= bspcoord[BOXTOP])\n\tboxy = 0;\n    else if (viewy > bspcoord[BOXBOTTOM])\n\tboxy = 1;\n    else\n\tboxy = 2;\n\t\t\n    boxpos = (boxy<<2)+boxx;\n    if (boxpos == 5)\n\treturn true;\n\t\n    x1 = bspcoord[checkcoord[boxpos][0]];\n    y1 = bspcoord[checkcoord[boxpos][1]];\n    x2 = bspcoord[checkcoord[boxpos][2]];\n    y2 = bspcoord[checkcoord[boxpos][3]];\n    \n    // check clip list for an open space\n    angle1 = R_PointToAngle (x1, y1) - viewangle;\n    angle2 = R_PointToAngle (x2, y2) - viewangle;\n\t\n    span = angle1 - angle2;\n\n    // Sitting on a line?\n    if (span >= ANG180)\n\treturn true;\n    \n    tspan = angle1 + clipangle;\n\n    if (tspan > 2*clipangle)\n    {\n\ttspan -= 2*clipangle;\n\n\t// Totally off the left edge?\n\tif (tspan >= span)\n\t    return false;\t\n\n\tangle1 = clipangle;\n    }\n    tspan = clipangle - angle2;\n    if (tspan > 2*clipangle)\n    {\n\ttspan -= 2*clipangle;\n\n\t// Totally off the left edge?\n\tif (tspan >= span)\n\t    return false;\n\t\n\tangle2 = -clipangle;\n    }\n\n\n    // Find the first clippost\n    //  that touches the source post\n    //  (adjacent pixels are touching).\n    angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT;\n    angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT;\n    sx1 = viewangletox[angle1];\n    sx2 = viewangletox[angle2];\n\n    // Does not cross a pixel.\n    if (sx1 == sx2)\n\treturn false;\t\t\t\n    sx2--;\n\t\n    start = solidsegs;\n    while (start->last < sx2)\n\tstart++;\n    \n    if (sx1 >= start->first\n\t&& sx2 <= start->last)\n    {\n\t// The clippost contains the new span.\n\treturn false;\n    }\n\n    return true;\n}\n\n\n\n//\n// R_Subsector\n// Determine floor/ceiling planes.\n// Add sprites of things in sector.\n// Draw one or more line segments.\n//\nvoid R_Subsector (int num)\n{\n    int\t\t\tcount;\n    seg_t*\t\tline;\n    subsector_t*\tsub;\n\t\n#ifdef RANGECHECK\n    if (num>=numsubsectors)\n\tI_Error (\"R_Subsector: ss %i with numss = %i\",\n\t\t num,\n\t\t numsubsectors);\n#endif\n\n    sscount++;\n    sub = &subsectors[num];\n    frontsector = sub->sector;\n    count = sub->numlines;\n    line = &segs[sub->firstline];\n\n    if (frontsector->floorheight < viewz)\n    {\n\tfloorplane = R_FindPlane (frontsector->floorheight,\n\t\t\t\t  frontsector->floorpic,\n\t\t\t\t  frontsector->lightlevel);\n    }\n    else\n\tfloorplane = NULL;\n    \n    if (frontsector->ceilingheight > viewz \n\t|| frontsector->ceilingpic == skyflatnum)\n    {\n\tceilingplane = R_FindPlane (frontsector->ceilingheight,\n\t\t\t\t    frontsector->ceilingpic,\n\t\t\t\t    frontsector->lightlevel);\n    }\n    else\n\tceilingplane = NULL;\n\t\t\n    R_AddSprites (frontsector);\t\n\n    while (count--)\n    {\n\tR_AddLine (line);\n\tline++;\n    }\n}\n\n\n\n\n//\n// RenderBSPNode\n// Renders all subsectors below a given node,\n//  traversing subtree recursively.\n// Just call with BSP root.\nvoid R_RenderBSPNode (int bspnum)\n{\n    node_t*\tbsp;\n    int\t\tside;\n\n    // Found a subsector?\n    if (bspnum & NF_SUBSECTOR)\n    {\n\tif (bspnum == -1)\t\t\t\n\t    R_Subsector (0);\n\telse\n\t    R_Subsector (bspnum&(~NF_SUBSECTOR));\n\treturn;\n    }\n\t\t\n    bsp = &nodes[bspnum];\n    \n    // Decide which side the view point is on.\n    side = R_PointOnSide (viewx, viewy, bsp);\n\n    // Recursively divide front space.\n    R_RenderBSPNode (bsp->children[side]); \n\n    // Possibly divide back space.\n    if (R_CheckBBox (bsp->bbox[side^1]))\t\n\tR_RenderBSPNode (bsp->children[side^1]);\n}\n\n\n"
  },
  {
    "path": "fbdoom/r_bsp.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tRefresh module, BSP traversal and handling.\n//\n\n\n#ifndef __R_BSP__\n#define __R_BSP__\n\n\n\nextern seg_t*\t\tcurline;\nextern side_t*\t\tsidedef;\nextern line_t*\t\tlinedef;\nextern sector_t*\tfrontsector;\nextern sector_t*\tbacksector;\n\nextern int\t\trw_x;\nextern int\t\trw_stopx;\n\nextern boolean\t\tsegtextured;\n\n// false if the back side is the same plane\nextern boolean\t\tmarkfloor;\t\t\nextern boolean\t\tmarkceiling;\n\nextern boolean\t\tskymap;\n\nextern drawseg_t\tdrawsegs[MAXDRAWSEGS];\nextern drawseg_t*\tds_p;\n\nextern lighttable_t**\thscalelight;\nextern lighttable_t**\tvscalelight;\nextern lighttable_t**\tdscalelight;\n\n\ntypedef void (*drawfunc_t) (int start, int stop);\n\n\n// BSP?\nvoid R_ClearClipSegs (void);\nvoid R_ClearDrawSegs (void);\n\n\nvoid R_RenderBSPNode (int bspnum);\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/r_data.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tPreparation of data for rendering,\n//\tgeneration of lookups, caching, retrieval by name.\n//\n\n#include <stdio.h>\n\n#include \"deh_main.h\"\n#include \"i_swap.h\"\n#include \"i_system.h\"\n#include \"z_zone.h\"\n\n\n#include \"w_wad.h\"\n\n#include \"doomdef.h\"\n#include \"m_misc.h\"\n#include \"r_local.h\"\n#include \"p_local.h\"\n\n#include \"doomstat.h\"\n#include \"r_sky.h\"\n\n\n#include \"r_data.h\"\n\n//\n// Graphics.\n// DOOM graphics for walls and sprites\n// is stored in vertical runs of opaque pixels (posts).\n// A column is composed of zero or more posts,\n// a patch or sprite is composed of zero or more columns.\n// \n\n\n\n//\n// Texture definition.\n// Each texture is composed of one or more patches,\n// with patches being lumps stored in the WAD.\n// The lumps are referenced by number, and patched\n// into the rectangular texture space using origin\n// and possibly other attributes.\n//\ntypedef struct\n{\n    short\toriginx;\n    short\toriginy;\n    short\tpatch;\n    short\tstepdir;\n    short\tcolormap;\n} PACKEDATTR mappatch_t;\n\n\n//\n// Texture definition.\n// A DOOM wall texture is a list of patches\n// which are to be combined in a predefined order.\n//\ntypedef struct\n{\n    char\t\tname[8];\n    int\t\t\tmasked;\t\n    short\t\twidth;\n    short\t\theight;\n    int                 obsolete;\n    short\t\tpatchcount;\n    mappatch_t\tpatches[1];\n} PACKEDATTR maptexture_t;\n\n\n// A single patch from a texture definition,\n//  basically a rectangular area within\n//  the texture rectangle.\ntypedef struct\n{\n    // Block origin (allways UL),\n    // which has allready accounted\n    // for the internal origin of the patch.\n    short\toriginx;\t\n    short\toriginy;\n    int\t\tpatch;\n} texpatch_t;\n\n\n// A maptexturedef_t describes a rectangular texture,\n//  which is composed of one or more mappatch_t structures\n//  that arrange graphic patches.\n\ntypedef struct texture_s texture_t;\n\nstruct texture_s\n{\n    // Keep name for switch changing, etc.\n    char\tname[8];\t\t\n    short\twidth;\n    short\theight;\n\n    // Index in textures list\n\n    int         index;\n\n    // Next in hash table chain\n\n    texture_t  *next;\n    \n    // All the patches[patchcount]\n    //  are drawn back to front into the cached texture.\n    short\tpatchcount;\n    texpatch_t\tpatches[1];\t\t\n};\n\n\n\nint\t\tfirstflat;\nint\t\tlastflat;\nint\t\tnumflats;\n\nint\t\tfirstpatch;\nint\t\tlastpatch;\nint\t\tnumpatches;\n\nint\t\tfirstspritelump;\nint\t\tlastspritelump;\nint\t\tnumspritelumps;\n\nint\t\tnumtextures;\ntexture_t**\ttextures;\ntexture_t**     textures_hashtable;\n\n\nint*\t\t\ttexturewidthmask;\n// needed for texture pegging\nfixed_t*\t\ttextureheight;\t\t\nint*\t\t\ttexturecompositesize;\nshort**\t\t\ttexturecolumnlump;\nunsigned short**\ttexturecolumnofs;\nbyte**\t\t\ttexturecomposite;\n\n// for global animation\nint*\t\tflattranslation;\nint*\t\ttexturetranslation;\n\n// needed for pre rendering\nfixed_t*\tspritewidth;\t\nfixed_t*\tspriteoffset;\nfixed_t*\tspritetopoffset;\n\nlighttable_t\t*colormaps;\n\n\n//\n// MAPTEXTURE_T CACHING\n// When a texture is first needed,\n//  it counts the number of composite columns\n//  required in the texture and allocates space\n//  for a column directory and any new columns.\n// The directory will simply point inside other patches\n//  if there is only one patch in a given column,\n//  but any columns with multiple patches\n//  will have new column_ts generated.\n//\n\n\n\n//\n// R_DrawColumnInCache\n// Clip and draw a column\n//  from a patch into a cached post.\n//\nvoid\nR_DrawColumnInCache\n( column_t*\tpatch,\n  byte*\t\tcache,\n  int\t\toriginy,\n  int\t\tcacheheight )\n{\n    int\t\tcount;\n    int\t\tposition;\n    byte*\tsource;\n\n    while (patch->topdelta != 0xff)\n    {\n\tsource = (byte *)patch + 3;\n\tcount = patch->length;\n\tposition = originy + patch->topdelta;\n\n\tif (position < 0)\n\t{\n\t    count += position;\n\t    position = 0;\n\t}\n\n\tif (position + count > cacheheight)\n\t    count = cacheheight - position;\n\n\tif (count > 0)\n\t    memcpy (cache + position, source, count);\n\t\t\n\tpatch = (column_t *)(  (byte *)patch + patch->length + 4); \n    }\n}\n\n\n\n//\n// R_GenerateComposite\n// Using the texture definition,\n//  the composite texture is created from the patches,\n//  and each column is cached.\n//\nvoid R_GenerateComposite (int texnum)\n{\n    byte*\t\tblock;\n    texture_t*\t\ttexture;\n    texpatch_t*\t\tpatch;\t\n    patch_t*\t\trealpatch;\n    int\t\t\tx;\n    int\t\t\tx1;\n    int\t\t\tx2;\n    int\t\t\ti;\n    column_t*\t\tpatchcol;\n    short*\t\tcollump;\n    unsigned short*\tcolofs;\n\t\n    texture = textures[texnum];\n\n    block = Z_Malloc (texturecompositesize[texnum],\n\t\t      PU_STATIC, \n\t\t      &texturecomposite[texnum]);\t\n\n    collump = texturecolumnlump[texnum];\n    colofs = texturecolumnofs[texnum];\n    \n    // Composite the columns together.\n    patch = texture->patches;\n\t\t\n    for (i=0 , patch = texture->patches;\n\t i<texture->patchcount;\n\t i++, patch++)\n    {\n\trealpatch = W_CacheLumpNum (patch->patch, PU_CACHE);\n\tx1 = patch->originx;\n\tx2 = x1 + SHORT(realpatch->width);\n\n\tif (x1<0)\n\t    x = 0;\n\telse\n\t    x = x1;\n\t\n\tif (x2 > texture->width)\n\t    x2 = texture->width;\n\n\tfor ( ; x<x2 ; x++)\n\t{\n\t    // Column does not have multiple patches?\n\t    if (collump[x] >= 0)\n\t\tcontinue;\n\t    \n\t    patchcol = (column_t *)((byte *)realpatch\n\t\t\t\t    + LONG(realpatch->columnofs[x-x1]));\n\t    R_DrawColumnInCache (patchcol,\n\t\t\t\t block + colofs[x],\n\t\t\t\t patch->originy,\n\t\t\t\t texture->height);\n\t}\n\t\t\t\t\t\t\n    }\n\n    // Now that the texture has been built in column cache,\n    //  it is purgable from zone memory.\n    Z_ChangeTag (block, PU_CACHE);\n}\n\n\n\n//\n// R_GenerateLookup\n//\nvoid R_GenerateLookup (int texnum)\n{\n    texture_t*\t\ttexture;\n    byte*\t\tpatchcount;\t// patchcount[texture->width]\n    texpatch_t*\t\tpatch;\t\n    patch_t*\t\trealpatch;\n    int\t\t\tx;\n    int\t\t\tx1;\n    int\t\t\tx2;\n    int\t\t\ti;\n    short*\t\tcollump;\n    unsigned short*\tcolofs;\n\t\n    texture = textures[texnum];\n\n    // Composited texture not created yet.\n    texturecomposite[texnum] = 0;\n    \n    texturecompositesize[texnum] = 0;\n    collump = texturecolumnlump[texnum];\n    colofs = texturecolumnofs[texnum];\n    \n    // Now count the number of columns\n    //  that are covered by more than one patch.\n    // Fill in the lump / offset, so columns\n    //  with only a single patch are all done.\n    patchcount = (byte *) Z_Malloc(texture->width, PU_STATIC, &patchcount);\n    memset (patchcount, 0, texture->width);\n    patch = texture->patches;\n\n    for (i=0 , patch = texture->patches;\n\t i<texture->patchcount;\n\t i++, patch++)\n    {\n\trealpatch = W_CacheLumpNum (patch->patch, PU_CACHE);\n\tx1 = patch->originx;\n\tx2 = x1 + SHORT(realpatch->width);\n\t\n\tif (x1 < 0)\n\t    x = 0;\n\telse\n\t    x = x1;\n\n\tif (x2 > texture->width)\n\t    x2 = texture->width;\n\tfor ( ; x<x2 ; x++)\n\t{\n\t    patchcount[x]++;\n\t    collump[x] = patch->patch;\n\t    colofs[x] = LONG(realpatch->columnofs[x-x1])+3;\n\t}\n    }\n\t\n    for (x=0 ; x<texture->width ; x++)\n    {\n\tif (!patchcount[x])\n\t{\n\t    printf (\"R_GenerateLookup: column without a patch (%s)\\n\",\n\t\t    texture->name);\n\t    return;\n\t}\n\t// I_Error (\"R_GenerateLookup: column without a patch\");\n\t\n\tif (patchcount[x] > 1)\n\t{\n\t    // Use the cached block.\n\t    collump[x] = -1;\t\n\t    colofs[x] = texturecompositesize[texnum];\n\t    \n\t    if (texturecompositesize[texnum] > 0x10000-texture->height)\n\t    {\n\t\tI_Error (\"R_GenerateLookup: texture %i is >64k\",\n\t\t\t texnum);\n\t    }\n\t    \n\t    texturecompositesize[texnum] += texture->height;\n\t}\n    }\n\n    Z_Free(patchcount);\n}\n\n\n\n\n//\n// R_GetColumn\n//\nbyte*\nR_GetColumn\n( int\t\ttex,\n  int\t\tcol )\n{\n    int\t\tlump;\n    int\t\tofs;\n\t\n    col &= texturewidthmask[tex];\n    lump = texturecolumnlump[tex][col];\n    ofs = texturecolumnofs[tex][col];\n    \n    if (lump > 0)\n\treturn (byte *)W_CacheLumpNum(lump,PU_CACHE)+ofs;\n\n    if (!texturecomposite[tex])\n\tR_GenerateComposite (tex);\n\n    return texturecomposite[tex] + ofs;\n}\n\n\nstatic void GenerateTextureHashTable(void)\n{\n    texture_t **rover;\n    int i;\n    int key;\n\n    textures_hashtable \n            = Z_Malloc(sizeof(texture_t *) * numtextures, PU_STATIC, 0);\n\n    memset(textures_hashtable, 0, sizeof(texture_t *) * numtextures);\n\n    // Add all textures to hash table\n\n    for (i=0; i<numtextures; ++i)\n    {\n        // Store index\n\n        textures[i]->index = i;\n\n        // Vanilla Doom does a linear search of the texures array\n        // and stops at the first entry it finds.  If there are two\n        // entries with the same name, the first one in the array\n        // wins. The new entry must therefore be added at the end\n        // of the hash chain, so that earlier entries win.\n\n        key = W_LumpNameHash(textures[i]->name) % numtextures;\n\n        rover = &textures_hashtable[key];\n\n        while (*rover != NULL)\n        {\n            rover = &(*rover)->next;\n        }\n\n        // Hook into hash table\n\n        textures[i]->next = NULL;\n        *rover = textures[i];\n    }\n}\n\n\n//\n// R_InitTextures\n// Initializes the texture list\n//  with the textures from the world map.\n//\nvoid R_InitTextures (void)\n{\n    maptexture_t*\tmtexture;\n    texture_t*\t\ttexture;\n    mappatch_t*\t\tmpatch;\n    texpatch_t*\t\tpatch;\n\n    int\t\t\ti;\n    int\t\t\tj;\n\n    int*\t\tmaptex;\n    int*\t\tmaptex2;\n    int*\t\tmaptex1;\n    \n    char\t\tname[9];\n    char*\t\tnames;\n    char*\t\tname_p;\n    \n    int*\t\tpatchlookup;\n    \n    int\t\t\ttotalwidth;\n    int\t\t\tnummappatches;\n    int\t\t\toffset;\n    int\t\t\tmaxoff;\n    int\t\t\tmaxoff2;\n    int\t\t\tnumtextures1;\n    int\t\t\tnumtextures2;\n\n    int*\t\tdirectory;\n    \n    int\t\t\ttemp1;\n    int\t\t\ttemp2;\n    int\t\t\ttemp3;\n\n    \n    // Load the patch names from pnames.lmp.\n    name[8] = 0;\n    names = W_CacheLumpName (DEH_String(\"PNAMES\"), PU_STATIC);\n    nummappatches = LONG ( *((int *)names) );\n    name_p = names + 4;\n    patchlookup = Z_Malloc(nummappatches*sizeof(*patchlookup), PU_STATIC, NULL);\n\n    for (i = 0; i < nummappatches; i++)\n    {\n        M_StringCopy(name, name_p + i * 8, sizeof(name));\n        patchlookup[i] = W_CheckNumForName(name);\n    }\n    W_ReleaseLumpName(DEH_String(\"PNAMES\"));\n\n    // Load the map texture definitions from textures.lmp.\n    // The data is contained in one or two lumps,\n    //  TEXTURE1 for shareware, plus TEXTURE2 for commercial.\n    maptex = maptex1 = W_CacheLumpName (DEH_String(\"TEXTURE1\"), PU_STATIC);\n    numtextures1 = LONG(*maptex);\n    maxoff = W_LumpLength (W_GetNumForName (DEH_String(\"TEXTURE1\")));\n    directory = maptex+1;\n\t\n    if (W_CheckNumForName (DEH_String(\"TEXTURE2\")) != -1)\n    {\n\tmaptex2 = W_CacheLumpName (DEH_String(\"TEXTURE2\"), PU_STATIC);\n\tnumtextures2 = LONG(*maptex2);\n\tmaxoff2 = W_LumpLength (W_GetNumForName (DEH_String(\"TEXTURE2\")));\n    }\n    else\n    {\n\tmaptex2 = NULL;\n\tnumtextures2 = 0;\n\tmaxoff2 = 0;\n    }\n    numtextures = numtextures1 + numtextures2;\n\t\n    textures = Z_Malloc (numtextures * sizeof(*textures), PU_STATIC, 0);\n    texturecolumnlump = Z_Malloc (numtextures * sizeof(*texturecolumnlump), PU_STATIC, 0);\n    texturecolumnofs = Z_Malloc (numtextures * sizeof(*texturecolumnofs), PU_STATIC, 0);\n    texturecomposite = Z_Malloc (numtextures * sizeof(*texturecomposite), PU_STATIC, 0);\n    texturecompositesize = Z_Malloc (numtextures * sizeof(*texturecompositesize), PU_STATIC, 0);\n    texturewidthmask = Z_Malloc (numtextures * sizeof(*texturewidthmask), PU_STATIC, 0);\n    textureheight = Z_Malloc (numtextures * sizeof(*textureheight), PU_STATIC, 0);\n\n    totalwidth = 0;\n    \n    //\tReally complex printing shit...\n    temp1 = W_GetNumForName (DEH_String(\"S_START\"));  // P_???????\n    temp2 = W_GetNumForName (DEH_String(\"S_END\")) - 1;\n    temp3 = ((temp2-temp1+63)/64) + ((numtextures+63)/64);\n\n    // If stdout is a real console, use the classic vanilla \"filling\n    // up the box\" effect, which uses backspace to \"step back\" inside\n    // the box.  If stdout is a file, don't draw the box.\n\n    if (I_ConsoleStdout())\n    {\n        printf(\"[\");\n        for (i = 0; i < temp3 + 9; i++)\n            printf(\" \");\n        printf(\"]\");\n        for (i = 0; i < temp3 + 10; i++)\n            printf(\"\\b\");\n    }\n\t\n    for (i=0 ; i<numtextures ; i++, directory++)\n    {\n\tif (!(i&63))\n\t    printf (\".\");\n\n\tif (i == numtextures1)\n\t{\n\t    // Start looking in second texture file.\n\t    maptex = maptex2;\n\t    maxoff = maxoff2;\n\t    directory = maptex+1;\n\t}\n\t\t\n\toffset = LONG(*directory);\n\n\tif (offset > maxoff)\n\t    I_Error (\"R_InitTextures: bad texture directory\");\n\t\n\tmtexture = (maptexture_t *) ( (byte *)maptex + offset);\n\n\ttexture = textures[i] =\n\t    Z_Malloc (sizeof(texture_t)\n\t\t      + sizeof(texpatch_t)*(SHORT(mtexture->patchcount)-1),\n\t\t      PU_STATIC, 0);\n\t\n\ttexture->width = SHORT(mtexture->width);\n\ttexture->height = SHORT(mtexture->height);\n\ttexture->patchcount = SHORT(mtexture->patchcount);\n\t\n\tmemcpy (texture->name, mtexture->name, sizeof(texture->name));\n\tmpatch = &mtexture->patches[0];\n\tpatch = &texture->patches[0];\n\n\tfor (j=0 ; j<texture->patchcount ; j++, mpatch++, patch++)\n\t{\n\t    patch->originx = SHORT(mpatch->originx);\n\t    patch->originy = SHORT(mpatch->originy);\n\t    patch->patch = patchlookup[SHORT(mpatch->patch)];\n\t    if (patch->patch == -1)\n\t    {\n\t\tI_Error (\"R_InitTextures: Missing patch in texture %s\",\n\t\t\t texture->name);\n\t    }\n\t}\t\t\n\ttexturecolumnlump[i] = Z_Malloc (texture->width*sizeof(**texturecolumnlump), PU_STATIC,0);\n\ttexturecolumnofs[i] = Z_Malloc (texture->width*sizeof(**texturecolumnofs), PU_STATIC,0);\n\n\tj = 1;\n\twhile (j*2 <= texture->width)\n\t    j<<=1;\n\n\ttexturewidthmask[i] = j-1;\n\ttextureheight[i] = texture->height<<FRACBITS;\n\t\t\n\ttotalwidth += texture->width;\n    }\n\n    Z_Free(patchlookup);\n\n    W_ReleaseLumpName(DEH_String(\"TEXTURE1\"));\n    if (maptex2)\n        W_ReleaseLumpName(DEH_String(\"TEXTURE2\"));\n    \n    // Precalculate whatever possible.\t\n\n    for (i=0 ; i<numtextures ; i++)\n\tR_GenerateLookup (i);\n    \n    // Create translation table for global animation.\n    texturetranslation = Z_Malloc ((numtextures+1)*sizeof(*texturetranslation), PU_STATIC, 0);\n    \n    for (i=0 ; i<numtextures ; i++)\n\ttexturetranslation[i] = i;\n\n    GenerateTextureHashTable();\n}\n\n\n\n//\n// R_InitFlats\n//\nvoid R_InitFlats (void)\n{\n    int\t\ti;\n\t\n    firstflat = W_GetNumForName (DEH_String(\"F_START\")) + 1;\n    lastflat = W_GetNumForName (DEH_String(\"F_END\")) - 1;\n    numflats = lastflat - firstflat + 1;\n\t\n    // Create translation table for global animation.\n    flattranslation = Z_Malloc ((numflats+1)*sizeof(*flattranslation), PU_STATIC, 0);\n    \n    for (i=0 ; i<numflats ; i++)\n\tflattranslation[i] = i;\n}\n\n\n//\n// R_InitSpriteLumps\n// Finds the width and hoffset of all sprites in the wad,\n//  so the sprite does not need to be cached completely\n//  just for having the header info ready during rendering.\n//\nvoid R_InitSpriteLumps (void)\n{\n    int\t\ti;\n    patch_t\t*patch;\n\t\n    firstspritelump = W_GetNumForName (DEH_String(\"S_START\")) + 1;\n    lastspritelump = W_GetNumForName (DEH_String(\"S_END\")) - 1;\n    \n    numspritelumps = lastspritelump - firstspritelump + 1;\n    spritewidth = Z_Malloc (numspritelumps*sizeof(*spritewidth), PU_STATIC, 0);\n    spriteoffset = Z_Malloc (numspritelumps*sizeof(*spriteoffset), PU_STATIC, 0);\n    spritetopoffset = Z_Malloc (numspritelumps*sizeof(*spritetopoffset), PU_STATIC, 0);\n\t\n    for (i=0 ; i< numspritelumps ; i++)\n    {\n\tif (!(i&63))\n\t    printf (\".\");\n\n\tpatch = W_CacheLumpNum (firstspritelump+i, PU_CACHE);\n\tspritewidth[i] = SHORT(patch->width)<<FRACBITS;\n\tspriteoffset[i] = SHORT(patch->leftoffset)<<FRACBITS;\n\tspritetopoffset[i] = SHORT(patch->topoffset)<<FRACBITS;\n    }\n}\n\n\n\n//\n// R_InitColormaps\n//\nvoid R_InitColormaps (void)\n{\n    int\tlump;\n\n    // Load in the light tables, \n    //  256 byte align tables.\n    lump = W_GetNumForName(DEH_String(\"COLORMAP\"));\n    colormaps = W_CacheLumpNum(lump, PU_STATIC);\n}\n\n\n\n//\n// R_InitData\n// Locates all the lumps\n//  that will be used by all views\n// Must be called after W_Init.\n//\nvoid R_InitData (void)\n{\n    R_InitTextures ();\n    printf (\".\");\n    R_InitFlats ();\n    printf (\".\");\n    R_InitSpriteLumps ();\n    printf (\".\");\n    R_InitColormaps ();\n}\n\n\n\n//\n// R_FlatNumForName\n// Retrieval, get a flat number for a flat name.\n//\nint R_FlatNumForName (char* name)\n{\n    int\t\ti;\n    char\tnamet[9];\n\n    i = W_CheckNumForName (name);\n\n    if (i == -1)\n    {\n\tnamet[8] = 0;\n\tmemcpy (namet, name,8);\n\tI_Error (\"R_FlatNumForName: %s not found\",namet);\n    }\n    return i - firstflat;\n}\n\n\n\n\n//\n// R_CheckTextureNumForName\n// Check whether texture is available.\n// Filter out NoTexture indicator.\n//\nint\tR_CheckTextureNumForName (char *name)\n{\n    texture_t *texture;\n    int key;\n\n    // \"NoTexture\" marker.\n    if (name[0] == '-')\t\t\n\treturn 0;\n\t\t\n    key = W_LumpNameHash(name) % numtextures;\n\n    texture=textures_hashtable[key]; \n    \n    while (texture != NULL)\n    {\n\tif (!strncasecmp (texture->name, name, 8) )\n\t    return texture->index;\n\n        texture = texture->next;\n    }\n    \n    return -1;\n}\n\n\n\n//\n// R_TextureNumForName\n// Calls R_CheckTextureNumForName,\n//  aborts with error message.\n//\nint\tR_TextureNumForName (char* name)\n{\n    int\t\ti;\n\t\n    i = R_CheckTextureNumForName (name);\n\n    if (i==-1)\n    {\n\tI_Error (\"R_TextureNumForName: %s not found\",\n\t\t name);\n    }\n    return i;\n}\n\n\n\n\n//\n// R_PrecacheLevel\n// Preloads all relevant graphics for the level.\n//\nint\t\tflatmemory;\nint\t\ttexturememory;\nint\t\tspritememory;\n\nvoid R_PrecacheLevel (void)\n{\n    char*\t\tflatpresent;\n    char*\t\ttexturepresent;\n    char*\t\tspritepresent;\n\n    int\t\t\ti;\n    int\t\t\tj;\n    int\t\t\tk;\n    int\t\t\tlump;\n    \n    texture_t*\t\ttexture;\n    thinker_t*\t\tth;\n    spriteframe_t*\tsf;\n\n    if (demoplayback)\n\treturn;\n    \n    // Precache flats.\n    flatpresent = Z_Malloc(numflats, PU_STATIC, NULL);\n    memset (flatpresent,0,numflats);\t\n\n    for (i=0 ; i<numsectors ; i++)\n    {\n\tflatpresent[sectors[i].floorpic] = 1;\n\tflatpresent[sectors[i].ceilingpic] = 1;\n    }\n\t\n    flatmemory = 0;\n\n    for (i=0 ; i<numflats ; i++)\n    {\n\tif (flatpresent[i])\n\t{\n\t    lump = firstflat + i;\n\t    flatmemory += lumpinfo[lump].size;\n\t    W_CacheLumpNum(lump, PU_CACHE);\n\t}\n    }\n\n    Z_Free(flatpresent);\n    \n    // Precache textures.\n    texturepresent = Z_Malloc(numtextures, PU_STATIC, NULL);\n    memset (texturepresent,0, numtextures);\n\t\n    for (i=0 ; i<numsides ; i++)\n    {\n\ttexturepresent[sides[i].toptexture] = 1;\n\ttexturepresent[sides[i].midtexture] = 1;\n\ttexturepresent[sides[i].bottomtexture] = 1;\n    }\n\n    // Sky texture is always present.\n    // Note that F_SKY1 is the name used to\n    //  indicate a sky floor/ceiling as a flat,\n    //  while the sky texture is stored like\n    //  a wall texture, with an episode dependend\n    //  name.\n    texturepresent[skytexture] = 1;\n\t\n    texturememory = 0;\n    for (i=0 ; i<numtextures ; i++)\n    {\n\tif (!texturepresent[i])\n\t    continue;\n\n\ttexture = textures[i];\n\t\n\tfor (j=0 ; j<texture->patchcount ; j++)\n\t{\n\t    lump = texture->patches[j].patch;\n\t    texturememory += lumpinfo[lump].size;\n\t    W_CacheLumpNum(lump , PU_CACHE);\n\t}\n    }\n\n    Z_Free(texturepresent);\n    \n    // Precache sprites.\n    spritepresent = Z_Malloc(numsprites, PU_STATIC, NULL);\n    memset (spritepresent,0, numsprites);\n\t\n    for (th = thinkercap.next ; th != &thinkercap ; th=th->next)\n    {\n\tif (th->function.acp1 == (actionf_p1)P_MobjThinker)\n\t    spritepresent[((mobj_t *)th)->sprite] = 1;\n    }\n\t\n    spritememory = 0;\n    for (i=0 ; i<numsprites ; i++)\n    {\n\tif (!spritepresent[i])\n\t    continue;\n\n\tfor (j=0 ; j<sprites[i].numframes ; j++)\n\t{\n\t    sf = &sprites[i].spriteframes[j];\n\t    for (k=0 ; k<8 ; k++)\n\t    {\n\t\tlump = firstspritelump + sf->lump[k];\n\t\tspritememory += lumpinfo[lump].size;\n\t\tW_CacheLumpNum(lump , PU_CACHE);\n\t    }\n\t}\n    }\n\n    Z_Free(spritepresent);\n}\n\n\n\n\n"
  },
  {
    "path": "fbdoom/r_data.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//  Refresh module, data I/O, caching, retrieval of graphics\n//  by name.\n//\n\n\n#ifndef __R_DATA__\n#define __R_DATA__\n\n#include \"r_defs.h\"\n#include \"r_state.h\"\n\n\n// Retrieve column data for span blitting.\nbyte*\nR_GetColumn\n( int\t\ttex,\n  int\t\tcol );\n\n\n// I/O, setting up the stuff.\nvoid R_InitData (void);\nvoid R_PrecacheLevel (void);\n\n\n// Retrieval.\n// Floor/ceiling opaque texture tiles,\n// lookup by name. For animation?\nint R_FlatNumForName (char* name);\n\n\n// Called by P_Ticker for switches and animations,\n// returns the texture number for the texture name.\nint R_TextureNumForName (char *name);\nint R_CheckTextureNumForName (char *name);\n\n#endif\n"
  },
  {
    "path": "fbdoom/r_defs.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//      Refresh/rendering module, shared data struct definitions.\n//\n\n\n#ifndef __R_DEFS__\n#define __R_DEFS__\n\n\n// Screenwidth.\n#include \"doomdef.h\"\n\n// Some more or less basic data types\n// we depend on.\n#include \"m_fixed.h\"\n\n// We rely on the thinker data struct\n// to handle sound origins in sectors.\n#include \"d_think.h\"\n// SECTORS do store MObjs anyway.\n#include \"p_mobj.h\"\n\n#include \"i_video.h\"\n\n#include \"v_patch.h\"\n\n\n\n\n// Silhouette, needed for clipping Segs (mainly)\n// and sprites representing things.\n#define SIL_NONE\t\t0\n#define SIL_BOTTOM\t\t1\n#define SIL_TOP\t\t\t2\n#define SIL_BOTH\t\t3\n\n#define MAXDRAWSEGS\t\t256\n\n\n\n\n\n//\n// INTERNAL MAP TYPES\n//  used by play and refresh\n//\n\n//\n// Your plain vanilla vertex.\n// Note: transformed values not buffered locally,\n//  like some DOOM-alikes (\"wt\", \"WebView\") did.\n//\ntypedef struct\n{\n    fixed_t\tx;\n    fixed_t\ty;\n    \n} vertex_t;\n\n\n// Forward of LineDefs, for Sectors.\nstruct line_s;\n\n// Each sector has a degenmobj_t in its center\n//  for sound origin purposes.\n// I suppose this does not handle sound from\n//  moving objects (doppler), because\n//  position is prolly just buffered, not\n//  updated.\ntypedef struct\n{\n    thinker_t\t\tthinker;\t// not used for anything\n    fixed_t\t\tx;\n    fixed_t\t\ty;\n    fixed_t\t\tz;\n\n} degenmobj_t;\n\n//\n// The SECTORS record, at runtime.\n// Stores things/mobjs.\n//\ntypedef\tstruct\n{\n    fixed_t\tfloorheight;\n    fixed_t\tceilingheight;\n    short\tfloorpic;\n    short\tceilingpic;\n    short\tlightlevel;\n    short\tspecial;\n    short\ttag;\n\n    // 0 = untraversed, 1,2 = sndlines -1\n    int\t\tsoundtraversed;\n\n    // thing that made a sound (or null)\n    mobj_t*\tsoundtarget;\n\n    // mapblock bounding box for height changes\n    int\t\tblockbox[4];\n\n    // origin for any sounds played by the sector\n    degenmobj_t\tsoundorg;\n\n    // if == validcount, already checked\n    int\t\tvalidcount;\n\n    // list of mobjs in sector\n    mobj_t*\tthinglist;\n\n    // thinker_t for reversable actions\n    void*\tspecialdata;\n\n    int\t\t\tlinecount;\n    struct line_s**\tlines;\t// [linecount] size\n    \n} sector_t;\n\n\n\n\n//\n// The SideDef.\n//\n\ntypedef struct\n{\n    // add this to the calculated texture column\n    fixed_t\ttextureoffset;\n    \n    // add this to the calculated texture top\n    fixed_t\trowoffset;\n\n    // Texture indices.\n    // We do not maintain names here. \n    short\ttoptexture;\n    short\tbottomtexture;\n    short\tmidtexture;\n\n    // Sector the SideDef is facing.\n    sector_t*\tsector;\n    \n} side_t;\n\n\n\n//\n// Move clipping aid for LineDefs.\n//\ntypedef enum\n{\n    ST_HORIZONTAL,\n    ST_VERTICAL,\n    ST_POSITIVE,\n    ST_NEGATIVE\n\n} slopetype_t;\n\n\n\ntypedef struct line_s\n{\n    // Vertices, from v1 to v2.\n    vertex_t*\tv1;\n    vertex_t*\tv2;\n\n    // Precalculated v2 - v1 for side checking.\n    fixed_t\tdx;\n    fixed_t\tdy;\n\n    // Animation related.\n    short\tflags;\n    short\tspecial;\n    short\ttag;\n\n    // Visual appearance: SideDefs.\n    //  sidenum[1] will be -1 if one sided\n    short\tsidenum[2];\t\t\t\n\n    // Neat. Another bounding box, for the extent\n    //  of the LineDef.\n    fixed_t\tbbox[4];\n\n    // To aid move clipping.\n    slopetype_t\tslopetype;\n\n    // Front and back sector.\n    // Note: redundant? Can be retrieved from SideDefs.\n    sector_t*\tfrontsector;\n    sector_t*\tbacksector;\n\n    // if == validcount, already checked\n    int\t\tvalidcount;\n\n    // thinker_t for reversable actions\n    void*\tspecialdata;\t\t\n} line_t;\n\n\n\n\n//\n// A SubSector.\n// References a Sector.\n// Basically, this is a list of LineSegs,\n//  indicating the visible walls that define\n//  (all or some) sides of a convex BSP leaf.\n//\ntypedef struct subsector_s\n{\n    sector_t*\tsector;\n    short\tnumlines;\n    short\tfirstline;\n    \n} subsector_t;\n\n\n\n//\n// The LineSeg.\n//\ntypedef struct\n{\n    vertex_t*\tv1;\n    vertex_t*\tv2;\n    \n    fixed_t\toffset;\n\n    angle_t\tangle;\n\n    side_t*\tsidedef;\n    line_t*\tlinedef;\n\n    // Sector references.\n    // Could be retrieved from linedef, too.\n    // backsector is NULL for one sided lines\n    sector_t*\tfrontsector;\n    sector_t*\tbacksector;\n    \n} seg_t;\n\n\n\n//\n// BSP node.\n//\ntypedef struct\n{\n    // Partition line.\n    fixed_t\tx;\n    fixed_t\ty;\n    fixed_t\tdx;\n    fixed_t\tdy;\n\n    // Bounding box for each child.\n    fixed_t\tbbox[2][4];\n\n    // If NF_SUBSECTOR its a subsector.\n    unsigned short children[2];\n    \n} node_t;\n\n\n\n\n// PC direct to screen pointers\n//B UNUSED - keep till detailshift in r_draw.c resolved\n//extern byte*\tdestview;\n//extern byte*\tdestscreen;\n\n\n\n\n\n//\n// OTHER TYPES\n//\n\n// This could be wider for >8 bit display.\n// Indeed, true color support is posibble\n//  precalculating 24bpp lightmap/colormap LUT.\n//  from darkening PLAYPAL to all black.\n// Could even us emore than 32 levels.\ntypedef byte\tlighttable_t;\t\n\n\n\n\n//\n// ?\n//\ntypedef struct drawseg_s\n{\n    seg_t*\t\tcurline;\n    int\t\t\tx1;\n    int\t\t\tx2;\n\n    fixed_t\t\tscale1;\n    fixed_t\t\tscale2;\n    fixed_t\t\tscalestep;\n\n    // 0=none, 1=bottom, 2=top, 3=both\n    int\t\t\tsilhouette;\n\n    // do not clip sprites above this\n    fixed_t\t\tbsilheight;\n\n    // do not clip sprites below this\n    fixed_t\t\ttsilheight;\n    \n    // Pointers to lists for sprite clipping,\n    //  all three adjusted so [x1] is first value.\n    short*\t\tsprtopclip;\t\t\n    short*\t\tsprbottomclip;\t\n    short*\t\tmaskedtexturecol;\n    \n} drawseg_t;\n\n\n\n// A vissprite_t is a thing\n//  that will be drawn during a refresh.\n// I.e. a sprite object that is partly visible.\ntypedef struct vissprite_s\n{\n    // Doubly linked list.\n    struct vissprite_s*\tprev;\n    struct vissprite_s*\tnext;\n    \n    int\t\t\tx1;\n    int\t\t\tx2;\n\n    // for line side calculation\n    fixed_t\t\tgx;\n    fixed_t\t\tgy;\t\t\n\n    // global bottom / top for silhouette clipping\n    fixed_t\t\tgz;\n    fixed_t\t\tgzt;\n\n    // horizontal position of x1\n    fixed_t\t\tstartfrac;\n    \n    fixed_t\t\tscale;\n    \n    // negative if flipped\n    fixed_t\t\txiscale;\t\n\n    fixed_t\t\ttexturemid;\n    int\t\t\tpatch;\n\n    // for color translation and shadow draw,\n    //  maxbright frames as well\n    lighttable_t*\tcolormap;\n   \n    int\t\t\tmobjflags;\n    \n} vissprite_t;\n\n\n//\t\n// Sprites are patches with a special naming convention\n//  so they can be recognized by R_InitSprites.\n// The base name is NNNNFx or NNNNFxFx, with\n//  x indicating the rotation, x = 0, 1-7.\n// The sprite and frame specified by a thing_t\n//  is range checked at run time.\n// A sprite is a patch_t that is assumed to represent\n//  a three dimensional object and may have multiple\n//  rotations pre drawn.\n// Horizontal flipping is used to save space,\n//  thus NNNNF2F5 defines a mirrored patch.\n// Some sprites will only have one picture used\n// for all views: NNNNF0\n//\ntypedef struct\n{\n    // If false use 0 for any position.\n    // Note: as eight entries are available,\n    //  we might as well insert the same name eight times.\n    boolean\trotate;\n\n    // Lump to use for view angles 0-7.\n    short\tlump[8];\n\n    // Flip bit (1 = flip) to use for view angles 0-7.\n    byte\tflip[8];\n    \n} spriteframe_t;\n\n\n\n//\n// A sprite definition:\n//  a number of animation frames.\n//\ntypedef struct\n{\n    int\t\t\tnumframes;\n    spriteframe_t*\tspriteframes;\n\n} spritedef_t;\n\n\n\n//\n// Now what is a visplane, anyway?\n// \ntypedef struct\n{\n  fixed_t\t\theight;\n  int\t\t\tpicnum;\n  int\t\t\tlightlevel;\n  int\t\t\tminx;\n  int\t\t\tmaxx;\n  \n  // leave pads for [minx-1]/[maxx+1]\n  \n  byte\t\tpad1;\n  // Here lies the rub for all\n  //  dynamic resize/change of resolution.\n  byte\t\ttop[SCREENWIDTH];\n  byte\t\tpad2;\n  byte\t\tpad3;\n  // See above.\n  byte\t\tbottom[SCREENWIDTH];\n  byte\t\tpad4;\n\n} visplane_t;\n\n\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/r_draw.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tThe actual span/column drawing functions.\n//\tHere find the main potential for optimization,\n//\t e.g. inline assembly, different algorithms.\n//\n\n\n\n\n#include \"doomdef.h\"\n#include \"deh_main.h\"\n\n#include \"i_system.h\"\n#include \"z_zone.h\"\n#include \"w_wad.h\"\n\n#include \"r_local.h\"\n\n// Needs access to LFB (guess what).\n#include \"v_video.h\"\n\n// State.\n#include \"doomstat.h\"\n\n\n// ?\n#define MAXWIDTH\t\t\t1120\n#define MAXHEIGHT\t\t\t832\n\n// status bar height at bottom of screen\n#define SBARHEIGHT\t\t32\n\n//\n// All drawing to the view buffer is accomplished in this file.\n// The other refresh files only know about ccordinates,\n//  not the architecture of the frame buffer.\n// Conveniently, the frame buffer is a linear one,\n//  and we need only the base address,\n//  and the total size == width*height*depth/8.,\n//\n\n\nbyte*\t\tviewimage; \nint\t\tviewwidth;\nint\t\tscaledviewwidth;\nint\t\tviewheight;\nint\t\tviewwindowx;\nint\t\tviewwindowy; \nbyte*\t\tylookup[MAXHEIGHT]; \nint\t\tcolumnofs[MAXWIDTH]; \n\n// Color tables for different players,\n//  translate a limited part to another\n//  (color ramps used for  suit colors).\n//\nbyte\t\ttranslations[3][256];\t\n \n// Backing buffer containing the bezel drawn around the screen and \n// surrounding background.\n\nstatic byte *background_buffer = NULL;\n\n\n//\n// R_DrawColumn\n// Source is the top of the column to scale.\n//\nlighttable_t*\t\tdc_colormap; \nint\t\t\tdc_x; \nint\t\t\tdc_yl; \nint\t\t\tdc_yh; \nfixed_t\t\t\tdc_iscale; \nfixed_t\t\t\tdc_texturemid;\n\n// first pixel in a column (possibly virtual) \nbyte*\t\t\tdc_source;\t\t\n\n// just for profiling \nint\t\t\tdccount;\n\n//\n// A column is a vertical slice/span from a wall texture that,\n//  given the DOOM style restrictions on the view orientation,\n//  will always have constant z depth.\n// Thus a special case loop for very fast rendering can\n//  be used. It has also been used with Wolfenstein 3D.\n// \nvoid R_DrawColumn (void) \n{ \n    int\t\t\tcount; \n    byte*\t\tdest; \n    fixed_t\t\tfrac;\n    fixed_t\t\tfracstep;\t \n \n    count = dc_yh - dc_yl; \n\n    // Zero length, column does not exceed a pixel.\n    if (count < 0) \n\treturn; \n\t\t\t\t \n#ifdef RANGECHECK \n    if ((unsigned)dc_x >= SCREENWIDTH\n\t|| dc_yl < 0\n\t|| dc_yh >= SCREENHEIGHT) \n\tI_Error (\"R_DrawColumn: %i to %i at %i\", dc_yl, dc_yh, dc_x); \n#endif \n\n    // Framebuffer destination address.\n    // Use ylookup LUT to avoid multiply with ScreenWidth.\n    // Use columnofs LUT for subwindows? \n    dest = ylookup[dc_yl] + columnofs[dc_x];  \n\n    // Determine scaling,\n    //  which is the only mapping to be done.\n    fracstep = dc_iscale; \n    frac = dc_texturemid + (dc_yl-centery)*fracstep; \n\n    // Inner loop that does the actual texture mapping,\n    //  e.g. a DDA-lile scaling.\n    // This is as fast as it gets.\n    do \n    {\n\t// Re-map color indices from wall texture column\n\t//  using a lighting/special effects LUT.\n\t*dest = dc_colormap[dc_source[(frac>>FRACBITS)&127]];\n\t\n\tdest += SCREENWIDTH; \n\tfrac += fracstep;\n\t\n    } while (count--); \n} \n\n\n\n// UNUSED.\n// Loop unrolled.\n#if 0\nvoid R_DrawColumn (void) \n{ \n    int\t\t\tcount; \n    byte*\t\tsource;\n    byte*\t\tdest;\n    byte*\t\tcolormap;\n    \n    unsigned\t\tfrac;\n    unsigned\t\tfracstep;\n    unsigned\t\tfracstep2;\n    unsigned\t\tfracstep3;\n    unsigned\t\tfracstep4;\t \n \n    count = dc_yh - dc_yl + 1; \n\n    source = dc_source;\n    colormap = dc_colormap;\t\t \n    dest = ylookup[dc_yl] + columnofs[dc_x];  \n\t \n    fracstep = dc_iscale<<9; \n    frac = (dc_texturemid + (dc_yl-centery)*dc_iscale)<<9; \n \n    fracstep2 = fracstep+fracstep;\n    fracstep3 = fracstep2+fracstep;\n    fracstep4 = fracstep3+fracstep;\n\t\n    while (count >= 8) \n    { \n\tdest[0] = colormap[source[frac>>25]]; \n\tdest[SCREENWIDTH] = colormap[source[(frac+fracstep)>>25]]; \n\tdest[SCREENWIDTH*2] = colormap[source[(frac+fracstep2)>>25]]; \n\tdest[SCREENWIDTH*3] = colormap[source[(frac+fracstep3)>>25]];\n\t\n\tfrac += fracstep4; \n\n\tdest[SCREENWIDTH*4] = colormap[source[frac>>25]]; \n\tdest[SCREENWIDTH*5] = colormap[source[(frac+fracstep)>>25]]; \n\tdest[SCREENWIDTH*6] = colormap[source[(frac+fracstep2)>>25]]; \n\tdest[SCREENWIDTH*7] = colormap[source[(frac+fracstep3)>>25]]; \n\n\tfrac += fracstep4; \n\tdest += SCREENWIDTH*8; \n\tcount -= 8;\n    } \n\t\n    while (count > 0)\n    { \n\t*dest = colormap[source[frac>>25]]; \n\tdest += SCREENWIDTH; \n\tfrac += fracstep; \n\tcount--;\n    } \n}\n#endif\n\n\nvoid R_DrawColumnLow (void) \n{ \n    int\t\t\tcount; \n    byte*\t\tdest; \n    byte*\t\tdest2;\n    fixed_t\t\tfrac;\n    fixed_t\t\tfracstep;\t \n    int                 x;\n \n    count = dc_yh - dc_yl; \n\n    // Zero length.\n    if (count < 0) \n\treturn; \n\t\t\t\t \n#ifdef RANGECHECK \n    if ((unsigned)dc_x >= SCREENWIDTH\n\t|| dc_yl < 0\n\t|| dc_yh >= SCREENHEIGHT)\n    {\n\t\n\tI_Error (\"R_DrawColumn: %i to %i at %i\", dc_yl, dc_yh, dc_x);\n    }\n    //\tdccount++; \n#endif \n    // Blocky mode, need to multiply by 2.\n    x = dc_x << 1;\n    \n    dest = ylookup[dc_yl] + columnofs[x];\n    dest2 = ylookup[dc_yl] + columnofs[x+1];\n    \n    fracstep = dc_iscale; \n    frac = dc_texturemid + (dc_yl-centery)*fracstep;\n    \n    do \n    {\n\t// Hack. Does not work corretly.\n\t*dest2 = *dest = dc_colormap[dc_source[(frac>>FRACBITS)&127]];\n\tdest += SCREENWIDTH;\n\tdest2 += SCREENWIDTH;\n\tfrac += fracstep; \n\n    } while (count--);\n}\n\n\n//\n// Spectre/Invisibility.\n//\n#define FUZZTABLE\t\t50 \n#define FUZZOFF\t(SCREENWIDTH)\n\n\nint\tfuzzoffset[FUZZTABLE] =\n{\n    FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,\n    FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,\n    FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,\n    FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,\n    FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,\n    FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,\n    FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF \n}; \n\nint\tfuzzpos = 0; \n\n\n//\n// Framebuffer postprocessing.\n// Creates a fuzzy image by copying pixels\n//  from adjacent ones to left and right.\n// Used with an all black colormap, this\n//  could create the SHADOW effect,\n//  i.e. spectres and invisible players.\n//\nvoid R_DrawFuzzColumn (void) \n{ \n    int\t\t\tcount; \n    byte*\t\tdest; \n    fixed_t\t\tfrac;\n    fixed_t\t\tfracstep;\t \n\n    // Adjust borders. Low... \n    if (!dc_yl) \n\tdc_yl = 1;\n\n    // .. and high.\n    if (dc_yh == viewheight-1) \n\tdc_yh = viewheight - 2; \n\t\t \n    count = dc_yh - dc_yl; \n\n    // Zero length.\n    if (count < 0) \n\treturn; \n\n#ifdef RANGECHECK \n    if ((unsigned)dc_x >= SCREENWIDTH\n\t|| dc_yl < 0 || dc_yh >= SCREENHEIGHT)\n    {\n\tI_Error (\"R_DrawFuzzColumn: %i to %i at %i\",\n\t\t dc_yl, dc_yh, dc_x);\n    }\n#endif\n    \n    dest = ylookup[dc_yl] + columnofs[dc_x];\n\n    // Looks familiar.\n    fracstep = dc_iscale; \n    frac = dc_texturemid + (dc_yl-centery)*fracstep; \n\n    // Looks like an attempt at dithering,\n    //  using the colormap #6 (of 0-31, a bit\n    //  brighter than average).\n    do \n    {\n\t// Lookup framebuffer, and retrieve\n\t//  a pixel that is either one column\n\t//  left or right of the current one.\n\t// Add index from colormap to index.\n\t*dest = colormaps[6*256+dest[fuzzoffset[fuzzpos]]]; \n\n\t// Clamp table lookup index.\n\tif (++fuzzpos == FUZZTABLE) \n\t    fuzzpos = 0;\n\t\n\tdest += SCREENWIDTH;\n\n\tfrac += fracstep; \n    } while (count--); \n} \n\n// low detail mode version\n \nvoid R_DrawFuzzColumnLow (void) \n{ \n    int\t\t\tcount; \n    byte*\t\tdest; \n    byte*\t\tdest2; \n    fixed_t\t\tfrac;\n    fixed_t\t\tfracstep;\t \n    int x;\n\n    // Adjust borders. Low... \n    if (!dc_yl) \n\tdc_yl = 1;\n\n    // .. and high.\n    if (dc_yh == viewheight-1) \n\tdc_yh = viewheight - 2; \n\t\t \n    count = dc_yh - dc_yl; \n\n    // Zero length.\n    if (count < 0) \n\treturn; \n\n    // low detail mode, need to multiply by 2\n    \n    x = dc_x << 1;\n    \n#ifdef RANGECHECK \n    if ((unsigned)x >= SCREENWIDTH\n\t|| dc_yl < 0 || dc_yh >= SCREENHEIGHT)\n    {\n\tI_Error (\"R_DrawFuzzColumn: %i to %i at %i\",\n\t\t dc_yl, dc_yh, dc_x);\n    }\n#endif\n    \n    dest = ylookup[dc_yl] + columnofs[x];\n    dest2 = ylookup[dc_yl] + columnofs[x+1];\n\n    // Looks familiar.\n    fracstep = dc_iscale; \n    frac = dc_texturemid + (dc_yl-centery)*fracstep; \n\n    // Looks like an attempt at dithering,\n    //  using the colormap #6 (of 0-31, a bit\n    //  brighter than average).\n    do \n    {\n\t// Lookup framebuffer, and retrieve\n\t//  a pixel that is either one column\n\t//  left or right of the current one.\n\t// Add index from colormap to index.\n\t*dest = colormaps[6*256+dest[fuzzoffset[fuzzpos]]]; \n\t*dest2 = colormaps[6*256+dest2[fuzzoffset[fuzzpos]]]; \n\n\t// Clamp table lookup index.\n\tif (++fuzzpos == FUZZTABLE) \n\t    fuzzpos = 0;\n\t\n\tdest += SCREENWIDTH;\n\tdest2 += SCREENWIDTH;\n\n\tfrac += fracstep; \n    } while (count--); \n} \n \n  \n  \n \n\n//\n// R_DrawTranslatedColumn\n// Used to draw player sprites\n//  with the green colorramp mapped to others.\n// Could be used with different translation\n//  tables, e.g. the lighter colored version\n//  of the BaronOfHell, the HellKnight, uses\n//  identical sprites, kinda brightened up.\n//\nbyte*\tdc_translation;\nbyte*\ttranslationtables;\n\nvoid R_DrawTranslatedColumn (void) \n{ \n    int\t\t\tcount; \n    byte*\t\tdest; \n    fixed_t\t\tfrac;\n    fixed_t\t\tfracstep;\t \n \n    count = dc_yh - dc_yl; \n    if (count < 0) \n\treturn; \n\t\t\t\t \n#ifdef RANGECHECK \n    if ((unsigned)dc_x >= SCREENWIDTH\n\t|| dc_yl < 0\n\t|| dc_yh >= SCREENHEIGHT)\n    {\n\tI_Error ( \"R_DrawColumn: %i to %i at %i\",\n\t\t  dc_yl, dc_yh, dc_x);\n    }\n    \n#endif \n\n\n    dest = ylookup[dc_yl] + columnofs[dc_x]; \n\n    // Looks familiar.\n    fracstep = dc_iscale; \n    frac = dc_texturemid + (dc_yl-centery)*fracstep; \n\n    // Here we do an additional index re-mapping.\n    do \n    {\n\t// Translation tables are used\n\t//  to map certain colorramps to other ones,\n\t//  used with PLAY sprites.\n\t// Thus the \"green\" ramp of the player 0 sprite\n\t//  is mapped to gray, red, black/indigo. \n\t*dest = dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]];\n\tdest += SCREENWIDTH;\n\t\n\tfrac += fracstep; \n    } while (count--); \n} \n\nvoid R_DrawTranslatedColumnLow (void) \n{ \n    int\t\t\tcount; \n    byte*\t\tdest; \n    byte*\t\tdest2; \n    fixed_t\t\tfrac;\n    fixed_t\t\tfracstep;\t \n    int                 x;\n \n    count = dc_yh - dc_yl; \n    if (count < 0) \n\treturn; \n\n    // low detail, need to scale by 2\n    x = dc_x << 1;\n\t\t\t\t \n#ifdef RANGECHECK \n    if ((unsigned)x >= SCREENWIDTH\n\t|| dc_yl < 0\n\t|| dc_yh >= SCREENHEIGHT)\n    {\n\tI_Error ( \"R_DrawColumn: %i to %i at %i\",\n\t\t  dc_yl, dc_yh, x);\n    }\n    \n#endif \n\n\n    dest = ylookup[dc_yl] + columnofs[x]; \n    dest2 = ylookup[dc_yl] + columnofs[x+1]; \n\n    // Looks familiar.\n    fracstep = dc_iscale; \n    frac = dc_texturemid + (dc_yl-centery)*fracstep; \n\n    // Here we do an additional index re-mapping.\n    do \n    {\n\t// Translation tables are used\n\t//  to map certain colorramps to other ones,\n\t//  used with PLAY sprites.\n\t// Thus the \"green\" ramp of the player 0 sprite\n\t//  is mapped to gray, red, black/indigo. \n\t*dest = dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]];\n\t*dest2 = dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]];\n\tdest += SCREENWIDTH;\n\tdest2 += SCREENWIDTH;\n\t\n\tfrac += fracstep; \n    } while (count--); \n} \n\n\n\n\n//\n// R_InitTranslationTables\n// Creates the translation tables to map\n//  the green color ramp to gray, brown, red.\n// Assumes a given structure of the PLAYPAL.\n// Could be read from a lump instead.\n//\nvoid R_InitTranslationTables (void)\n{\n    int\t\ti;\n\t\n    translationtables = Z_Malloc (256*3, PU_STATIC, 0);\n    \n    // translate just the 16 green colors\n    for (i=0 ; i<256 ; i++)\n    {\n\tif (i >= 0x70 && i<= 0x7f)\n\t{\n\t    // map green ramp to gray, brown, red\n\t    translationtables[i] = 0x60 + (i&0xf);\n\t    translationtables [i+256] = 0x40 + (i&0xf);\n\t    translationtables [i+512] = 0x20 + (i&0xf);\n\t}\n\telse\n\t{\n\t    // Keep all other colors as is.\n\t    translationtables[i] = translationtables[i+256] \n\t\t= translationtables[i+512] = i;\n\t}\n    }\n}\n\n\n\n\n//\n// R_DrawSpan \n// With DOOM style restrictions on view orientation,\n//  the floors and ceilings consist of horizontal slices\n//  or spans with constant z depth.\n// However, rotation around the world z axis is possible,\n//  thus this mapping, while simpler and faster than\n//  perspective correct texture mapping, has to traverse\n//  the texture at an angle in all but a few cases.\n// In consequence, flats are not stored by column (like walls),\n//  and the inner loop has to step in texture space u and v.\n//\nint\t\t\tds_y; \nint\t\t\tds_x1; \nint\t\t\tds_x2;\n\nlighttable_t*\t\tds_colormap; \n\nfixed_t\t\t\tds_xfrac; \nfixed_t\t\t\tds_yfrac; \nfixed_t\t\t\tds_xstep; \nfixed_t\t\t\tds_ystep;\n\n// start of a 64*64 tile image \nbyte*\t\t\tds_source;\t\n\n// just for profiling\nint\t\t\tdscount;\n\n\n//\n// Draws the actual span.\nvoid R_DrawSpan (void) \n{ \n    unsigned int position, step;\n    byte *dest;\n    int count;\n    int spot;\n    unsigned int xtemp, ytemp;\n\n#ifdef RANGECHECK\n    if (ds_x2 < ds_x1\n\t|| ds_x1<0\n\t|| ds_x2>=SCREENWIDTH\n\t|| (unsigned)ds_y>SCREENHEIGHT)\n    {\n\tI_Error( \"R_DrawSpan: %i to %i at %i\",\n\t\t ds_x1,ds_x2,ds_y);\n    }\n//\tdscount++;\n#endif\n\n    // Pack position and step variables into a single 32-bit integer,\n    // with x in the top 16 bits and y in the bottom 16 bits.  For\n    // each 16-bit part, the top 6 bits are the integer part and the\n    // bottom 10 bits are the fractional part of the pixel position.\n\n    position = ((ds_xfrac << 10) & 0xffff0000)\n             | ((ds_yfrac >> 6)  & 0x0000ffff);\n    step = ((ds_xstep << 10) & 0xffff0000)\n         | ((ds_ystep >> 6)  & 0x0000ffff);\n\n    dest = ylookup[ds_y] + columnofs[ds_x1];\n\n    // We do not check for zero spans here?\n    count = ds_x2 - ds_x1;\n\n    do\n    {\n\t// Calculate current texture index in u,v.\n        ytemp = (position >> 4) & 0x0fc0;\n        xtemp = (position >> 26);\n        spot = xtemp | ytemp;\n\n\t// Lookup pixel from flat texture tile,\n\t//  re-index using light/colormap.\n\t*dest++ = ds_colormap[ds_source[spot]];\n\n        position += step;\n\n    } while (count--);\n}\n\n\n\n// UNUSED.\n// Loop unrolled by 4.\n#if 0\nvoid R_DrawSpan (void) \n{ \n    unsigned\tposition, step;\n\n    byte*\tsource;\n    byte*\tcolormap;\n    byte*\tdest;\n    \n    unsigned\tcount;\n    usingned\tspot; \n    unsigned\tvalue;\n    unsigned\ttemp;\n    unsigned\txtemp;\n    unsigned\tytemp;\n\t\t\n    position = ((ds_xfrac<<10)&0xffff0000) | ((ds_yfrac>>6)&0xffff);\n    step = ((ds_xstep<<10)&0xffff0000) | ((ds_ystep>>6)&0xffff);\n\t\t\n    source = ds_source;\n    colormap = ds_colormap;\n    dest = ylookup[ds_y] + columnofs[ds_x1];\t \n    count = ds_x2 - ds_x1 + 1; \n\t\n    while (count >= 4) \n    { \n\tytemp = position>>4;\n\tytemp = ytemp & 4032;\n\txtemp = position>>26;\n\tspot = xtemp | ytemp;\n\tposition += step;\n\tdest[0] = colormap[source[spot]]; \n\n\tytemp = position>>4;\n\tytemp = ytemp & 4032;\n\txtemp = position>>26;\n\tspot = xtemp | ytemp;\n\tposition += step;\n\tdest[1] = colormap[source[spot]];\n\t\n\tytemp = position>>4;\n\tytemp = ytemp & 4032;\n\txtemp = position>>26;\n\tspot = xtemp | ytemp;\n\tposition += step;\n\tdest[2] = colormap[source[spot]];\n\t\n\tytemp = position>>4;\n\tytemp = ytemp & 4032;\n\txtemp = position>>26;\n\tspot = xtemp | ytemp;\n\tposition += step;\n\tdest[3] = colormap[source[spot]]; \n\t\t\n\tcount -= 4;\n\tdest += 4;\n    } \n    while (count > 0) \n    { \n\tytemp = position>>4;\n\tytemp = ytemp & 4032;\n\txtemp = position>>26;\n\tspot = xtemp | ytemp;\n\tposition += step;\n\t*dest++ = colormap[source[spot]]; \n\tcount--;\n    } \n} \n#endif\n\n\n//\n// Again..\n//\nvoid R_DrawSpanLow (void)\n{\n    unsigned int position, step;\n    unsigned int xtemp, ytemp;\n    byte *dest;\n    int count;\n    int spot;\n\n#ifdef RANGECHECK\n    if (ds_x2 < ds_x1\n\t|| ds_x1<0\n\t|| ds_x2>=SCREENWIDTH\n\t|| (unsigned)ds_y>SCREENHEIGHT)\n    {\n\tI_Error( \"R_DrawSpan: %i to %i at %i\",\n\t\t ds_x1,ds_x2,ds_y);\n    }\n//\tdscount++; \n#endif\n\n    position = ((ds_xfrac << 10) & 0xffff0000)\n             | ((ds_yfrac >> 6)  & 0x0000ffff);\n    step = ((ds_xstep << 10) & 0xffff0000)\n         | ((ds_ystep >> 6)  & 0x0000ffff);\n\n    count = (ds_x2 - ds_x1);\n\n    // Blocky mode, need to multiply by 2.\n    ds_x1 <<= 1;\n    ds_x2 <<= 1;\n\n    dest = ylookup[ds_y] + columnofs[ds_x1];\n\n    do\n    {\n\t// Calculate current texture index in u,v.\n        ytemp = (position >> 4) & 0x0fc0;\n        xtemp = (position >> 26);\n        spot = xtemp | ytemp;\n\n\t// Lowres/blocky mode does it twice,\n\t//  while scale is adjusted appropriately.\n\t*dest++ = ds_colormap[ds_source[spot]];\n\t*dest++ = ds_colormap[ds_source[spot]];\n\n\tposition += step;\n\n    } while (count--);\n}\n\n//\n// R_InitBuffer \n// Creats lookup tables that avoid\n//  multiplies and other hazzles\n//  for getting the framebuffer address\n//  of a pixel to draw.\n//\nvoid\nR_InitBuffer\n( int\t\twidth,\n  int\t\theight ) \n{ \n    int\t\ti; \n\n    // Handle resize,\n    //  e.g. smaller view windows\n    //  with border and/or status bar.\n    viewwindowx = (SCREENWIDTH-width) >> 1; \n\n    // Column offset. For windows.\n    for (i=0 ; i<width ; i++) \n\tcolumnofs[i] = viewwindowx + i;\n\n    // Samw with base row offset.\n    if (width == SCREENWIDTH) \n\tviewwindowy = 0; \n    else \n\tviewwindowy = (SCREENHEIGHT-SBARHEIGHT-height) >> 1; \n\n    // Preclaculate all row offsets.\n    for (i=0 ; i<height ; i++) \n\tylookup[i] = I_VideoBuffer + (i+viewwindowy)*SCREENWIDTH; \n} \n \n \n\n\n//\n// R_FillBackScreen\n// Fills the back screen with a pattern\n//  for variable screen sizes\n// Also draws a beveled edge.\n//\nvoid R_FillBackScreen (void) \n{ \n    byte*\tsrc;\n    byte*\tdest; \n    int\t\tx;\n    int\t\ty; \n    patch_t*\tpatch;\n\n    // DOOM border patch.\n    char       *name1 = DEH_String(\"FLOOR7_2\");\n\n    // DOOM II border patch.\n    char *name2 = DEH_String(\"GRNROCK\");\n\n    char *name;\n\n    // If we are running full screen, there is no need to do any of this,\n    // and the background buffer can be freed if it was previously in use.\n\n    if (scaledviewwidth == SCREENWIDTH)\n    {\n        if (background_buffer != NULL)\n        {\n            Z_Free(background_buffer);\n            background_buffer = NULL;\n        }\n\n\treturn;\n    }\n\n    // Allocate the background buffer if necessary\n\t\n    if (background_buffer == NULL)\n    {\n        background_buffer = Z_Malloc(SCREENWIDTH * (SCREENHEIGHT - SBARHEIGHT),\n                                     PU_STATIC, NULL);\n    }\n\n    if (gamemode == commercial)\n\tname = name2;\n    else\n\tname = name1;\n    \n    src = W_CacheLumpName(name, PU_CACHE); \n    dest = background_buffer;\n\t \n    for (y=0 ; y<SCREENHEIGHT-SBARHEIGHT ; y++) \n    { \n\tfor (x=0 ; x<SCREENWIDTH/64 ; x++) \n\t{ \n\t    memcpy (dest, src+((y&63)<<6), 64); \n\t    dest += 64; \n\t} \n\n\tif (SCREENWIDTH&63) \n\t{ \n\t    memcpy (dest, src+((y&63)<<6), SCREENWIDTH&63); \n\t    dest += (SCREENWIDTH&63); \n\t} \n    } \n     \n    // Draw screen and bezel; this is done to a separate screen buffer.\n\n    V_UseBuffer(background_buffer);\n\n    patch = W_CacheLumpName(DEH_String(\"brdr_t\"),PU_CACHE);\n\n    for (x=0 ; x<scaledviewwidth ; x+=8)\n\tV_DrawPatch(viewwindowx+x, viewwindowy-8, patch);\n    patch = W_CacheLumpName(DEH_String(\"brdr_b\"),PU_CACHE);\n\n    for (x=0 ; x<scaledviewwidth ; x+=8)\n\tV_DrawPatch(viewwindowx+x, viewwindowy+viewheight, patch);\n    patch = W_CacheLumpName(DEH_String(\"brdr_l\"),PU_CACHE);\n\n    for (y=0 ; y<viewheight ; y+=8)\n\tV_DrawPatch(viewwindowx-8, viewwindowy+y, patch);\n    patch = W_CacheLumpName(DEH_String(\"brdr_r\"),PU_CACHE);\n\n    for (y=0 ; y<viewheight ; y+=8)\n\tV_DrawPatch(viewwindowx+scaledviewwidth, viewwindowy+y, patch);\n\n    // Draw beveled edge. \n    V_DrawPatch(viewwindowx-8,\n                viewwindowy-8,\n                W_CacheLumpName(DEH_String(\"brdr_tl\"),PU_CACHE));\n    \n    V_DrawPatch(viewwindowx+scaledviewwidth,\n                viewwindowy-8,\n                W_CacheLumpName(DEH_String(\"brdr_tr\"),PU_CACHE));\n    \n    V_DrawPatch(viewwindowx-8,\n                viewwindowy+viewheight,\n                W_CacheLumpName(DEH_String(\"brdr_bl\"),PU_CACHE));\n    \n    V_DrawPatch(viewwindowx+scaledviewwidth,\n                viewwindowy+viewheight,\n                W_CacheLumpName(DEH_String(\"brdr_br\"),PU_CACHE));\n\n    V_RestoreBuffer();\n} \n \n\n//\n// Copy a screen buffer.\n//\nvoid\nR_VideoErase\n( unsigned\tofs,\n  int\t\tcount ) \n{ \n  // LFB copy.\n  // This might not be a good idea if memcpy\n  //  is not optiomal, e.g. byte by byte on\n  //  a 32bit CPU, as GNU GCC/Linux libc did\n  //  at one point.\n\n    if (background_buffer != NULL)\n    {\n        memcpy(I_VideoBuffer + ofs, background_buffer + ofs, count); \n    }\n} \n\n\n//\n// R_DrawViewBorder\n// Draws the border around the view\n//  for different size windows?\n//\nvoid R_DrawViewBorder (void) \n{ \n    int\t\ttop;\n    int\t\tside;\n    int\t\tofs;\n    int\t\ti; \n \n    if (scaledviewwidth == SCREENWIDTH) \n\treturn; \n  \n    top = ((SCREENHEIGHT-SBARHEIGHT)-viewheight)/2; \n    side = (SCREENWIDTH-scaledviewwidth)/2; \n \n    // copy top and one line of left side \n    R_VideoErase (0, top*SCREENWIDTH+side); \n \n    // copy one line of right side and bottom \n    ofs = (viewheight+top)*SCREENWIDTH-side; \n    R_VideoErase (ofs, top*SCREENWIDTH+side); \n \n    // copy sides using wraparound \n    ofs = top*SCREENWIDTH + SCREENWIDTH-side; \n    side <<= 1;\n    \n    for (i=1 ; i<viewheight ; i++) \n    { \n\tR_VideoErase (ofs, side); \n\tofs += SCREENWIDTH; \n    } \n\n    // ? \n    V_MarkRect (0,0,SCREENWIDTH, SCREENHEIGHT-SBARHEIGHT); \n} \n \n \n"
  },
  {
    "path": "fbdoom/r_draw.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tSystem specific interface stuff.\n//\n\n\n#ifndef __R_DRAW__\n#define __R_DRAW__\n\n\n\n\nextern lighttable_t*\tdc_colormap;\nextern int\t\tdc_x;\nextern int\t\tdc_yl;\nextern int\t\tdc_yh;\nextern fixed_t\t\tdc_iscale;\nextern fixed_t\t\tdc_texturemid;\n\n// first pixel in a column\nextern byte*\t\tdc_source;\t\t\n\n\n// The span blitting interface.\n// Hook in assembler or system specific BLT\n//  here.\nvoid \tR_DrawColumn (void);\nvoid \tR_DrawColumnLow (void);\n\n// The Spectre/Invisibility effect.\nvoid \tR_DrawFuzzColumn (void);\nvoid \tR_DrawFuzzColumnLow (void);\n\n// Draw with color translation tables,\n//  for player sprite rendering,\n//  Green/Red/Blue/Indigo shirts.\nvoid\tR_DrawTranslatedColumn (void);\nvoid\tR_DrawTranslatedColumnLow (void);\n\nvoid\nR_VideoErase\n( unsigned\tofs,\n  int\t\tcount );\n\nextern int\t\tds_y;\nextern int\t\tds_x1;\nextern int\t\tds_x2;\n\nextern lighttable_t*\tds_colormap;\n\nextern fixed_t\t\tds_xfrac;\nextern fixed_t\t\tds_yfrac;\nextern fixed_t\t\tds_xstep;\nextern fixed_t\t\tds_ystep;\n\n// start of a 64*64 tile image\nextern byte*\t\tds_source;\t\t\n\nextern byte*\t\ttranslationtables;\nextern byte*\t\tdc_translation;\n\n\n// Span blitting for rows, floor/ceiling.\n// No Sepctre effect needed.\nvoid \tR_DrawSpan (void);\n\n// Low resolution mode, 160x200?\nvoid \tR_DrawSpanLow (void);\n\n\nvoid\nR_InitBuffer\n( int\t\twidth,\n  int\t\theight );\n\n\n// Initialize color translation tables,\n//  for player rendering etc.\nvoid\tR_InitTranslationTables (void);\n\n\n\n// Rendering function.\nvoid R_FillBackScreen (void);\n\n// If the view size is not full screen, draws a border around it.\nvoid R_DrawViewBorder (void);\n\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/r_local.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tRefresh (R_*) module, global header.\n//\tAll the rendering/drawing stuff is here.\n//\n\n#ifndef __R_LOCAL__\n#define __R_LOCAL__\n\n// Binary Angles, sine/cosine/atan lookups.\n#include \"tables.h\"\n\n// Screen size related parameters.\n#include \"doomdef.h\"\n\n// Include the refresh/render data structs.\n#include \"r_data.h\"\n\n\n\n//\n// Separate header file for each module.\n//\n#include \"r_main.h\"\n#include \"r_bsp.h\"\n#include \"r_segs.h\"\n#include \"r_plane.h\"\n#include \"r_data.h\"\n#include \"r_things.h\"\n#include \"r_draw.h\"\n\n#endif\t\t// __R_LOCAL__\n"
  },
  {
    "path": "fbdoom/r_main.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tRendering main loop and setup functions,\n//\t utility functions (BSP, geometry, trigonometry).\n//\tSee tables.c, too.\n//\n\n\n\n\n\n#include <stdlib.h>\n#include <math.h>\n\n\n#include \"doomdef.h\"\n#include \"d_loop.h\"\n\n#include \"m_bbox.h\"\n#include \"m_menu.h\"\n\n#include \"r_local.h\"\n#include \"r_sky.h\"\n\n\n\n\n\n// Fineangles in the SCREENWIDTH wide window.\n#define FIELDOFVIEW\t\t2048\t\n\n\n\nint\t\t\tviewangleoffset;\n\n// increment every time a check is made\nint\t\t\tvalidcount = 1;\t\t\n\n\nlighttable_t*\t\tfixedcolormap;\nextern lighttable_t**\twalllights;\n\nint\t\t\tcenterx;\nint\t\t\tcentery;\n\nfixed_t\t\t\tcenterxfrac;\nfixed_t\t\t\tcenteryfrac;\nfixed_t\t\t\tprojection;\n\n// just for profiling purposes\nint\t\t\tframecount;\t\n\nint\t\t\tsscount;\nint\t\t\tlinecount;\nint\t\t\tloopcount;\n\nfixed_t\t\t\tviewx;\nfixed_t\t\t\tviewy;\nfixed_t\t\t\tviewz;\n\nangle_t\t\t\tviewangle;\n\nfixed_t\t\t\tviewcos;\nfixed_t\t\t\tviewsin;\n\nplayer_t*\t\tviewplayer;\n\n// 0 = high, 1 = low\nint\t\t\tdetailshift;\t\n\n//\n// precalculated math tables\n//\nangle_t\t\t\tclipangle;\n\n// The viewangletox[viewangle + FINEANGLES/4] lookup\n// maps the visible view angles to screen X coordinates,\n// flattening the arc to a flat projection plane.\n// There will be many angles mapped to the same X. \nint\t\t\tviewangletox[FINEANGLES/2];\n\n// The xtoviewangleangle[] table maps a screen pixel\n// to the lowest viewangle that maps back to x ranges\n// from clipangle to -clipangle.\nangle_t\t\t\txtoviewangle[SCREENWIDTH+1];\n\nlighttable_t*\t\tscalelight[LIGHTLEVELS][MAXLIGHTSCALE];\nlighttable_t*\t\tscalelightfixed[MAXLIGHTSCALE];\nlighttable_t*\t\tzlight[LIGHTLEVELS][MAXLIGHTZ];\n\n// bumped light from gun blasts\nint\t\t\textralight;\t\t\t\n\n\n\nvoid (*colfunc) (void);\nvoid (*basecolfunc) (void);\nvoid (*fuzzcolfunc) (void);\nvoid (*transcolfunc) (void);\nvoid (*spanfunc) (void);\n\n\n\n//\n// R_AddPointToBox\n// Expand a given bbox\n// so that it encloses a given point.\n//\nvoid\nR_AddPointToBox\n( int\t\tx,\n  int\t\ty,\n  fixed_t*\tbox )\n{\n    if (x< box[BOXLEFT])\n\tbox[BOXLEFT] = x;\n    if (x> box[BOXRIGHT])\n\tbox[BOXRIGHT] = x;\n    if (y< box[BOXBOTTOM])\n\tbox[BOXBOTTOM] = y;\n    if (y> box[BOXTOP])\n\tbox[BOXTOP] = y;\n}\n\n\n//\n// R_PointOnSide\n// Traverse BSP (sub) tree,\n//  check point against partition plane.\n// Returns side 0 (front) or 1 (back).\n//\nint\nR_PointOnSide\n( fixed_t\tx,\n  fixed_t\ty,\n  node_t*\tnode )\n{\n    fixed_t\tdx;\n    fixed_t\tdy;\n    fixed_t\tleft;\n    fixed_t\tright;\n\t\n    if (!node->dx)\n    {\n\tif (x <= node->x)\n\t    return node->dy > 0;\n\t\n\treturn node->dy < 0;\n    }\n    if (!node->dy)\n    {\n\tif (y <= node->y)\n\t    return node->dx < 0;\n\t\n\treturn node->dx > 0;\n    }\n\t\n    dx = (x - node->x);\n    dy = (y - node->y);\n\t\n    // Try to quickly decide by looking at sign bits.\n    if ( (node->dy ^ node->dx ^ dx ^ dy)&0x80000000 )\n    {\n\tif  ( (node->dy ^ dx) & 0x80000000 )\n\t{\n\t    // (left is negative)\n\t    return 1;\n\t}\n\treturn 0;\n    }\n\n    left = FixedMul ( node->dy>>FRACBITS , dx );\n    right = FixedMul ( dy , node->dx>>FRACBITS );\n\t\n    if (right < left)\n    {\n\t// front side\n\treturn 0;\n    }\n    // back side\n    return 1;\t\t\t\n}\n\n\nint\nR_PointOnSegSide\n( fixed_t\tx,\n  fixed_t\ty,\n  seg_t*\tline )\n{\n    fixed_t\tlx;\n    fixed_t\tly;\n    fixed_t\tldx;\n    fixed_t\tldy;\n    fixed_t\tdx;\n    fixed_t\tdy;\n    fixed_t\tleft;\n    fixed_t\tright;\n\t\n    lx = line->v1->x;\n    ly = line->v1->y;\n\t\n    ldx = line->v2->x - lx;\n    ldy = line->v2->y - ly;\n\t\n    if (!ldx)\n    {\n\tif (x <= lx)\n\t    return ldy > 0;\n\t\n\treturn ldy < 0;\n    }\n    if (!ldy)\n    {\n\tif (y <= ly)\n\t    return ldx < 0;\n\t\n\treturn ldx > 0;\n    }\n\t\n    dx = (x - lx);\n    dy = (y - ly);\n\t\n    // Try to quickly decide by looking at sign bits.\n    if ( (ldy ^ ldx ^ dx ^ dy)&0x80000000 )\n    {\n\tif  ( (ldy ^ dx) & 0x80000000 )\n\t{\n\t    // (left is negative)\n\t    return 1;\n\t}\n\treturn 0;\n    }\n\n    left = FixedMul ( ldy>>FRACBITS , dx );\n    right = FixedMul ( dy , ldx>>FRACBITS );\n\t\n    if (right < left)\n    {\n\t// front side\n\treturn 0;\n    }\n    // back side\n    return 1;\t\t\t\n}\n\n\n//\n// R_PointToAngle\n// To get a global angle from cartesian coordinates,\n//  the coordinates are flipped until they are in\n//  the first octant of the coordinate system, then\n//  the y (<=x) is scaled and divided by x to get a\n//  tangent (slope) value which is looked up in the\n//  tantoangle[] table.\n\n//\n\n\n\n\nangle_t\nR_PointToAngle\n( fixed_t\tx,\n  fixed_t\ty )\n{\t\n    x -= viewx;\n    y -= viewy;\n    \n    if ( (!x) && (!y) )\n\treturn 0;\n\n    if (x>= 0)\n    {\n\t// x >=0\n\tif (y>= 0)\n\t{\n\t    // y>= 0\n\n\t    if (x>y)\n\t    {\n\t\t// octant 0\n\t\treturn tantoangle[ SlopeDiv(y,x)];\n\t    }\n\t    else\n\t    {\n\t\t// octant 1\n\t\treturn ANG90-1-tantoangle[ SlopeDiv(x,y)];\n\t    }\n\t}\n\telse\n\t{\n\t    // y<0\n\t    y = -y;\n\n\t    if (x>y)\n\t    {\n\t\t// octant 8\n\t\treturn -tantoangle[SlopeDiv(y,x)];\n\t    }\n\t    else\n\t    {\n\t\t// octant 7\n\t\treturn ANG270+tantoangle[ SlopeDiv(x,y)];\n\t    }\n\t}\n    }\n    else\n    {\n\t// x<0\n\tx = -x;\n\n\tif (y>= 0)\n\t{\n\t    // y>= 0\n\t    if (x>y)\n\t    {\n\t\t// octant 3\n\t\treturn ANG180-1-tantoangle[ SlopeDiv(y,x)];\n\t    }\n\t    else\n\t    {\n\t\t// octant 2\n\t\treturn ANG90+ tantoangle[ SlopeDiv(x,y)];\n\t    }\n\t}\n\telse\n\t{\n\t    // y<0\n\t    y = -y;\n\n\t    if (x>y)\n\t    {\n\t\t// octant 4\n\t\treturn ANG180+tantoangle[ SlopeDiv(y,x)];\n\t    }\n\t    else\n\t    {\n\t\t // octant 5\n\t\treturn ANG270-1-tantoangle[ SlopeDiv(x,y)];\n\t    }\n\t}\n    }\n    return 0;\n}\n\n\nangle_t\nR_PointToAngle2\n( fixed_t\tx1,\n  fixed_t\ty1,\n  fixed_t\tx2,\n  fixed_t\ty2 )\n{\t\n    viewx = x1;\n    viewy = y1;\n    \n    return R_PointToAngle (x2, y2);\n}\n\n\nfixed_t\nR_PointToDist\n( fixed_t\tx,\n  fixed_t\ty )\n{\n    int\t\tangle;\n    fixed_t\tdx;\n    fixed_t\tdy;\n    fixed_t\ttemp;\n    fixed_t\tdist;\n    fixed_t     frac;\n\t\n    dx = abs(x - viewx);\n    dy = abs(y - viewy);\n\t\n    if (dy>dx)\n    {\n\ttemp = dx;\n\tdx = dy;\n\tdy = temp;\n    }\n\n    // Fix crashes in udm1.wad\n\n    if (dx != 0)\n    {\n        frac = FixedDiv(dy, dx);\n    }\n    else\n    {\n\tfrac = 0;\n    }\n\t\n    angle = (tantoangle[frac>>DBITS]+ANG90) >> ANGLETOFINESHIFT;\n\n    // use as cosine\n    dist = FixedDiv (dx, finesine[angle] );\t\n\t\n    return dist;\n}\n\n\n\n\n//\n// R_InitPointToAngle\n//\nvoid R_InitPointToAngle (void)\n{\n    // UNUSED - now getting from tables.c\n#if 0\n    int\ti;\n    long\tt;\n    float\tf;\n//\n// slope (tangent) to angle lookup\n//\n    for (i=0 ; i<=SLOPERANGE ; i++)\n    {\n\tf = atan( (float)i/SLOPERANGE )/(3.141592657*2);\n\tt = 0xffffffff*f;\n\ttantoangle[i] = t;\n    }\n#endif\n}\n\n\n//\n// R_ScaleFromGlobalAngle\n// Returns the texture mapping scale\n//  for the current line (horizontal span)\n//  at the given angle.\n// rw_distance must be calculated first.\n//\nfixed_t R_ScaleFromGlobalAngle (angle_t visangle)\n{\n    fixed_t\t\tscale;\n    angle_t\t\tanglea;\n    angle_t\t\tangleb;\n    int\t\t\tsinea;\n    int\t\t\tsineb;\n    fixed_t\t\tnum;\n    int\t\t\tden;\n\n    // UNUSED\n#if 0\n{\n    fixed_t\t\tdist;\n    fixed_t\t\tz;\n    fixed_t\t\tsinv;\n    fixed_t\t\tcosv;\n\t\n    sinv = finesine[(visangle-rw_normalangle)>>ANGLETOFINESHIFT];\t\n    dist = FixedDiv (rw_distance, sinv);\n    cosv = finecosine[(viewangle-visangle)>>ANGLETOFINESHIFT];\n    z = abs(FixedMul (dist, cosv));\n    scale = FixedDiv(projection, z);\n    return scale;\n}\n#endif\n\n    anglea = ANG90 + (visangle-viewangle);\n    angleb = ANG90 + (visangle-rw_normalangle);\n\n    // both sines are allways positive\n    sinea = finesine[anglea>>ANGLETOFINESHIFT];\t\n    sineb = finesine[angleb>>ANGLETOFINESHIFT];\n    num = FixedMul(projection,sineb)<<detailshift;\n    den = FixedMul(rw_distance,sinea);\n\n    if (den > num>>16)\n    {\n\tscale = FixedDiv (num, den);\n\n\tif (scale > 64*FRACUNIT)\n\t    scale = 64*FRACUNIT;\n\telse if (scale < 256)\n\t    scale = 256;\n    }\n    else\n\tscale = 64*FRACUNIT;\n\t\n    return scale;\n}\n\n\n\n//\n// R_InitTables\n//\nvoid R_InitTables (void)\n{\n    // UNUSED: now getting from tables.c\n#if 0\n    int\t\ti;\n    float\ta;\n    float\tfv;\n    int\t\tt;\n    \n    // viewangle tangent table\n    for (i=0 ; i<FINEANGLES/2 ; i++)\n    {\n\ta = (i-FINEANGLES/4+0.5)*PI*2/FINEANGLES;\n\tfv = FRACUNIT*tan (a);\n\tt = fv;\n\tfinetangent[i] = t;\n    }\n    \n    // finesine table\n    for (i=0 ; i<5*FINEANGLES/4 ; i++)\n    {\n\t// OPTIMIZE: mirror...\n\ta = (i+0.5)*PI*2/FINEANGLES;\n\tt = FRACUNIT*sin (a);\n\tfinesine[i] = t;\n    }\n#endif\n\n}\n\n\n\n//\n// R_InitTextureMapping\n//\nvoid R_InitTextureMapping (void)\n{\n    int\t\t\ti;\n    int\t\t\tx;\n    int\t\t\tt;\n    fixed_t\t\tfocallength;\n    \n    // Use tangent table to generate viewangletox:\n    //  viewangletox will give the next greatest x\n    //  after the view angle.\n    //\n    // Calc focallength\n    //  so FIELDOFVIEW angles covers SCREENWIDTH.\n    focallength = FixedDiv (centerxfrac,\n\t\t\t    finetangent[FINEANGLES/4+FIELDOFVIEW/2] );\n\t\n    for (i=0 ; i<FINEANGLES/2 ; i++)\n    {\n\tif (finetangent[i] > FRACUNIT*2)\n\t    t = -1;\n\telse if (finetangent[i] < -FRACUNIT*2)\n\t    t = viewwidth+1;\n\telse\n\t{\n\t    t = FixedMul (finetangent[i], focallength);\n\t    t = (centerxfrac - t+FRACUNIT-1)>>FRACBITS;\n\n\t    if (t < -1)\n\t\tt = -1;\n\t    else if (t>viewwidth+1)\n\t\tt = viewwidth+1;\n\t}\n\tviewangletox[i] = t;\n    }\n    \n    // Scan viewangletox[] to generate xtoviewangle[]:\n    //  xtoviewangle will give the smallest view angle\n    //  that maps to x.\t\n    for (x=0;x<=viewwidth;x++)\n    {\n\ti = 0;\n\twhile (viewangletox[i]>x)\n\t    i++;\n\txtoviewangle[x] = (i<<ANGLETOFINESHIFT)-ANG90;\n    }\n    \n    // Take out the fencepost cases from viewangletox.\n    for (i=0 ; i<FINEANGLES/2 ; i++)\n    {\n\tt = FixedMul (finetangent[i], focallength);\n\tt = centerx - t;\n\t\n\tif (viewangletox[i] == -1)\n\t    viewangletox[i] = 0;\n\telse if (viewangletox[i] == viewwidth+1)\n\t    viewangletox[i]  = viewwidth;\n    }\n\t\n    clipangle = xtoviewangle[0];\n}\n\n\n\n//\n// R_InitLightTables\n// Only inits the zlight table,\n//  because the scalelight table changes with view size.\n//\n#define DISTMAP\t\t2\n\nvoid R_InitLightTables (void)\n{\n    int\t\ti;\n    int\t\tj;\n    int\t\tlevel;\n    int\t\tstartmap; \t\n    int\t\tscale;\n    \n    // Calculate the light levels to use\n    //  for each level / distance combination.\n    for (i=0 ; i< LIGHTLEVELS ; i++)\n    {\n\tstartmap = ((LIGHTLEVELS-1-i)*2)*NUMCOLORMAPS/LIGHTLEVELS;\n\tfor (j=0 ; j<MAXLIGHTZ ; j++)\n\t{\n\t    scale = FixedDiv ((SCREENWIDTH/2*FRACUNIT), (j+1)<<LIGHTZSHIFT);\n\t    scale >>= LIGHTSCALESHIFT;\n\t    level = startmap - scale/DISTMAP;\n\t    \n\t    if (level < 0)\n\t\tlevel = 0;\n\n\t    if (level >= NUMCOLORMAPS)\n\t\tlevel = NUMCOLORMAPS-1;\n\n\t    zlight[i][j] = colormaps + level*256;\n\t}\n    }\n}\n\n\n\n//\n// R_SetViewSize\n// Do not really change anything here,\n//  because it might be in the middle of a refresh.\n// The change will take effect next refresh.\n//\nboolean\t\tsetsizeneeded;\nint\t\tsetblocks;\nint\t\tsetdetail;\n\n\nvoid\nR_SetViewSize\n( int\t\tblocks,\n  int\t\tdetail )\n{\n    setsizeneeded = true;\n    setblocks = blocks;\n    setdetail = detail;\n}\n\n\n//\n// R_ExecuteSetViewSize\n//\nvoid R_ExecuteSetViewSize (void)\n{\n    fixed_t\tcosadj;\n    fixed_t\tdy;\n    int\t\ti;\n    int\t\tj;\n    int\t\tlevel;\n    int\t\tstartmap; \t\n\n    setsizeneeded = false;\n\n    if (setblocks == 11)\n    {\n\tscaledviewwidth = SCREENWIDTH;\n\tviewheight = SCREENHEIGHT;\n    }\n    else\n    {\n\tscaledviewwidth = setblocks*32;\n\tviewheight = (setblocks*168/10)&~7;\n    }\n    \n    detailshift = setdetail;\n    viewwidth = scaledviewwidth>>detailshift;\n\t\n    centery = viewheight/2;\n    centerx = viewwidth/2;\n    centerxfrac = centerx<<FRACBITS;\n    centeryfrac = centery<<FRACBITS;\n    projection = centerxfrac;\n\n    if (!detailshift)\n    {\n\tcolfunc = basecolfunc = R_DrawColumn;\n\tfuzzcolfunc = R_DrawFuzzColumn;\n\ttranscolfunc = R_DrawTranslatedColumn;\n\tspanfunc = R_DrawSpan;\n    }\n    else\n    {\n\tcolfunc = basecolfunc = R_DrawColumnLow;\n\tfuzzcolfunc = R_DrawFuzzColumnLow;\n\ttranscolfunc = R_DrawTranslatedColumnLow;\n\tspanfunc = R_DrawSpanLow;\n    }\n\n    R_InitBuffer (scaledviewwidth, viewheight);\n\t\n    R_InitTextureMapping ();\n    \n    // psprite scales\n    pspritescale = FRACUNIT*viewwidth/SCREENWIDTH;\n    pspriteiscale = FRACUNIT*SCREENWIDTH/viewwidth;\n    \n    // thing clipping\n    for (i=0 ; i<viewwidth ; i++)\n\tscreenheightarray[i] = viewheight;\n    \n    // planes\n    for (i=0 ; i<viewheight ; i++)\n    {\n\tdy = ((i-viewheight/2)<<FRACBITS)+FRACUNIT/2;\n\tdy = abs(dy);\n\tyslope[i] = FixedDiv ( (viewwidth<<detailshift)/2*FRACUNIT, dy);\n    }\n\t\n    for (i=0 ; i<viewwidth ; i++)\n    {\n\tcosadj = abs(finecosine[xtoviewangle[i]>>ANGLETOFINESHIFT]);\n\tdistscale[i] = FixedDiv (FRACUNIT,cosadj);\n    }\n    \n    // Calculate the light levels to use\n    //  for each level / scale combination.\n    for (i=0 ; i< LIGHTLEVELS ; i++)\n    {\n\tstartmap = ((LIGHTLEVELS-1-i)*2)*NUMCOLORMAPS/LIGHTLEVELS;\n\tfor (j=0 ; j<MAXLIGHTSCALE ; j++)\n\t{\n\t    level = startmap - j*SCREENWIDTH/(viewwidth<<detailshift)/DISTMAP;\n\t    \n\t    if (level < 0)\n\t\tlevel = 0;\n\n\t    if (level >= NUMCOLORMAPS)\n\t\tlevel = NUMCOLORMAPS-1;\n\n\t    scalelight[i][j] = colormaps + level*256;\n\t}\n    }\n}\n\n\n\n//\n// R_Init\n//\n\n\n\nvoid R_Init (void)\n{\n    R_InitData ();\n    printf (\".\");\n    R_InitPointToAngle ();\n    printf (\".\");\n    R_InitTables ();\n    // viewwidth / viewheight / detailLevel are set by the defaults\n    printf (\".\");\n\n    R_SetViewSize (screenblocks, detailLevel);\n    R_InitPlanes ();\n    printf (\".\");\n    R_InitLightTables ();\n    printf (\".\");\n    R_InitSkyMap ();\n    R_InitTranslationTables ();\n    printf (\".\");\n\t\n    framecount = 0;\n}\n\n\n//\n// R_PointInSubsector\n//\nsubsector_t*\nR_PointInSubsector\n( fixed_t\tx,\n  fixed_t\ty )\n{\n    node_t*\tnode;\n    int\t\tside;\n    int\t\tnodenum;\n\n    // single subsector is a special case\n    if (!numnodes)\t\t\t\t\n\treturn subsectors;\n\t\t\n    nodenum = numnodes-1;\n\n    while (! (nodenum & NF_SUBSECTOR) )\n    {\n\tnode = &nodes[nodenum];\n\tside = R_PointOnSide (x, y, node);\n\tnodenum = node->children[side];\n    }\n\t\n    return &subsectors[nodenum & ~NF_SUBSECTOR];\n}\n\n\n\n//\n// R_SetupFrame\n//\nvoid R_SetupFrame (player_t* player)\n{\t\t\n    int\t\ti;\n    \n    viewplayer = player;\n    viewx = player->mo->x;\n    viewy = player->mo->y;\n    viewangle = player->mo->angle + viewangleoffset;\n    extralight = player->extralight;\n\n    viewz = player->viewz;\n    \n    viewsin = finesine[viewangle>>ANGLETOFINESHIFT];\n    viewcos = finecosine[viewangle>>ANGLETOFINESHIFT];\n\t\n    sscount = 0;\n\t\n    if (player->fixedcolormap)\n    {\n\tfixedcolormap =\n\t    colormaps\n\t    + player->fixedcolormap*256*sizeof(lighttable_t);\n\t\n\twalllights = scalelightfixed;\n\n\tfor (i=0 ; i<MAXLIGHTSCALE ; i++)\n\t    scalelightfixed[i] = fixedcolormap;\n    }\n    else\n\tfixedcolormap = 0;\n\t\t\n    framecount++;\n    validcount++;\n}\n\n\n\n//\n// R_RenderView\n//\nvoid R_RenderPlayerView (player_t* player)\n{\t\n    R_SetupFrame (player);\n\n    // Clear buffers.\n    R_ClearClipSegs ();\n    R_ClearDrawSegs ();\n    R_ClearPlanes ();\n    R_ClearSprites ();\n    \n    // check for new console commands.\n    NetUpdate ();\n\n    // The head node is the last node output.\n    R_RenderBSPNode (numnodes-1);\n    \n    // Check for new console commands.\n    NetUpdate ();\n    \n    R_DrawPlanes ();\n    \n    // Check for new console commands.\n    NetUpdate ();\n    \n    R_DrawMasked ();\n\n    // Check for new console commands.\n    NetUpdate ();\t\t\t\t\n}\n"
  },
  {
    "path": "fbdoom/r_main.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tSystem specific interface stuff.\n//\n\n\n#ifndef __R_MAIN__\n#define __R_MAIN__\n\n#include \"d_player.h\"\n#include \"r_data.h\"\n\n\n\n\n//\n// POV related.\n//\nextern fixed_t\t\tviewcos;\nextern fixed_t\t\tviewsin;\n\nextern int\t\tviewwindowx;\nextern int\t\tviewwindowy;\n\n\n\nextern int\t\tcenterx;\nextern int\t\tcentery;\n\nextern fixed_t\t\tcenterxfrac;\nextern fixed_t\t\tcenteryfrac;\nextern fixed_t\t\tprojection;\n\nextern int\t\tvalidcount;\n\nextern int\t\tlinecount;\nextern int\t\tloopcount;\n\n\n//\n// Lighting LUT.\n// Used for z-depth cuing per column/row,\n//  and other lighting effects (sector ambient, flash).\n//\n\n// Lighting constants.\n// Now why not 32 levels here?\n#define LIGHTLEVELS\t        16\n#define LIGHTSEGSHIFT\t         4\n\n#define MAXLIGHTSCALE\t\t48\n#define LIGHTSCALESHIFT\t\t12\n#define MAXLIGHTZ\t       128\n#define LIGHTZSHIFT\t\t20\n\nextern lighttable_t*\tscalelight[LIGHTLEVELS][MAXLIGHTSCALE];\nextern lighttable_t*\tscalelightfixed[MAXLIGHTSCALE];\nextern lighttable_t*\tzlight[LIGHTLEVELS][MAXLIGHTZ];\n\nextern int\t\textralight;\nextern lighttable_t*\tfixedcolormap;\n\n\n// Number of diminishing brightness levels.\n// There a 0-31, i.e. 32 LUT in the COLORMAP lump.\n#define NUMCOLORMAPS\t\t32\n\n\n// Blocky/low detail mode.\n//B remove this?\n//  0 = high, 1 = low\nextern\tint\t\tdetailshift;\t\n\n\n//\n// Function pointers to switch refresh/drawing functions.\n// Used to select shadow mode etc.\n//\nextern void\t\t(*colfunc) (void);\nextern void\t\t(*transcolfunc) (void);\nextern void\t\t(*basecolfunc) (void);\nextern void\t\t(*fuzzcolfunc) (void);\n// No shadow effects on floors.\nextern void\t\t(*spanfunc) (void);\n\n\n//\n// Utility functions.\nint\nR_PointOnSide\n( fixed_t\tx,\n  fixed_t\ty,\n  node_t*\tnode );\n\nint\nR_PointOnSegSide\n( fixed_t\tx,\n  fixed_t\ty,\n  seg_t*\tline );\n\nangle_t\nR_PointToAngle\n( fixed_t\tx,\n  fixed_t\ty );\n\nangle_t\nR_PointToAngle2\n( fixed_t\tx1,\n  fixed_t\ty1,\n  fixed_t\tx2,\n  fixed_t\ty2 );\n\nfixed_t\nR_PointToDist\n( fixed_t\tx,\n  fixed_t\ty );\n\n\nfixed_t R_ScaleFromGlobalAngle (angle_t visangle);\n\nsubsector_t*\nR_PointInSubsector\n( fixed_t\tx,\n  fixed_t\ty );\n\nvoid\nR_AddPointToBox\n( int\t\tx,\n  int\t\ty,\n  fixed_t*\tbox );\n\n\n\n//\n// REFRESH - the actual rendering functions.\n//\n\n// Called by G_Drawer.\nvoid R_RenderPlayerView (player_t *player);\n\n// Called by startup code.\nvoid R_Init (void);\n\n// Called by M_Responder.\nvoid R_SetViewSize (int blocks, int detail);\n\n#endif\n"
  },
  {
    "path": "fbdoom/r_plane.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tHere is a core component: drawing the floors and ceilings,\n//\t while maintaining a per column clipping list only.\n//\tMoreover, the sky areas have to be determined.\n//\n\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"i_system.h\"\n#include \"z_zone.h\"\n#include \"w_wad.h\"\n\n#include \"doomdef.h\"\n#include \"doomstat.h\"\n\n#include \"r_local.h\"\n#include \"r_sky.h\"\n\n\n\nplanefunction_t\t\tfloorfunc;\nplanefunction_t\t\tceilingfunc;\n\n//\n// opening\n//\n\n// Here comes the obnoxious \"visplane\".\n#define MAXVISPLANES\t128\nvisplane_t\t\tvisplanes[MAXVISPLANES];\nvisplane_t*\t\tlastvisplane;\nvisplane_t*\t\tfloorplane;\nvisplane_t*\t\tceilingplane;\n\n// ?\n#define MAXOPENINGS\tSCREENWIDTH*64\nshort\t\t\topenings[MAXOPENINGS];\nshort*\t\t\tlastopening;\n\n\n//\n// Clip values are the solid pixel bounding the range.\n//  floorclip starts out SCREENHEIGHT\n//  ceilingclip starts out -1\n//\nshort\t\t\tfloorclip[SCREENWIDTH];\nshort\t\t\tceilingclip[SCREENWIDTH];\n\n//\n// spanstart holds the start of a plane span\n// initialized to 0 at start\n//\nint\t\t\tspanstart[SCREENHEIGHT];\nint\t\t\tspanstop[SCREENHEIGHT];\n\n//\n// texture mapping\n//\nlighttable_t**\t\tplanezlight;\nfixed_t\t\t\tplaneheight;\n\nfixed_t\t\t\tyslope[SCREENHEIGHT];\nfixed_t\t\t\tdistscale[SCREENWIDTH];\nfixed_t\t\t\tbasexscale;\nfixed_t\t\t\tbaseyscale;\n\nfixed_t\t\t\tcachedheight[SCREENHEIGHT];\nfixed_t\t\t\tcacheddistance[SCREENHEIGHT];\nfixed_t\t\t\tcachedxstep[SCREENHEIGHT];\nfixed_t\t\t\tcachedystep[SCREENHEIGHT];\n\n\n\n//\n// R_InitPlanes\n// Only at game startup.\n//\nvoid R_InitPlanes (void)\n{\n  // Doh!\n}\n\n\n//\n// R_MapPlane\n//\n// Uses global vars:\n//  planeheight\n//  ds_source\n//  basexscale\n//  baseyscale\n//  viewx\n//  viewy\n//\n// BASIC PRIMITIVE\n//\nvoid\nR_MapPlane\n( int\t\ty,\n  int\t\tx1,\n  int\t\tx2 )\n{\n    angle_t\tangle;\n    fixed_t\tdistance;\n    fixed_t\tlength;\n    unsigned\tindex;\n\t\n#ifdef RANGECHECK\n    if (x2 < x1\n     || x1 < 0\n     || x2 >= viewwidth\n     || y > viewheight)\n    {\n\tI_Error (\"R_MapPlane: %i, %i at %i\",x1,x2,y);\n    }\n#endif\n\n    if (planeheight != cachedheight[y])\n    {\n\tcachedheight[y] = planeheight;\n\tdistance = cacheddistance[y] = FixedMul (planeheight, yslope[y]);\n\tds_xstep = cachedxstep[y] = FixedMul (distance,basexscale);\n\tds_ystep = cachedystep[y] = FixedMul (distance,baseyscale);\n    }\n    else\n    {\n\tdistance = cacheddistance[y];\n\tds_xstep = cachedxstep[y];\n\tds_ystep = cachedystep[y];\n    }\n\t\n    length = FixedMul (distance,distscale[x1]);\n    angle = (viewangle + xtoviewangle[x1])>>ANGLETOFINESHIFT;\n    ds_xfrac = viewx + FixedMul(finecosine[angle], length);\n    ds_yfrac = -viewy - FixedMul(finesine[angle], length);\n\n    if (fixedcolormap)\n\tds_colormap = fixedcolormap;\n    else\n    {\n\tindex = distance >> LIGHTZSHIFT;\n\t\n\tif (index >= MAXLIGHTZ )\n\t    index = MAXLIGHTZ-1;\n\n\tds_colormap = planezlight[index];\n    }\n\t\n    ds_y = y;\n    ds_x1 = x1;\n    ds_x2 = x2;\n\n    // high or low detail\n    spanfunc ();\t\n}\n\n\n//\n// R_ClearPlanes\n// At begining of frame.\n//\nvoid R_ClearPlanes (void)\n{\n    int\t\ti;\n    angle_t\tangle;\n    \n    // opening / clipping determination\n    for (i=0 ; i<viewwidth ; i++)\n    {\n\tfloorclip[i] = viewheight;\n\tceilingclip[i] = -1;\n    }\n\n    lastvisplane = visplanes;\n    lastopening = openings;\n    \n    // texture calculation\n    memset (cachedheight, 0, sizeof(cachedheight));\n\n    // left to right mapping\n    angle = (viewangle-ANG90)>>ANGLETOFINESHIFT;\n\t\n    // scale will be unit scale at SCREENWIDTH/2 distance\n    basexscale = FixedDiv (finecosine[angle],centerxfrac);\n    baseyscale = -FixedDiv (finesine[angle],centerxfrac);\n}\n\n\n\n\n//\n// R_FindPlane\n//\nvisplane_t*\nR_FindPlane\n( fixed_t\theight,\n  int\t\tpicnum,\n  int\t\tlightlevel )\n{\n    visplane_t*\tcheck;\n\t\n    if (picnum == skyflatnum)\n    {\n\theight = 0;\t\t\t// all skys map together\n\tlightlevel = 0;\n    }\n\t\n    for (check=visplanes; check<lastvisplane; check++)\n    {\n\tif (height == check->height\n\t    && picnum == check->picnum\n\t    && lightlevel == check->lightlevel)\n\t{\n\t    break;\n\t}\n    }\n    \n\t\t\t\n    if (check < lastvisplane)\n\treturn check;\n\t\t\n    if (lastvisplane - visplanes == MAXVISPLANES)\n\tI_Error (\"R_FindPlane: no more visplanes\");\n\t\t\n    lastvisplane++;\n\n    check->height = height;\n    check->picnum = picnum;\n    check->lightlevel = lightlevel;\n    check->minx = SCREENWIDTH;\n    check->maxx = -1;\n    \n    memset (check->top,0xff,sizeof(check->top));\n\t\t\n    return check;\n}\n\n\n//\n// R_CheckPlane\n//\nvisplane_t*\nR_CheckPlane\n( visplane_t*\tpl,\n  int\t\tstart,\n  int\t\tstop )\n{\n    int\t\tintrl;\n    int\t\tintrh;\n    int\t\tunionl;\n    int\t\tunionh;\n    int\t\tx;\n\t\n    if (start < pl->minx)\n    {\n\tintrl = pl->minx;\n\tunionl = start;\n    }\n    else\n    {\n\tunionl = pl->minx;\n\tintrl = start;\n    }\n\t\n    if (stop > pl->maxx)\n    {\n\tintrh = pl->maxx;\n\tunionh = stop;\n    }\n    else\n    {\n\tunionh = pl->maxx;\n\tintrh = stop;\n    }\n\n    for (x=intrl ; x<= intrh ; x++)\n\tif (pl->top[x] != 0xff)\n\t    break;\n\n    if (x > intrh)\n    {\n\tpl->minx = unionl;\n\tpl->maxx = unionh;\n\n\t// use the same one\n\treturn pl;\t\t\n    }\n\t\n    // make a new visplane\n    lastvisplane->height = pl->height;\n    lastvisplane->picnum = pl->picnum;\n    lastvisplane->lightlevel = pl->lightlevel;\n    \n    pl = lastvisplane++;\n    pl->minx = start;\n    pl->maxx = stop;\n\n    memset (pl->top,0xff,sizeof(pl->top));\n\t\t\n    return pl;\n}\n\n\n//\n// R_MakeSpans\n//\nvoid\nR_MakeSpans\n( int\t\tx,\n  int\t\tt1,\n  int\t\tb1,\n  int\t\tt2,\n  int\t\tb2 )\n{\n    while (t1 < t2 && t1<=b1)\n    {\n\tR_MapPlane (t1,spanstart[t1],x-1);\n\tt1++;\n    }\n    while (b1 > b2 && b1>=t1)\n    {\n\tR_MapPlane (b1,spanstart[b1],x-1);\n\tb1--;\n    }\n\t\n    while (t2 < t1 && t2<=b2)\n    {\n\tspanstart[t2] = x;\n\tt2++;\n    }\n    while (b2 > b1 && b2>=t2)\n    {\n\tspanstart[b2] = x;\n\tb2--;\n    }\n}\n\n\n\n//\n// R_DrawPlanes\n// At the end of each frame.\n//\nvoid R_DrawPlanes (void)\n{\n    visplane_t*\t\tpl;\n    int\t\t\tlight;\n    int\t\t\tx;\n    int\t\t\tstop;\n    int\t\t\tangle;\n    int                 lumpnum;\n\t\t\t\t\n#ifdef RANGECHECK\n    if (ds_p - drawsegs > MAXDRAWSEGS)\n\tI_Error (\"R_DrawPlanes: drawsegs overflow (%i)\",\n\t\t ds_p - drawsegs);\n    \n    if (lastvisplane - visplanes > MAXVISPLANES)\n\tI_Error (\"R_DrawPlanes: visplane overflow (%i)\",\n\t\t lastvisplane - visplanes);\n    \n    if (lastopening - openings > MAXOPENINGS)\n\tI_Error (\"R_DrawPlanes: opening overflow (%i)\",\n\t\t lastopening - openings);\n#endif\n\n    for (pl = visplanes ; pl < lastvisplane ; pl++)\n    {\n\tif (pl->minx > pl->maxx)\n\t    continue;\n\n\t\n\t// sky flat\n\tif (pl->picnum == skyflatnum)\n\t{\n\t    dc_iscale = pspriteiscale>>detailshift;\n\t    \n\t    // Sky is allways drawn full bright,\n\t    //  i.e. colormaps[0] is used.\n\t    // Because of this hack, sky is not affected\n\t    //  by INVUL inverse mapping.\n\t    dc_colormap = colormaps;\n\t    dc_texturemid = skytexturemid;\n\t    for (x=pl->minx ; x <= pl->maxx ; x++)\n\t    {\n\t\tdc_yl = pl->top[x];\n\t\tdc_yh = pl->bottom[x];\n\n\t\tif (dc_yl <= dc_yh)\n\t\t{\n\t\t    angle = (viewangle + xtoviewangle[x])>>ANGLETOSKYSHIFT;\n\t\t    dc_x = x;\n\t\t    dc_source = R_GetColumn(skytexture, angle);\n\t\t    colfunc ();\n\t\t}\n\t    }\n\t    continue;\n\t}\n\t\n\t// regular flat\n        lumpnum = firstflat + flattranslation[pl->picnum];\n\tds_source = W_CacheLumpNum(lumpnum, PU_STATIC);\n\t\n\tplaneheight = abs(pl->height-viewz);\n\tlight = (pl->lightlevel >> LIGHTSEGSHIFT)+extralight;\n\n\tif (light >= LIGHTLEVELS)\n\t    light = LIGHTLEVELS-1;\n\n\tif (light < 0)\n\t    light = 0;\n\n\tplanezlight = zlight[light];\n\n\tpl->top[pl->maxx+1] = 0xff;\n\tpl->top[pl->minx-1] = 0xff;\n\t\t\n\tstop = pl->maxx + 1;\n\n\tfor (x=pl->minx ; x<= stop ; x++)\n\t{\n\t    R_MakeSpans(x,pl->top[x-1],\n\t\t\tpl->bottom[x-1],\n\t\t\tpl->top[x],\n\t\t\tpl->bottom[x]);\n\t}\n\t\n        W_ReleaseLumpNum(lumpnum);\n    }\n}\n"
  },
  {
    "path": "fbdoom/r_plane.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tRefresh, visplane stuff (floor, ceilings).\n//\n\n\n#ifndef __R_PLANE__\n#define __R_PLANE__\n\n\n#include \"r_data.h\"\n\n\n\n// Visplane related.\nextern  short*\t\tlastopening;\n\n\ntypedef void (*planefunction_t) (int top, int bottom);\n\nextern planefunction_t\tfloorfunc;\nextern planefunction_t\tceilingfunc_t;\n\nextern short\t\tfloorclip[SCREENWIDTH];\nextern short\t\tceilingclip[SCREENWIDTH];\n\nextern fixed_t\t\tyslope[SCREENHEIGHT];\nextern fixed_t\t\tdistscale[SCREENWIDTH];\n\nvoid R_InitPlanes (void);\nvoid R_ClearPlanes (void);\n\nvoid\nR_MapPlane\n( int\t\ty,\n  int\t\tx1,\n  int\t\tx2 );\n\nvoid\nR_MakeSpans\n( int\t\tx,\n  int\t\tt1,\n  int\t\tb1,\n  int\t\tt2,\n  int\t\tb2 );\n\nvoid R_DrawPlanes (void);\n\nvisplane_t*\nR_FindPlane\n( fixed_t\theight,\n  int\t\tpicnum,\n  int\t\tlightlevel );\n\nvisplane_t*\nR_CheckPlane\n( visplane_t*\tpl,\n  int\t\tstart,\n  int\t\tstop );\n\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/r_segs.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tAll the clipping: columns, horizontal spans, sky columns.\n//\n\n\n\n\n\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"i_system.h\"\n\n#include \"doomdef.h\"\n#include \"doomstat.h\"\n\n#include \"r_local.h\"\n#include \"r_sky.h\"\n\n\n// OPTIMIZE: closed two sided lines as single sided\n\n// True if any of the segs textures might be visible.\nboolean\t\tsegtextured;\t\n\n// False if the back side is the same plane.\nboolean\t\tmarkfloor;\t\nboolean\t\tmarkceiling;\n\nboolean\t\tmaskedtexture;\nint\t\ttoptexture;\nint\t\tbottomtexture;\nint\t\tmidtexture;\n\n\nangle_t\t\trw_normalangle;\n// angle to line origin\nint\t\trw_angle1;\t\n\n//\n// regular wall\n//\nint\t\trw_x;\nint\t\trw_stopx;\nangle_t\t\trw_centerangle;\nfixed_t\t\trw_offset;\nfixed_t\t\trw_distance;\nfixed_t\t\trw_scale;\nfixed_t\t\trw_scalestep;\nfixed_t\t\trw_midtexturemid;\nfixed_t\t\trw_toptexturemid;\nfixed_t\t\trw_bottomtexturemid;\n\nint\t\tworldtop;\nint\t\tworldbottom;\nint\t\tworldhigh;\nint\t\tworldlow;\n\nfixed_t\t\tpixhigh;\nfixed_t\t\tpixlow;\nfixed_t\t\tpixhighstep;\nfixed_t\t\tpixlowstep;\n\nfixed_t\t\ttopfrac;\nfixed_t\t\ttopstep;\n\nfixed_t\t\tbottomfrac;\nfixed_t\t\tbottomstep;\n\n\nlighttable_t**\twalllights;\n\nshort*\t\tmaskedtexturecol;\n\n\n\n//\n// R_RenderMaskedSegRange\n//\nvoid\nR_RenderMaskedSegRange\n( drawseg_t*\tds,\n  int\t\tx1,\n  int\t\tx2 )\n{\n    unsigned\tindex;\n    column_t*\tcol;\n    int\t\tlightnum;\n    int\t\ttexnum;\n    \n    // Calculate light table.\n    // Use different light tables\n    //   for horizontal / vertical / diagonal. Diagonal?\n    // OPTIMIZE: get rid of LIGHTSEGSHIFT globally\n    curline = ds->curline;\n    frontsector = curline->frontsector;\n    backsector = curline->backsector;\n    texnum = texturetranslation[curline->sidedef->midtexture];\n\t\n    lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT)+extralight;\n\n    if (curline->v1->y == curline->v2->y)\n\tlightnum--;\n    else if (curline->v1->x == curline->v2->x)\n\tlightnum++;\n\n    if (lightnum < 0)\t\t\n\twalllights = scalelight[0];\n    else if (lightnum >= LIGHTLEVELS)\n\twalllights = scalelight[LIGHTLEVELS-1];\n    else\n\twalllights = scalelight[lightnum];\n\n    maskedtexturecol = ds->maskedtexturecol;\n\n    rw_scalestep = ds->scalestep;\t\t\n    spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep;\n    mfloorclip = ds->sprbottomclip;\n    mceilingclip = ds->sprtopclip;\n    \n    // find positioning\n    if (curline->linedef->flags & ML_DONTPEGBOTTOM)\n    {\n\tdc_texturemid = frontsector->floorheight > backsector->floorheight\n\t    ? frontsector->floorheight : backsector->floorheight;\n\tdc_texturemid = dc_texturemid + textureheight[texnum] - viewz;\n    }\n    else\n    {\n\tdc_texturemid =frontsector->ceilingheight<backsector->ceilingheight\n\t    ? frontsector->ceilingheight : backsector->ceilingheight;\n\tdc_texturemid = dc_texturemid - viewz;\n    }\n    dc_texturemid += curline->sidedef->rowoffset;\n\t\t\t\n    if (fixedcolormap)\n\tdc_colormap = fixedcolormap;\n    \n    // draw the columns\n    for (dc_x = x1 ; dc_x <= x2 ; dc_x++)\n    {\n\t// calculate lighting\n\tif (maskedtexturecol[dc_x] != SHRT_MAX)\n\t{\n\t    if (!fixedcolormap)\n\t    {\n\t\tindex = spryscale>>LIGHTSCALESHIFT;\n\n\t\tif (index >=  MAXLIGHTSCALE )\n\t\t    index = MAXLIGHTSCALE-1;\n\n\t\tdc_colormap = walllights[index];\n\t    }\n\t\t\t\n\t    sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);\n\t    dc_iscale = 0xffffffffu / (unsigned)spryscale;\n\t    \n\t    // draw the texture\n\t    col = (column_t *)( \n\t\t(byte *)R_GetColumn(texnum,maskedtexturecol[dc_x]) -3);\n\t\t\t\n\t    R_DrawMaskedColumn (col);\n\t    maskedtexturecol[dc_x] = SHRT_MAX;\n\t}\n\tspryscale += rw_scalestep;\n    }\n\t\n}\n\n\n\n\n//\n// R_RenderSegLoop\n// Draws zero, one, or two textures (and possibly a masked\n//  texture) for walls.\n// Can draw or mark the starting pixel of floor and ceiling\n//  textures.\n// CALLED: CORE LOOPING ROUTINE.\n//\n#define HEIGHTBITS\t\t12\n#define HEIGHTUNIT\t\t(1<<HEIGHTBITS)\n\nvoid R_RenderSegLoop (void)\n{\n    angle_t\t\tangle;\n    unsigned\t\tindex;\n    int\t\t\tyl;\n    int\t\t\tyh;\n    int\t\t\tmid;\n    fixed_t\t\ttexturecolumn;\n    int\t\t\ttop;\n    int\t\t\tbottom;\n\n    for ( ; rw_x < rw_stopx ; rw_x++)\n    {\n\t// mark floor / ceiling areas\n\tyl = (topfrac+HEIGHTUNIT-1)>>HEIGHTBITS;\n\n\t// no space above wall?\n\tif (yl < ceilingclip[rw_x]+1)\n\t    yl = ceilingclip[rw_x]+1;\n\t\n\tif (markceiling)\n\t{\n\t    top = ceilingclip[rw_x]+1;\n\t    bottom = yl-1;\n\n\t    if (bottom >= floorclip[rw_x])\n\t\tbottom = floorclip[rw_x]-1;\n\n\t    if (top <= bottom)\n\t    {\n\t\tceilingplane->top[rw_x] = top;\n\t\tceilingplane->bottom[rw_x] = bottom;\n\t    }\n\t}\n\t\t\n\tyh = bottomfrac>>HEIGHTBITS;\n\n\tif (yh >= floorclip[rw_x])\n\t    yh = floorclip[rw_x]-1;\n\n\tif (markfloor)\n\t{\n\t    top = yh+1;\n\t    bottom = floorclip[rw_x]-1;\n\t    if (top <= ceilingclip[rw_x])\n\t\ttop = ceilingclip[rw_x]+1;\n\t    if (top <= bottom)\n\t    {\n\t\tfloorplane->top[rw_x] = top;\n\t\tfloorplane->bottom[rw_x] = bottom;\n\t    }\n\t}\n\t\n\t// texturecolumn and lighting are independent of wall tiers\n\tif (segtextured)\n\t{\n\t    // calculate texture offset\n\t    angle = (rw_centerangle + xtoviewangle[rw_x])>>ANGLETOFINESHIFT;\n\t    texturecolumn = rw_offset-FixedMul(finetangent[angle],rw_distance);\n\t    texturecolumn >>= FRACBITS;\n\t    // calculate lighting\n\t    index = rw_scale>>LIGHTSCALESHIFT;\n\n\t    if (index >=  MAXLIGHTSCALE )\n\t\tindex = MAXLIGHTSCALE-1;\n\n\t    dc_colormap = walllights[index];\n\t    dc_x = rw_x;\n\t    dc_iscale = 0xffffffffu / (unsigned)rw_scale;\n\t}\n        else\n        {\n            // purely to shut up the compiler\n\n            texturecolumn = 0;\n        }\n\t\n\t// draw the wall tiers\n\tif (midtexture)\n\t{\n\t    // single sided line\n\t    dc_yl = yl;\n\t    dc_yh = yh;\n\t    dc_texturemid = rw_midtexturemid;\n\t    dc_source = R_GetColumn(midtexture,texturecolumn);\n\t    colfunc ();\n\t    ceilingclip[rw_x] = viewheight;\n\t    floorclip[rw_x] = -1;\n\t}\n\telse\n\t{\n\t    // two sided line\n\t    if (toptexture)\n\t    {\n\t\t// top wall\n\t\tmid = pixhigh>>HEIGHTBITS;\n\t\tpixhigh += pixhighstep;\n\n\t\tif (mid >= floorclip[rw_x])\n\t\t    mid = floorclip[rw_x]-1;\n\n\t\tif (mid >= yl)\n\t\t{\n\t\t    dc_yl = yl;\n\t\t    dc_yh = mid;\n\t\t    dc_texturemid = rw_toptexturemid;\n\t\t    dc_source = R_GetColumn(toptexture,texturecolumn);\n\t\t    colfunc ();\n\t\t    ceilingclip[rw_x] = mid;\n\t\t}\n\t\telse\n\t\t    ceilingclip[rw_x] = yl-1;\n\t    }\n\t    else\n\t    {\n\t\t// no top wall\n\t\tif (markceiling)\n\t\t    ceilingclip[rw_x] = yl-1;\n\t    }\n\t\t\t\n\t    if (bottomtexture)\n\t    {\n\t\t// bottom wall\n\t\tmid = (pixlow+HEIGHTUNIT-1)>>HEIGHTBITS;\n\t\tpixlow += pixlowstep;\n\n\t\t// no space above wall?\n\t\tif (mid <= ceilingclip[rw_x])\n\t\t    mid = ceilingclip[rw_x]+1;\n\t\t\n\t\tif (mid <= yh)\n\t\t{\n\t\t    dc_yl = mid;\n\t\t    dc_yh = yh;\n\t\t    dc_texturemid = rw_bottomtexturemid;\n\t\t    dc_source = R_GetColumn(bottomtexture,\n\t\t\t\t\t    texturecolumn);\n\t\t    colfunc ();\n\t\t    floorclip[rw_x] = mid;\n\t\t}\n\t\telse\n\t\t    floorclip[rw_x] = yh+1;\n\t    }\n\t    else\n\t    {\n\t\t// no bottom wall\n\t\tif (markfloor)\n\t\t    floorclip[rw_x] = yh+1;\n\t    }\n\t\t\t\n\t    if (maskedtexture)\n\t    {\n\t\t// save texturecol\n\t\t//  for backdrawing of masked mid texture\n\t\tmaskedtexturecol[rw_x] = texturecolumn;\n\t    }\n\t}\n\t\t\n\trw_scale += rw_scalestep;\n\ttopfrac += topstep;\n\tbottomfrac += bottomstep;\n    }\n}\n\n\n\n\n//\n// R_StoreWallRange\n// A wall segment will be drawn\n//  between start and stop pixels (inclusive).\n//\nvoid\nR_StoreWallRange\n( int\tstart,\n  int\tstop )\n{\n    fixed_t\t\thyp;\n    fixed_t\t\tsineval;\n    angle_t\t\tdistangle, offsetangle;\n    fixed_t\t\tvtop;\n    int\t\t\tlightnum;\n\n    // don't overflow and crash\n    if (ds_p == &drawsegs[MAXDRAWSEGS])\n\treturn;\t\t\n\t\t\n#ifdef RANGECHECK\n    if (start >=viewwidth || start > stop)\n\tI_Error (\"Bad R_RenderWallRange: %i to %i\", start , stop);\n#endif\n    \n    sidedef = curline->sidedef;\n    linedef = curline->linedef;\n\n    // mark the segment as visible for auto map\n    linedef->flags |= ML_MAPPED;\n    \n    // calculate rw_distance for scale calculation\n    rw_normalangle = curline->angle + ANG90;\n    offsetangle = abs(rw_normalangle-rw_angle1);\n    \n    if (offsetangle > ANG90)\n\toffsetangle = ANG90;\n\n    distangle = ANG90 - offsetangle;\n    hyp = R_PointToDist (curline->v1->x, curline->v1->y);\n    sineval = finesine[distangle>>ANGLETOFINESHIFT];\n    rw_distance = FixedMul (hyp, sineval);\n\t\t\n\t\n    ds_p->x1 = rw_x = start;\n    ds_p->x2 = stop;\n    ds_p->curline = curline;\n    rw_stopx = stop+1;\n    \n    // calculate scale at both ends and step\n    ds_p->scale1 = rw_scale = \n\tR_ScaleFromGlobalAngle (viewangle + xtoviewangle[start]);\n    \n    if (stop > start )\n    {\n\tds_p->scale2 = R_ScaleFromGlobalAngle (viewangle + xtoviewangle[stop]);\n\tds_p->scalestep = rw_scalestep = \n\t    (ds_p->scale2 - rw_scale) / (stop-start);\n    }\n    else\n    {\n\t// UNUSED: try to fix the stretched line bug\n#if 0\n\tif (rw_distance < FRACUNIT/2)\n\t{\n\t    fixed_t\t\ttrx,try;\n\t    fixed_t\t\tgxt,gyt;\n\n\t    trx = curline->v1->x - viewx;\n\t    try = curline->v1->y - viewy;\n\t\t\t\n\t    gxt = FixedMul(trx,viewcos); \n\t    gyt = -FixedMul(try,viewsin); \n\t    ds_p->scale1 = FixedDiv(projection, gxt-gyt)<<detailshift;\n\t}\n#endif\n\tds_p->scale2 = ds_p->scale1;\n    }\n    \n    // calculate texture boundaries\n    //  and decide if floor / ceiling marks are needed\n    worldtop = frontsector->ceilingheight - viewz;\n    worldbottom = frontsector->floorheight - viewz;\n\t\n    midtexture = toptexture = bottomtexture = maskedtexture = 0;\n    ds_p->maskedtexturecol = NULL;\n\t\n    if (!backsector)\n    {\n\t// single sided line\n\tmidtexture = texturetranslation[sidedef->midtexture];\n\t// a single sided line is terminal, so it must mark ends\n\tmarkfloor = markceiling = true;\n\tif (linedef->flags & ML_DONTPEGBOTTOM)\n\t{\n\t    vtop = frontsector->floorheight +\n\t\ttextureheight[sidedef->midtexture];\n\t    // bottom of texture at bottom\n\t    rw_midtexturemid = vtop - viewz;\t\n\t}\n\telse\n\t{\n\t    // top of texture at top\n\t    rw_midtexturemid = worldtop;\n\t}\n\trw_midtexturemid += sidedef->rowoffset;\n\n\tds_p->silhouette = SIL_BOTH;\n\tds_p->sprtopclip = screenheightarray;\n\tds_p->sprbottomclip = negonearray;\n\tds_p->bsilheight = INT_MAX;\n\tds_p->tsilheight = INT_MIN;\n    }\n    else\n    {\n\t// two sided line\n\tds_p->sprtopclip = ds_p->sprbottomclip = NULL;\n\tds_p->silhouette = 0;\n\t\n\tif (frontsector->floorheight > backsector->floorheight)\n\t{\n\t    ds_p->silhouette = SIL_BOTTOM;\n\t    ds_p->bsilheight = frontsector->floorheight;\n\t}\n\telse if (backsector->floorheight > viewz)\n\t{\n\t    ds_p->silhouette = SIL_BOTTOM;\n\t    ds_p->bsilheight = INT_MAX;\n\t    // ds_p->sprbottomclip = negonearray;\n\t}\n\t\n\tif (frontsector->ceilingheight < backsector->ceilingheight)\n\t{\n\t    ds_p->silhouette |= SIL_TOP;\n\t    ds_p->tsilheight = frontsector->ceilingheight;\n\t}\n\telse if (backsector->ceilingheight < viewz)\n\t{\n\t    ds_p->silhouette |= SIL_TOP;\n\t    ds_p->tsilheight = INT_MIN;\n\t    // ds_p->sprtopclip = screenheightarray;\n\t}\n\t\t\n\tif (backsector->ceilingheight <= frontsector->floorheight)\n\t{\n\t    ds_p->sprbottomclip = negonearray;\n\t    ds_p->bsilheight = INT_MAX;\n\t    ds_p->silhouette |= SIL_BOTTOM;\n\t}\n\t\n\tif (backsector->floorheight >= frontsector->ceilingheight)\n\t{\n\t    ds_p->sprtopclip = screenheightarray;\n\t    ds_p->tsilheight = INT_MIN;\n\t    ds_p->silhouette |= SIL_TOP;\n\t}\n\t\n\tworldhigh = backsector->ceilingheight - viewz;\n\tworldlow = backsector->floorheight - viewz;\n\t\t\n\t// hack to allow height changes in outdoor areas\n\tif (frontsector->ceilingpic == skyflatnum \n\t    && backsector->ceilingpic == skyflatnum)\n\t{\n\t    worldtop = worldhigh;\n\t}\n\t\n\t\t\t\n\tif (worldlow != worldbottom \n\t    || backsector->floorpic != frontsector->floorpic\n\t    || backsector->lightlevel != frontsector->lightlevel)\n\t{\n\t    markfloor = true;\n\t}\n\telse\n\t{\n\t    // same plane on both sides\n\t    markfloor = false;\n\t}\n\t\n\t\t\t\n\tif (worldhigh != worldtop \n\t    || backsector->ceilingpic != frontsector->ceilingpic\n\t    || backsector->lightlevel != frontsector->lightlevel)\n\t{\n\t    markceiling = true;\n\t}\n\telse\n\t{\n\t    // same plane on both sides\n\t    markceiling = false;\n\t}\n\t\n\tif (backsector->ceilingheight <= frontsector->floorheight\n\t    || backsector->floorheight >= frontsector->ceilingheight)\n\t{\n\t    // closed door\n\t    markceiling = markfloor = true;\n\t}\n\t\n\n\tif (worldhigh < worldtop)\n\t{\n\t    // top texture\n\t    toptexture = texturetranslation[sidedef->toptexture];\n\t    if (linedef->flags & ML_DONTPEGTOP)\n\t    {\n\t\t// top of texture at top\n\t\trw_toptexturemid = worldtop;\n\t    }\n\t    else\n\t    {\n\t\tvtop =\n\t\t    backsector->ceilingheight\n\t\t    + textureheight[sidedef->toptexture];\n\t\t\n\t\t// bottom of texture\n\t\trw_toptexturemid = vtop - viewz;\t\n\t    }\n\t}\n\tif (worldlow > worldbottom)\n\t{\n\t    // bottom texture\n\t    bottomtexture = texturetranslation[sidedef->bottomtexture];\n\n\t    if (linedef->flags & ML_DONTPEGBOTTOM )\n\t    {\n\t\t// bottom of texture at bottom\n\t\t// top of texture at top\n\t\trw_bottomtexturemid = worldtop;\n\t    }\n\t    else\t// top of texture at top\n\t\trw_bottomtexturemid = worldlow;\n\t}\n\trw_toptexturemid += sidedef->rowoffset;\n\trw_bottomtexturemid += sidedef->rowoffset;\n\t\n\t// allocate space for masked texture tables\n\tif (sidedef->midtexture)\n\t{\n\t    // masked midtexture\n\t    maskedtexture = true;\n\t    ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x;\n\t    lastopening += rw_stopx - rw_x;\n\t}\n    }\n    \n    // calculate rw_offset (only needed for textured lines)\n    segtextured = midtexture | toptexture | bottomtexture | maskedtexture;\n\n    if (segtextured)\n    {\n\toffsetangle = rw_normalangle-rw_angle1;\n\t\n\tif (offsetangle > ANG180)\n\t    offsetangle = -offsetangle;\n\n\tif (offsetangle > ANG90)\n\t    offsetangle = ANG90;\n\n\tsineval = finesine[offsetangle >>ANGLETOFINESHIFT];\n\trw_offset = FixedMul (hyp, sineval);\n\n\tif (rw_normalangle-rw_angle1 < ANG180)\n\t    rw_offset = -rw_offset;\n\n\trw_offset += sidedef->textureoffset + curline->offset;\n\trw_centerangle = ANG90 + viewangle - rw_normalangle;\n\t\n\t// calculate light table\n\t//  use different light tables\n\t//  for horizontal / vertical / diagonal\n\t// OPTIMIZE: get rid of LIGHTSEGSHIFT globally\n\tif (!fixedcolormap)\n\t{\n\t    lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT)+extralight;\n\n\t    if (curline->v1->y == curline->v2->y)\n\t\tlightnum--;\n\t    else if (curline->v1->x == curline->v2->x)\n\t\tlightnum++;\n\n\t    if (lightnum < 0)\t\t\n\t\twalllights = scalelight[0];\n\t    else if (lightnum >= LIGHTLEVELS)\n\t\twalllights = scalelight[LIGHTLEVELS-1];\n\t    else\n\t\twalllights = scalelight[lightnum];\n\t}\n    }\n    \n    // if a floor / ceiling plane is on the wrong side\n    //  of the view plane, it is definitely invisible\n    //  and doesn't need to be marked.\n    \n  \n    if (frontsector->floorheight >= viewz)\n    {\n\t// above view plane\n\tmarkfloor = false;\n    }\n    \n    if (frontsector->ceilingheight <= viewz \n\t&& frontsector->ceilingpic != skyflatnum)\n    {\n\t// below view plane\n\tmarkceiling = false;\n    }\n\n    \n    // calculate incremental stepping values for texture edges\n    worldtop >>= 4;\n    worldbottom >>= 4;\n\t\n    topstep = -FixedMul (rw_scalestep, worldtop);\n    topfrac = (centeryfrac>>4) - FixedMul (worldtop, rw_scale);\n\n    bottomstep = -FixedMul (rw_scalestep,worldbottom);\n    bottomfrac = (centeryfrac>>4) - FixedMul (worldbottom, rw_scale);\n\t\n    if (backsector)\n    {\t\n\tworldhigh >>= 4;\n\tworldlow >>= 4;\n\n\tif (worldhigh < worldtop)\n\t{\n\t    pixhigh = (centeryfrac>>4) - FixedMul (worldhigh, rw_scale);\n\t    pixhighstep = -FixedMul (rw_scalestep,worldhigh);\n\t}\n\t\n\tif (worldlow > worldbottom)\n\t{\n\t    pixlow = (centeryfrac>>4) - FixedMul (worldlow, rw_scale);\n\t    pixlowstep = -FixedMul (rw_scalestep,worldlow);\n\t}\n    }\n    \n    // render it\n    if (markceiling)\n\tceilingplane = R_CheckPlane (ceilingplane, rw_x, rw_stopx-1);\n    \n    if (markfloor)\n\tfloorplane = R_CheckPlane (floorplane, rw_x, rw_stopx-1);\n\n    R_RenderSegLoop ();\n\n    \n    // save sprite clipping info\n    if ( ((ds_p->silhouette & SIL_TOP) || maskedtexture)\n\t && !ds_p->sprtopclip)\n    {\n\tmemcpy (lastopening, ceilingclip+start, 2*(rw_stopx-start));\n\tds_p->sprtopclip = lastopening - start;\n\tlastopening += rw_stopx - start;\n    }\n    \n    if ( ((ds_p->silhouette & SIL_BOTTOM) || maskedtexture)\n\t && !ds_p->sprbottomclip)\n    {\n\tmemcpy (lastopening, floorclip+start, 2*(rw_stopx-start));\n\tds_p->sprbottomclip = lastopening - start;\n\tlastopening += rw_stopx - start;\t\n    }\n\n    if (maskedtexture && !(ds_p->silhouette&SIL_TOP))\n    {\n\tds_p->silhouette |= SIL_TOP;\n\tds_p->tsilheight = INT_MIN;\n    }\n    if (maskedtexture && !(ds_p->silhouette&SIL_BOTTOM))\n    {\n\tds_p->silhouette |= SIL_BOTTOM;\n\tds_p->bsilheight = INT_MAX;\n    }\n    ds_p++;\n}\n\n"
  },
  {
    "path": "fbdoom/r_segs.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tRefresh module, drawing LineSegs from BSP.\n//\n\n\n#ifndef __R_SEGS__\n#define __R_SEGS__\n\n\n\n\nvoid\nR_RenderMaskedSegRange\n( drawseg_t*\tds,\n  int\t\tx1,\n  int\t\tx2 );\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/r_sky.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//  Sky rendering. The DOOM sky is a texture map like any\n//  wall, wrapping around. A 1024 columns equal 360 degrees.\n//  The default sky map is 256 columns and repeats 4 times\n//  on a 320 screen?\n//  \n//\n\n\n\n// Needed for FRACUNIT.\n#include \"m_fixed.h\"\n\n// Needed for Flat retrieval.\n#include \"r_data.h\"\n\n\n#include \"r_sky.h\"\n\n//\n// sky mapping\n//\nint\t\t\tskyflatnum;\nint\t\t\tskytexture;\nint\t\t\tskytexturemid;\n\n\n\n//\n// R_InitSkyMap\n// Called whenever the view size changes.\n//\nvoid R_InitSkyMap (void)\n{\n  // skyflatnum = R_FlatNumForName ( SKYFLATNAME );\n    skytexturemid = 100*FRACUNIT;\n}\n\n"
  },
  {
    "path": "fbdoom/r_sky.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tSky rendering.\n//\n\n\n#ifndef __R_SKY__\n#define __R_SKY__\n\n\n\n// SKY, store the number for name.\n#define\t\t\tSKYFLATNAME  \"F_SKY1\"\n\n// The sky map is 256*128*4 maps.\n#define ANGLETOSKYSHIFT\t\t22\n\nextern  int\t\tskytexture;\nextern int\t\tskytexturemid;\n\n// Called whenever the view size changes.\nvoid R_InitSkyMap (void);\n\n#endif\n"
  },
  {
    "path": "fbdoom/r_state.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tRefresh/render internal state variables (global).\n//\n\n\n#ifndef __R_STATE__\n#define __R_STATE__\n\n// Need data structure definitions.\n#include \"d_player.h\"\n#include \"r_data.h\"\n\n\n\n\n\n\n//\n// Refresh internal data structures,\n//  for rendering.\n//\n\n// needed for texture pegging\nextern fixed_t*\t\ttextureheight;\n\n// needed for pre rendering (fracs)\nextern fixed_t*\t\tspritewidth;\n\nextern fixed_t*\t\tspriteoffset;\nextern fixed_t*\t\tspritetopoffset;\n\nextern lighttable_t*\tcolormaps;\n\nextern int\t\tviewwidth;\nextern int\t\tscaledviewwidth;\nextern int\t\tviewheight;\n\nextern int\t\tfirstflat;\n\n// for global animation\nextern int*\t\tflattranslation;\t\nextern int*\t\ttexturetranslation;\t\n\n\n// Sprite....\nextern int\t\tfirstspritelump;\nextern int\t\tlastspritelump;\nextern int\t\tnumspritelumps;\n\n\n\n//\n// Lookup tables for map data.\n//\nextern int\t\tnumsprites;\nextern spritedef_t*\tsprites;\n\nextern int\t\tnumvertexes;\nextern vertex_t*\tvertexes;\n\nextern int\t\tnumsegs;\nextern seg_t*\t\tsegs;\n\nextern int\t\tnumsectors;\nextern sector_t*\tsectors;\n\nextern int\t\tnumsubsectors;\nextern subsector_t*\tsubsectors;\n\nextern int\t\tnumnodes;\nextern node_t*\t\tnodes;\n\nextern int\t\tnumlines;\nextern line_t*\t\tlines;\n\nextern int\t\tnumsides;\nextern side_t*\t\tsides;\n\n\n//\n// POV data.\n//\nextern fixed_t\t\tviewx;\nextern fixed_t\t\tviewy;\nextern fixed_t\t\tviewz;\n\nextern angle_t\t\tviewangle;\nextern player_t*\tviewplayer;\n\n\n// ?\nextern angle_t\t\tclipangle;\n\nextern int\t\tviewangletox[FINEANGLES/2];\nextern angle_t\t\txtoviewangle[SCREENWIDTH+1];\n//extern fixed_t\t\tfinetangent[FINEANGLES/2];\n\nextern fixed_t\t\trw_distance;\nextern angle_t\t\trw_normalangle;\n\n\n\n// angle to line origin\nextern int\t\trw_angle1;\n\n// Segs count?\nextern int\t\tsscount;\n\nextern visplane_t*\tfloorplane;\nextern visplane_t*\tceilingplane;\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/r_things.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tRefresh of things, i.e. objects represented by sprites.\n//\n\n\n\n\n#include <stdio.h>\n#include <stdlib.h>\n\n\n#include \"deh_main.h\"\n#include \"doomdef.h\"\n\n#include \"i_swap.h\"\n#include \"i_system.h\"\n#include \"z_zone.h\"\n#include \"w_wad.h\"\n\n#include \"r_local.h\"\n\n#include \"doomstat.h\"\n\n\n\n#define MINZ\t\t\t\t(FRACUNIT*4)\n#define BASEYCENTER\t\t\t100\n\n//void R_DrawColumn (void);\n//void R_DrawFuzzColumn (void);\n\n\n\ntypedef struct\n{\n    int\t\tx1;\n    int\t\tx2;\n\t\n    int\t\tcolumn;\n    int\t\ttopclip;\n    int\t\tbottomclip;\n\n} maskdraw_t;\n\n\n\n//\n// Sprite rotation 0 is facing the viewer,\n//  rotation 1 is one angle turn CLOCKWISE around the axis.\n// This is not the same as the angle,\n//  which increases counter clockwise (protractor).\n// There was a lot of stuff grabbed wrong, so I changed it...\n//\nfixed_t\t\tpspritescale;\nfixed_t\t\tpspriteiscale;\n\nlighttable_t**\tspritelights;\n\n// constant arrays\n//  used for psprite clipping and initializing clipping\nshort\t\tnegonearray[SCREENWIDTH];\nshort\t\tscreenheightarray[SCREENWIDTH];\n\n\n//\n// INITIALIZATION FUNCTIONS\n//\n\n// variables used to look up\n//  and range check thing_t sprites patches\nspritedef_t*\tsprites;\nint\t\tnumsprites;\n\nspriteframe_t\tsprtemp[29];\nint\t\tmaxframe;\nchar*\t\tspritename;\n\n\n\n\n//\n// R_InstallSpriteLump\n// Local function for R_InitSprites.\n//\nvoid\nR_InstallSpriteLump\n( int\t\tlump,\n  unsigned\tframe,\n  unsigned\trotation,\n  boolean\tflipped )\n{\n    int\t\tr;\n\t\n    if (frame >= 29 || rotation > 8)\n\tI_Error(\"R_InstallSpriteLump: \"\n\t\t\"Bad frame characters in lump %i\", lump);\n\t\n    if ((int)frame > maxframe)\n\tmaxframe = frame;\n\t\t\n    if (rotation == 0)\n    {\n\t// the lump should be used for all rotations\n\tif (sprtemp[frame].rotate == false)\n\t    I_Error (\"R_InitSprites: Sprite %s frame %c has \"\n\t\t     \"multip rot=0 lump\", spritename, 'A'+frame);\n\n\tif (sprtemp[frame].rotate == true)\n\t    I_Error (\"R_InitSprites: Sprite %s frame %c has rotations \"\n\t\t     \"and a rot=0 lump\", spritename, 'A'+frame);\n\t\t\t\n\tsprtemp[frame].rotate = false;\n\tfor (r=0 ; r<8 ; r++)\n\t{\n\t    sprtemp[frame].lump[r] = lump - firstspritelump;\n\t    sprtemp[frame].flip[r] = (byte)flipped;\n\t}\n\treturn;\n    }\n\t\n    // the lump is only used for one rotation\n    if (sprtemp[frame].rotate == false)\n\tI_Error (\"R_InitSprites: Sprite %s frame %c has rotations \"\n\t\t \"and a rot=0 lump\", spritename, 'A'+frame);\n\t\t\n    sprtemp[frame].rotate = true;\n\n    // make 0 based\n    rotation--;\t\t\n    if (sprtemp[frame].lump[rotation] != -1)\n\tI_Error (\"R_InitSprites: Sprite %s : %c : %c \"\n\t\t \"has two lumps mapped to it\",\n\t\t spritename, 'A'+frame, '1'+rotation);\n\t\t\n    sprtemp[frame].lump[rotation] = lump - firstspritelump;\n    sprtemp[frame].flip[rotation] = (byte)flipped;\n}\n\n\n\n\n//\n// R_InitSpriteDefs\n// Pass a null terminated list of sprite names\n//  (4 chars exactly) to be used.\n// Builds the sprite rotation matrixes to account\n//  for horizontally flipped sprites.\n// Will report an error if the lumps are inconsistant. \n// Only called at startup.\n//\n// Sprite lump names are 4 characters for the actor,\n//  a letter for the frame, and a number for the rotation.\n// A sprite that is flippable will have an additional\n//  letter/number appended.\n// The rotation character can be 0 to signify no rotations.\n//\nvoid R_InitSpriteDefs (char** namelist) \n{ \n    char**\tcheck;\n    int\t\ti;\n    int\t\tl;\n    int\t\tframe;\n    int\t\trotation;\n    int\t\tstart;\n    int\t\tend;\n    int\t\tpatched;\n\t\t\n    // count the number of sprite names\n    check = namelist;\n    while (*check != NULL)\n\tcheck++;\n\n    numsprites = check-namelist;\n\t\n    if (!numsprites)\n\treturn;\n\t\t\n    sprites = Z_Malloc(numsprites *sizeof(*sprites), PU_STATIC, NULL);\n\t\n    start = firstspritelump-1;\n    end = lastspritelump+1;\n\t\n    // scan all the lump names for each of the names,\n    //  noting the highest frame letter.\n    // Just compare 4 characters as ints\n    for (i=0 ; i<numsprites ; i++)\n    {\n\tspritename = DEH_String(namelist[i]);\n\tmemset (sprtemp,-1, sizeof(sprtemp));\n\t\t\n\tmaxframe = -1;\n\t\n\t// scan the lumps,\n\t//  filling in the frames for whatever is found\n\tfor (l=start+1 ; l<end ; l++)\n\t{\n\t    if (!strncasecmp(lumpinfo[l].name, spritename, 4))\n\t    {\n\t\tframe = lumpinfo[l].name[4] - 'A';\n\t\trotation = lumpinfo[l].name[5] - '0';\n\n\t\tif (modifiedgame)\n\t\t    patched = W_GetNumForName (lumpinfo[l].name);\n\t\telse\n\t\t    patched = l;\n\n\t\tR_InstallSpriteLump (patched, frame, rotation, false);\n\n\t\tif (lumpinfo[l].name[6])\n\t\t{\n\t\t    frame = lumpinfo[l].name[6] - 'A';\n\t\t    rotation = lumpinfo[l].name[7] - '0';\n\t\t    R_InstallSpriteLump (l, frame, rotation, true);\n\t\t}\n\t    }\n\t}\n\t\n\t// check the frames that were found for completeness\n\tif (maxframe == -1)\n\t{\n\t    sprites[i].numframes = 0;\n\t    continue;\n\t}\n\t\t\n\tmaxframe++;\n\t\n\tfor (frame = 0 ; frame < maxframe ; frame++)\n\t{\n\t    switch ((int)sprtemp[frame].rotate)\n\t    {\n\t      case -1:\n\t\t// no rotations were found for that frame at all\n\t\tI_Error (\"R_InitSprites: No patches found \"\n\t\t\t \"for %s frame %c\", spritename, frame+'A');\n\t\tbreak;\n\t\t\n\t      case 0:\n\t\t// only the first rotation is needed\n\t\tbreak;\n\t\t\t\n\t      case 1:\n\t\t// must have all 8 frames\n\t\tfor (rotation=0 ; rotation<8 ; rotation++)\n\t\t    if (sprtemp[frame].lump[rotation] == -1)\n\t\t\tI_Error (\"R_InitSprites: Sprite %s frame %c \"\n\t\t\t\t \"is missing rotations\",\n\t\t\t\t spritename, frame+'A');\n\t\tbreak;\n\t    }\n\t}\n\t\n\t// allocate space for the frames present and copy sprtemp to it\n\tsprites[i].numframes = maxframe;\n\tsprites[i].spriteframes = \n\t    Z_Malloc (maxframe * sizeof(spriteframe_t), PU_STATIC, NULL);\n\tmemcpy (sprites[i].spriteframes, sprtemp, maxframe*sizeof(spriteframe_t));\n    }\n\n}\n\n\n\n\n//\n// GAME FUNCTIONS\n//\nvissprite_t\tvissprites[MAXVISSPRITES];\nvissprite_t*\tvissprite_p;\nint\t\tnewvissprite;\n\n\n\n//\n// R_InitSprites\n// Called at program start.\n//\nvoid R_InitSprites (char** namelist)\n{\n    int\t\ti;\n\t\n    for (i=0 ; i<SCREENWIDTH ; i++)\n    {\n\tnegonearray[i] = -1;\n    }\n\t\n    R_InitSpriteDefs (namelist);\n}\n\n\n\n//\n// R_ClearSprites\n// Called at frame start.\n//\nvoid R_ClearSprites (void)\n{\n    vissprite_p = vissprites;\n}\n\n\n//\n// R_NewVisSprite\n//\nvissprite_t\toverflowsprite;\n\nvissprite_t* R_NewVisSprite (void)\n{\n    if (vissprite_p == &vissprites[MAXVISSPRITES])\n\treturn &overflowsprite;\n    \n    vissprite_p++;\n    return vissprite_p-1;\n}\n\n\n\n//\n// R_DrawMaskedColumn\n// Used for sprites and masked mid textures.\n// Masked means: partly transparent, i.e. stored\n//  in posts/runs of opaque pixels.\n//\nshort*\t\tmfloorclip;\nshort*\t\tmceilingclip;\n\nfixed_t\t\tspryscale;\nfixed_t\t\tsprtopscreen;\n\nvoid R_DrawMaskedColumn (column_t* column)\n{\n    int\t\ttopscreen;\n    int \tbottomscreen;\n    fixed_t\tbasetexturemid;\n\t\n    basetexturemid = dc_texturemid;\n\t\n    for ( ; column->topdelta != 0xff ; ) \n    {\n\t// calculate unclipped screen coordinates\n\t//  for post\n\ttopscreen = sprtopscreen + spryscale*column->topdelta;\n\tbottomscreen = topscreen + spryscale*column->length;\n\n\tdc_yl = (topscreen+FRACUNIT-1)>>FRACBITS;\n\tdc_yh = (bottomscreen-1)>>FRACBITS;\n\t\t\n\tif (dc_yh >= mfloorclip[dc_x])\n\t    dc_yh = mfloorclip[dc_x]-1;\n\tif (dc_yl <= mceilingclip[dc_x])\n\t    dc_yl = mceilingclip[dc_x]+1;\n\n\tif (dc_yl <= dc_yh)\n\t{\n\t    dc_source = (byte *)column + 3;\n\t    dc_texturemid = basetexturemid - (column->topdelta<<FRACBITS);\n\t    // dc_source = (byte *)column + 3 - column->topdelta;\n\n\t    // Drawn by either R_DrawColumn\n\t    //  or (SHADOW) R_DrawFuzzColumn.\n\t    colfunc ();\t\n\t}\n\tcolumn = (column_t *)(  (byte *)column + column->length + 4);\n    }\n\t\n    dc_texturemid = basetexturemid;\n}\n\n\n\n//\n// R_DrawVisSprite\n//  mfloorclip and mceilingclip should also be set.\n//\nvoid\nR_DrawVisSprite\n( vissprite_t*\t\tvis,\n  int\t\t\tx1,\n  int\t\t\tx2 )\n{\n    column_t*\t\tcolumn;\n    int\t\t\ttexturecolumn;\n    fixed_t\t\tfrac;\n    patch_t*\t\tpatch;\n\t\n\t\n    patch = W_CacheLumpNum (vis->patch+firstspritelump, PU_CACHE);\n\n    dc_colormap = vis->colormap;\n    \n    if (!dc_colormap)\n    {\n\t// NULL colormap = shadow draw\n\tcolfunc = fuzzcolfunc;\n    }\n    else if (vis->mobjflags & MF_TRANSLATION)\n    {\n\tcolfunc = transcolfunc;\n\tdc_translation = translationtables - 256 +\n\t    ( (vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT-8) );\n    }\n\t\n    dc_iscale = abs(vis->xiscale)>>detailshift;\n    dc_texturemid = vis->texturemid;\n    frac = vis->startfrac;\n    spryscale = vis->scale;\n    sprtopscreen = centeryfrac - FixedMul(dc_texturemid,spryscale);\n\t\n    for (dc_x=vis->x1 ; dc_x<=vis->x2 ; dc_x++, frac += vis->xiscale)\n    {\n\ttexturecolumn = frac>>FRACBITS;\n#ifdef RANGECHECK\n\tif (texturecolumn < 0 || texturecolumn >= SHORT(patch->width))\n\t    I_Error (\"R_DrawSpriteRange: bad texturecolumn\");\n#endif\n\tcolumn = (column_t *) ((byte *)patch +\n\t\t\t       LONG(patch->columnofs[texturecolumn]));\n\tR_DrawMaskedColumn (column);\n    }\n\n    colfunc = basecolfunc;\n}\n\n\n\n//\n// R_ProjectSprite\n// Generates a vissprite for a thing\n//  if it might be visible.\n//\nvoid R_ProjectSprite (mobj_t* thing)\n{\n    fixed_t\t\ttr_x;\n    fixed_t\t\ttr_y;\n    \n    fixed_t\t\tgxt;\n    fixed_t\t\tgyt;\n    \n    fixed_t\t\ttx;\n    fixed_t\t\ttz;\n\n    fixed_t\t\txscale;\n    \n    int\t\t\tx1;\n    int\t\t\tx2;\n\n    spritedef_t*\tsprdef;\n    spriteframe_t*\tsprframe;\n    int\t\t\tlump;\n    \n    unsigned\t\trot;\n    boolean\t\tflip;\n    \n    int\t\t\tindex;\n\n    vissprite_t*\tvis;\n    \n    angle_t\t\tang;\n    fixed_t\t\tiscale;\n    \n    // transform the origin point\n    tr_x = thing->x - viewx;\n    tr_y = thing->y - viewy;\n\t\n    gxt = FixedMul(tr_x,viewcos); \n    gyt = -FixedMul(tr_y,viewsin);\n    \n    tz = gxt-gyt; \n\n    // thing is behind view plane?\n    if (tz < MINZ)\n\treturn;\n    \n    xscale = FixedDiv(projection, tz);\n\t\n    gxt = -FixedMul(tr_x,viewsin); \n    gyt = FixedMul(tr_y,viewcos); \n    tx = -(gyt+gxt); \n\n    // too far off the side?\n    if (abs(tx)>(tz<<2))\n\treturn;\n    \n    // decide which patch to use for sprite relative to player\n#ifdef RANGECHECK\n    if ((unsigned int) thing->sprite >= (unsigned int) numsprites)\n\tI_Error (\"R_ProjectSprite: invalid sprite number %i \",\n\t\t thing->sprite);\n#endif\n    sprdef = &sprites[thing->sprite];\n#ifdef RANGECHECK\n    if ( (thing->frame&FF_FRAMEMASK) >= sprdef->numframes )\n\tI_Error (\"R_ProjectSprite: invalid sprite frame %i : %i \",\n\t\t thing->sprite, thing->frame);\n#endif\n    sprframe = &sprdef->spriteframes[ thing->frame & FF_FRAMEMASK];\n\n    if (sprframe->rotate)\n    {\n\t// choose a different rotation based on player view\n\tang = R_PointToAngle (thing->x, thing->y);\n\trot = (ang-thing->angle+(unsigned)(ANG45/2)*9)>>29;\n\tlump = sprframe->lump[rot];\n\tflip = (boolean)sprframe->flip[rot];\n    }\n    else\n    {\n\t// use single rotation for all views\n\tlump = sprframe->lump[0];\n\tflip = (boolean)sprframe->flip[0];\n    }\n    \n    // calculate edges of the shape\n    tx -= spriteoffset[lump];\t\n    x1 = (centerxfrac + FixedMul (tx,xscale) ) >>FRACBITS;\n\n    // off the right side?\n    if (x1 > viewwidth)\n\treturn;\n    \n    tx +=  spritewidth[lump];\n    x2 = ((centerxfrac + FixedMul (tx,xscale) ) >>FRACBITS) - 1;\n\n    // off the left side\n    if (x2 < 0)\n\treturn;\n    \n    // store information in a vissprite\n    vis = R_NewVisSprite ();\n    vis->mobjflags = thing->flags;\n    vis->scale = xscale<<detailshift;\n    vis->gx = thing->x;\n    vis->gy = thing->y;\n    vis->gz = thing->z;\n    vis->gzt = thing->z + spritetopoffset[lump];\n    vis->texturemid = vis->gzt - viewz;\n    vis->x1 = x1 < 0 ? 0 : x1;\n    vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2;\t\n    iscale = FixedDiv (FRACUNIT, xscale);\n\n    if (flip)\n    {\n\tvis->startfrac = spritewidth[lump]-1;\n\tvis->xiscale = -iscale;\n    }\n    else\n    {\n\tvis->startfrac = 0;\n\tvis->xiscale = iscale;\n    }\n\n    if (vis->x1 > x1)\n\tvis->startfrac += vis->xiscale*(vis->x1-x1);\n    vis->patch = lump;\n    \n    // get light level\n    if (thing->flags & MF_SHADOW)\n    {\n\t// shadow draw\n\tvis->colormap = NULL;\n    }\n    else if (fixedcolormap)\n    {\n\t// fixed map\n\tvis->colormap = fixedcolormap;\n    }\n    else if (thing->frame & FF_FULLBRIGHT)\n    {\n\t// full bright\n\tvis->colormap = colormaps;\n    }\n    \n    else\n    {\n\t// diminished light\n\tindex = xscale>>(LIGHTSCALESHIFT-detailshift);\n\n\tif (index >= MAXLIGHTSCALE) \n\t    index = MAXLIGHTSCALE-1;\n\n\tvis->colormap = spritelights[index];\n    }\t\n}\n\n\n\n\n//\n// R_AddSprites\n// During BSP traversal, this adds sprites by sector.\n//\nvoid R_AddSprites (sector_t* sec)\n{\n    mobj_t*\t\tthing;\n    int\t\t\tlightnum;\n\n    // BSP is traversed by subsector.\n    // A sector might have been split into several\n    //  subsectors during BSP building.\n    // Thus we check whether its already added.\n    if (sec->validcount == validcount)\n\treturn;\t\t\n\n    // Well, now it will be done.\n    sec->validcount = validcount;\n\t\n    lightnum = (sec->lightlevel >> LIGHTSEGSHIFT)+extralight;\n\n    if (lightnum < 0)\t\t\n\tspritelights = scalelight[0];\n    else if (lightnum >= LIGHTLEVELS)\n\tspritelights = scalelight[LIGHTLEVELS-1];\n    else\n\tspritelights = scalelight[lightnum];\n\n    // Handle all things in sector.\n    for (thing = sec->thinglist ; thing ; thing = thing->snext)\n\tR_ProjectSprite (thing);\n}\n\n\n//\n// R_DrawPSprite\n//\nvoid R_DrawPSprite (pspdef_t* psp)\n{\n    fixed_t\t\ttx;\n    int\t\t\tx1;\n    int\t\t\tx2;\n    spritedef_t*\tsprdef;\n    spriteframe_t*\tsprframe;\n    int\t\t\tlump;\n    boolean\t\tflip;\n    vissprite_t*\tvis;\n    vissprite_t\t\tavis;\n    \n    // decide which patch to use\n#ifdef RANGECHECK\n    if ( (unsigned)psp->state->sprite >= (unsigned int) numsprites)\n\tI_Error (\"R_ProjectSprite: invalid sprite number %i \",\n\t\t psp->state->sprite);\n#endif\n    sprdef = &sprites[psp->state->sprite];\n#ifdef RANGECHECK\n    if ( (psp->state->frame & FF_FRAMEMASK)  >= sprdef->numframes)\n\tI_Error (\"R_ProjectSprite: invalid sprite frame %i : %i \",\n\t\t psp->state->sprite, psp->state->frame);\n#endif\n    sprframe = &sprdef->spriteframes[ psp->state->frame & FF_FRAMEMASK ];\n\n    lump = sprframe->lump[0];\n    flip = (boolean)sprframe->flip[0];\n    \n    // calculate edges of the shape\n    tx = psp->sx-160*FRACUNIT;\n\t\n    tx -= spriteoffset[lump];\t\n    x1 = (centerxfrac + FixedMul (tx,pspritescale) ) >>FRACBITS;\n\n    // off the right side\n    if (x1 > viewwidth)\n\treturn;\t\t\n\n    tx +=  spritewidth[lump];\n    x2 = ((centerxfrac + FixedMul (tx, pspritescale) ) >>FRACBITS) - 1;\n\n    // off the left side\n    if (x2 < 0)\n\treturn;\n    \n    // store information in a vissprite\n    vis = &avis;\n    vis->mobjflags = 0;\n    vis->texturemid = (BASEYCENTER<<FRACBITS)+FRACUNIT/2-(psp->sy-spritetopoffset[lump]);\n    vis->x1 = x1 < 0 ? 0 : x1;\n    vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2;\t\n    vis->scale = pspritescale<<detailshift;\n    \n    if (flip)\n    {\n\tvis->xiscale = -pspriteiscale;\n\tvis->startfrac = spritewidth[lump]-1;\n    }\n    else\n    {\n\tvis->xiscale = pspriteiscale;\n\tvis->startfrac = 0;\n    }\n    \n    if (vis->x1 > x1)\n\tvis->startfrac += vis->xiscale*(vis->x1-x1);\n\n    vis->patch = lump;\n\n    if (viewplayer->powers[pw_invisibility] > 4*32\n\t|| viewplayer->powers[pw_invisibility] & 8)\n    {\n\t// shadow draw\n\tvis->colormap = NULL;\n    }\n    else if (fixedcolormap)\n    {\n\t// fixed color\n\tvis->colormap = fixedcolormap;\n    }\n    else if (psp->state->frame & FF_FULLBRIGHT)\n    {\n\t// full bright\n\tvis->colormap = colormaps;\n    }\n    else\n    {\n\t// local light\n\tvis->colormap = spritelights[MAXLIGHTSCALE-1];\n    }\n\t\n    R_DrawVisSprite (vis, vis->x1, vis->x2);\n}\n\n\n\n//\n// R_DrawPlayerSprites\n//\nvoid R_DrawPlayerSprites (void)\n{\n    int\t\ti;\n    int\t\tlightnum;\n    pspdef_t*\tpsp;\n    \n    // get light level\n    lightnum =\n\t(viewplayer->mo->subsector->sector->lightlevel >> LIGHTSEGSHIFT) \n\t+extralight;\n\n    if (lightnum < 0)\t\t\n\tspritelights = scalelight[0];\n    else if (lightnum >= LIGHTLEVELS)\n\tspritelights = scalelight[LIGHTLEVELS-1];\n    else\n\tspritelights = scalelight[lightnum];\n    \n    // clip to screen bounds\n    mfloorclip = screenheightarray;\n    mceilingclip = negonearray;\n    \n    // add all active psprites\n    for (i=0, psp=viewplayer->psprites;\n\t i<NUMPSPRITES;\n\t i++,psp++)\n    {\n\tif (psp->state)\n\t    R_DrawPSprite (psp);\n    }\n}\n\n\n\n\n//\n// R_SortVisSprites\n//\nvissprite_t\tvsprsortedhead;\n\n\nvoid R_SortVisSprites (void)\n{\n    int\t\t\ti;\n    int\t\t\tcount;\n    vissprite_t*\tds;\n    vissprite_t*\tbest;\n    vissprite_t\t\tunsorted;\n    fixed_t\t\tbestscale;\n\n    count = vissprite_p - vissprites;\n\t\n    unsorted.next = unsorted.prev = &unsorted;\n\n    if (!count)\n\treturn;\n\t\t\n    for (ds=vissprites ; ds<vissprite_p ; ds++)\n    {\n\tds->next = ds+1;\n\tds->prev = ds-1;\n    }\n    \n    vissprites[0].prev = &unsorted;\n    unsorted.next = &vissprites[0];\n    (vissprite_p-1)->next = &unsorted;\n    unsorted.prev = vissprite_p-1;\n    \n    // pull the vissprites out by scale\n\n    vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead;\n    for (i=0 ; i<count ; i++)\n    {\n\tbestscale = INT_MAX;\n        best = unsorted.next;\n\tfor (ds=unsorted.next ; ds!= &unsorted ; ds=ds->next)\n\t{\n\t    if (ds->scale < bestscale)\n\t    {\n\t\tbestscale = ds->scale;\n\t\tbest = ds;\n\t    }\n\t}\n\tbest->next->prev = best->prev;\n\tbest->prev->next = best->next;\n\tbest->next = &vsprsortedhead;\n\tbest->prev = vsprsortedhead.prev;\n\tvsprsortedhead.prev->next = best;\n\tvsprsortedhead.prev = best;\n    }\n}\n\n\n\n//\n// R_DrawSprite\n//\nstatic short\t\tclipbot[SCREENWIDTH];\nstatic short\t\tcliptop[SCREENWIDTH];\nvoid R_DrawSprite (vissprite_t* spr)\n{\n    drawseg_t*\t\tds;\n    int\t\t\tx;\n    int\t\t\tr1;\n    int\t\t\tr2;\n    fixed_t\t\tscale;\n    fixed_t\t\tlowscale;\n    int\t\t\tsilhouette;\n\t\t\n    for (x = spr->x1 ; x<=spr->x2 ; x++)\n\tclipbot[x] = cliptop[x] = -2;\n    \n    // Scan drawsegs from end to start for obscuring segs.\n    // The first drawseg that has a greater scale\n    //  is the clip seg.\n    for (ds=ds_p-1 ; ds >= drawsegs ; ds--)\n    {\n\t// determine if the drawseg obscures the sprite\n\tif (ds->x1 > spr->x2\n\t    || ds->x2 < spr->x1\n\t    || (!ds->silhouette\n\t\t&& !ds->maskedtexturecol) )\n\t{\n\t    // does not cover sprite\n\t    continue;\n\t}\n\t\t\t\n\tr1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1;\n\tr2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2;\n\n\tif (ds->scale1 > ds->scale2)\n\t{\n\t    lowscale = ds->scale2;\n\t    scale = ds->scale1;\n\t}\n\telse\n\t{\n\t    lowscale = ds->scale1;\n\t    scale = ds->scale2;\n\t}\n\t\t\n\tif (scale < spr->scale\n\t    || ( lowscale < spr->scale\n\t\t && !R_PointOnSegSide (spr->gx, spr->gy, ds->curline) ) )\n\t{\n\t    // masked mid texture?\n\t    if (ds->maskedtexturecol)\t\n\t\tR_RenderMaskedSegRange (ds, r1, r2);\n\t    // seg is behind sprite\n\t    continue;\t\t\t\n\t}\n\n\t\n\t// clip this piece of the sprite\n\tsilhouette = ds->silhouette;\n\t\n\tif (spr->gz >= ds->bsilheight)\n\t    silhouette &= ~SIL_BOTTOM;\n\n\tif (spr->gzt <= ds->tsilheight)\n\t    silhouette &= ~SIL_TOP;\n\t\t\t\n\tif (silhouette == 1)\n\t{\n\t    // bottom sil\n\t    for (x=r1 ; x<=r2 ; x++)\n\t\tif (clipbot[x] == -2)\n\t\t    clipbot[x] = ds->sprbottomclip[x];\n\t}\n\telse if (silhouette == 2)\n\t{\n\t    // top sil\n\t    for (x=r1 ; x<=r2 ; x++)\n\t\tif (cliptop[x] == -2)\n\t\t    cliptop[x] = ds->sprtopclip[x];\n\t}\n\telse if (silhouette == 3)\n\t{\n\t    // both\n\t    for (x=r1 ; x<=r2 ; x++)\n\t    {\n\t\tif (clipbot[x] == -2)\n\t\t    clipbot[x] = ds->sprbottomclip[x];\n\t\tif (cliptop[x] == -2)\n\t\t    cliptop[x] = ds->sprtopclip[x];\n\t    }\n\t}\n\t\t\n    }\n    \n    // all clipping has been performed, so draw the sprite\n\n    // check for unclipped columns\n    for (x = spr->x1 ; x<=spr->x2 ; x++)\n    {\n\tif (clipbot[x] == -2)\t\t\n\t    clipbot[x] = viewheight;\n\n\tif (cliptop[x] == -2)\n\t    cliptop[x] = -1;\n    }\n\t\t\n    mfloorclip = clipbot;\n    mceilingclip = cliptop;\n    R_DrawVisSprite (spr, spr->x1, spr->x2);\n}\n\n\n\n\n//\n// R_DrawMasked\n//\nvoid R_DrawMasked (void)\n{\n    vissprite_t*\tspr;\n    drawseg_t*\t\tds;\n\t\n    R_SortVisSprites ();\n\n    if (vissprite_p > vissprites)\n    {\n\t// draw all vissprites back to front\n\tfor (spr = vsprsortedhead.next ;\n\t     spr != &vsprsortedhead ;\n\t     spr=spr->next)\n\t{\n\t    \n\t    R_DrawSprite (spr);\n\t}\n    }\n    \n    // render any remaining masked mid textures\n    for (ds=ds_p-1 ; ds >= drawsegs ; ds--)\n\tif (ds->maskedtexturecol)\n\t    R_RenderMaskedSegRange (ds, ds->x1, ds->x2);\n    \n    // draw the psprites on top of everything\n    //  but does not draw on side views\n    if (!viewangleoffset)\t\t\n\tR_DrawPlayerSprites ();\n}\n\n\n\n"
  },
  {
    "path": "fbdoom/r_things.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tRendering of moving objects, sprites.\n//\n\n\n#ifndef __R_THINGS__\n#define __R_THINGS__\n\n\n\n#define MAXVISSPRITES  \t128\n\nextern vissprite_t\tvissprites[MAXVISSPRITES];\nextern vissprite_t*\tvissprite_p;\nextern vissprite_t\tvsprsortedhead;\n\n// Constant arrays used for psprite clipping\n//  and initializing clipping.\nextern short\t\tnegonearray[SCREENWIDTH];\nextern short\t\tscreenheightarray[SCREENWIDTH];\n\n// vars for R_DrawMaskedColumn\nextern short*\t\tmfloorclip;\nextern short*\t\tmceilingclip;\nextern fixed_t\t\tspryscale;\nextern fixed_t\t\tsprtopscreen;\n\nextern fixed_t\t\tpspritescale;\nextern fixed_t\t\tpspriteiscale;\n\n\nvoid R_DrawMaskedColumn (column_t* column);\n\n\nvoid R_SortVisSprites (void);\n\nvoid R_AddSprites (sector_t* sec);\nvoid R_AddPSprites (void);\nvoid R_DrawSprites (void);\nvoid R_InitSprites (char** namelist);\nvoid R_ClearSprites (void);\nvoid R_DrawMasked (void);\n\nvoid\nR_ClipVisSprite\n( vissprite_t*\t\tvis,\n  int\t\t\txl,\n  int\t\t\txh );\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/s_sound.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:  none\n//\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"i_sound.h\"\n#include \"i_system.h\"\n\n#include \"doomfeatures.h\"\n#include \"deh_str.h\"\n\n#include \"doomstat.h\"\n#include \"doomtype.h\"\n\n#include \"sounds.h\"\n#include \"s_sound.h\"\n\n#include \"m_misc.h\"\n#include \"m_random.h\"\n#include \"m_argv.h\"\n\n#include \"p_local.h\"\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\n// when to clip out sounds\n// Does not fit the large outdoor areas.\n\n#define S_CLIPPING_DIST (1200 * FRACUNIT)\n\n// Distance tp origin when sounds should be maxed out.\n// This should relate to movement clipping resolution\n// (see BLOCKMAP handling).\n// In the source code release: (160*FRACUNIT).  Changed back to the \n// Vanilla value of 200 (why was this changed?)\n\n#define S_CLOSE_DIST (200 * FRACUNIT)\n\n// The range over which sound attenuates\n\n#define S_ATTENUATOR ((S_CLIPPING_DIST - S_CLOSE_DIST) >> FRACBITS)\n\n// Stereo separation\n\n#define S_STEREO_SWING (96 * FRACUNIT)\n\n#define NORM_PITCH 128\n#define NORM_PRIORITY 64\n#define NORM_SEP 128\n\ntypedef struct\n{\n    // sound information (if null, channel avail.)\n    sfxinfo_t *sfxinfo;\n\n    // origin of sound\n    mobj_t *origin;\n\n    // handle of the sound being played\n    int handle;\n    \n} channel_t;\n\n// The set of channels available\n\nstatic channel_t *channels;\n\n// Maximum volume of a sound effect.\n// Internal default is max out of 0-15.\n\nint sfxVolume = 8;\n\n// Maximum volume of music. \n\nint musicVolume = 8;\n\n// Internal volume level, ranging from 0-127\n\nstatic int snd_SfxVolume;\n\n// Whether songs are mus_paused\n\nstatic boolean mus_paused;        \n\n// Music currently being played\n\nstatic musicinfo_t *mus_playing = NULL;\n\n// Number of channels to use\n\nint snd_channels = 8;\n\n//\n// Initializes sound stuff, including volume\n// Sets channels, SFX and music volume,\n//  allocates channel buffer, sets S_sfx lookup.\n//\n\nvoid S_Init(int sfxVolume, int musicVolume)\n{  \n    int i;\n\n    I_PrecacheSounds(S_sfx, NUMSFX);\n\n    S_SetSfxVolume(sfxVolume);\n    S_SetMusicVolume(musicVolume);\n\n    // Allocating the internal channels for mixing\n    // (the maximum numer of sounds rendered\n    // simultaneously) within zone memory.\n    channels = Z_Malloc(snd_channels*sizeof(channel_t), PU_STATIC, 0);\n\n    // Free all channels for use\n    for (i=0 ; i<snd_channels ; i++)\n    {\n        channels[i].sfxinfo = 0;\n    }\n\n    // no sounds are playing, and they are not mus_paused\n    mus_paused = 0;\n\n    // Note that sounds have not been cached (yet).\n    for (i=1 ; i<NUMSFX ; i++)\n    {\n        S_sfx[i].lumpnum = S_sfx[i].usefulness = -1;\n    }\n\n    I_AtExit(S_Shutdown, true);\n}\n\nvoid S_Shutdown(void)\n{\n    I_ShutdownSound();\n    I_ShutdownMusic();\n}\n\nstatic void S_StopChannel(int cnum)\n{\n    int i;\n    channel_t *c;\n\n    c = &channels[cnum];\n\n    if (c->sfxinfo)\n    {\n        // stop the sound playing\n\n        if (I_SoundIsPlaying(c->handle))\n        {\n            I_StopSound(c->handle);\n        }\n\n        // check to see if other channels are playing the sound\n\n        for (i=0; i<snd_channels; i++)\n        {\n            if (cnum != i && c->sfxinfo == channels[i].sfxinfo)\n            {\n                break;\n            }\n        }\n        \n        // degrade usefulness of sound data\n\n        c->sfxinfo->usefulness--;\n        c->sfxinfo = NULL;\n    }\n}\n\n//\n// Per level startup code.\n// Kills playing sounds at start of level,\n//  determines music if any, changes music.\n//\n\nvoid S_Start(void)\n{\n    int cnum;\n    int mnum;\n\n    // kill all playing sounds at start of level\n    //  (trust me - a good idea)\n    for (cnum=0 ; cnum<snd_channels ; cnum++)\n    {\n        if (channels[cnum].sfxinfo)\n        {\n            S_StopChannel(cnum);\n        }\n    }\n\n    // start new music for the level\n    mus_paused = 0;\n\n    if (gamemode == commercial)\n    {\n        mnum = mus_runnin + gamemap - 1;\n    }\n    else\n    {\n        int spmus[]=\n        {\n            // Song - Who? - Where?\n\n            mus_e3m4,        // American     e4m1\n            mus_e3m2,        // Romero       e4m2\n            mus_e3m3,        // Shawn        e4m3\n            mus_e1m5,        // American     e4m4\n            mus_e2m7,        // Tim          e4m5\n            mus_e2m4,        // Romero       e4m6\n            mus_e2m6,        // J.Anderson   e4m7 CHIRON.WAD\n            mus_e2m5,        // Shawn        e4m8\n            mus_e1m9,        // Tim          e4m9\n        };\n\n        if (gameepisode < 4)\n        {\n            mnum = mus_e1m1 + (gameepisode-1)*9 + gamemap-1;\n        }\n        else\n        {\n            mnum = spmus[gamemap-1];\n        }\n    }        \n\n    S_ChangeMusic(mnum, true);\n}        \n\nvoid S_StopSound(mobj_t *origin)\n{\n    int cnum;\n\n    for (cnum=0 ; cnum<snd_channels ; cnum++)\n    {\n        if (channels[cnum].sfxinfo && channels[cnum].origin == origin)\n        {\n            S_StopChannel(cnum);\n            break;\n        }\n    }\n}\n\n//\n// S_GetChannel :\n//   If none available, return -1.  Otherwise channel #.\n//\n\nstatic int S_GetChannel(mobj_t *origin, sfxinfo_t *sfxinfo)\n{\n    // channel number to use\n    int                cnum;\n    \n    channel_t*        c;\n\n    // Find an open channel\n    for (cnum=0 ; cnum<snd_channels ; cnum++)\n    {\n        if (!channels[cnum].sfxinfo)\n        {\n            break;\n        }\n        else if (origin && channels[cnum].origin == origin)\n        {\n            S_StopChannel(cnum);\n            break;\n        }\n    }\n\n    // None available\n    if (cnum == snd_channels)\n    {\n        // Look for lower priority\n        for (cnum=0 ; cnum<snd_channels ; cnum++)\n        {\n            if (channels[cnum].sfxinfo->priority >= sfxinfo->priority)\n            {\n                break;\n            }\n        }\n\n        if (cnum == snd_channels)\n        {\n            // FUCK!  No lower priority.  Sorry, Charlie.    \n            return -1;\n        }\n        else\n        {\n            // Otherwise, kick out lower priority.\n            S_StopChannel(cnum);\n        }\n    }\n\n    c = &channels[cnum];\n\n    // channel is decided to be cnum.\n    c->sfxinfo = sfxinfo;\n    c->origin = origin;\n\n    return cnum;\n}\n\n//\n// Changes volume and stereo-separation variables\n//  from the norm of a sound effect to be played.\n// If the sound is not audible, returns a 0.\n// Otherwise, modifies parameters and returns 1.\n//\n\nstatic int S_AdjustSoundParams(mobj_t *listener, mobj_t *source,\n                               int *vol, int *sep)\n{\n    fixed_t        approx_dist;\n    fixed_t        adx;\n    fixed_t        ady;\n    angle_t        angle;\n\n    // calculate the distance to sound origin\n    //  and clip it if necessary\n    adx = abs(listener->x - source->x);\n    ady = abs(listener->y - source->y);\n\n    // From _GG1_ p.428. Appox. eucledian distance fast.\n    approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1);\n    \n    if (gamemap != 8 && approx_dist > S_CLIPPING_DIST)\n    {\n        return 0;\n    }\n    \n    // angle of source to listener\n    angle = R_PointToAngle2(listener->x,\n                            listener->y,\n                            source->x,\n                            source->y);\n\n    if (angle > listener->angle)\n    {\n        angle = angle - listener->angle;\n    }\n    else\n    {\n        angle = angle + (0xffffffff - listener->angle);\n    }\n\n    angle >>= ANGLETOFINESHIFT;\n\n    // stereo separation\n    *sep = 128 - (FixedMul(S_STEREO_SWING, finesine[angle]) >> FRACBITS);\n\n    // volume calculation\n    if (approx_dist < S_CLOSE_DIST)\n    {\n        *vol = snd_SfxVolume;\n    }\n    else if (gamemap == 8)\n    {\n        if (approx_dist > S_CLIPPING_DIST)\n        {\n            approx_dist = S_CLIPPING_DIST;\n        }\n\n        *vol = 15+ ((snd_SfxVolume-15)\n                    *((S_CLIPPING_DIST - approx_dist)>>FRACBITS))\n            / S_ATTENUATOR;\n    }\n    else\n    {\n        // distance effect\n        *vol = (snd_SfxVolume\n                * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS))\n            / S_ATTENUATOR; \n    }\n    \n    return (*vol > 0);\n}\n\nvoid S_StartSound(void *origin_p, int sfx_id)\n{\n    sfxinfo_t *sfx;\n    mobj_t *origin;\n    int rc;\n    int sep;\n    int cnum;\n    int volume;\n\n    origin = (mobj_t *) origin_p;\n    volume = snd_SfxVolume;\n\n    // check for bogus sound #\n    if (sfx_id < 1 || sfx_id > NUMSFX)\n    {\n        I_Error(\"Bad sfx #: %d\", sfx_id);\n    }\n\n    sfx = &S_sfx[sfx_id];\n\n    // Initialize sound parameters\n    if (sfx->link)\n    {\n        volume += sfx->volume;\n\n        if (volume < 1)\n        {\n            return;\n        }\n\n        if (volume > snd_SfxVolume)\n        {\n            volume = snd_SfxVolume;\n        }\n    }\n\n\n    // Check to see if it is audible,\n    //  and if not, modify the params\n    if (origin && origin != players[consoleplayer].mo)\n    {\n        rc = S_AdjustSoundParams(players[consoleplayer].mo,\n                                 origin,\n                                 &volume,\n                                 &sep);\n\n        if (origin->x == players[consoleplayer].mo->x\n         && origin->y == players[consoleplayer].mo->y)\n        {        \n            sep = NORM_SEP;\n        }\n\n        if (!rc)\n        {\n            return;\n        }\n    }        \n    else\n    {\n        sep = NORM_SEP;\n    }\n\n    // kill old sound\n    S_StopSound(origin);\n\n    // try to find a channel\n    cnum = S_GetChannel(origin, sfx);\n\n    if (cnum < 0)\n    {\n        return;\n    }\n\n    // increase the usefulness\n    if (sfx->usefulness++ < 0)\n    {\n        sfx->usefulness = 1;\n    }\n\n    if (sfx->lumpnum < 0)\n    {\n        sfx->lumpnum = I_GetSfxLumpNum(sfx);\n    }\n\n    channels[cnum].handle = I_StartSound(sfx, cnum, volume, sep);\n}        \n\n//\n// Stop and resume music, during game PAUSE.\n//\n\nvoid S_PauseSound(void)\n{\n    if (mus_playing && !mus_paused)\n    {\n        I_PauseSong();\n        mus_paused = true;\n    }\n}\n\nvoid S_ResumeSound(void)\n{\n    if (mus_playing && mus_paused)\n    {\n        I_ResumeSong();\n        mus_paused = false;\n    }\n}\n\n//\n// Updates music & sounds\n//\n\nvoid S_UpdateSounds(mobj_t *listener)\n{\n    int                audible;\n    int                cnum;\n    int                volume;\n    int                sep;\n    sfxinfo_t*        sfx;\n    channel_t*        c;\n\n    I_UpdateSound();\n\n    for (cnum=0; cnum<snd_channels; cnum++)\n    {\n        c = &channels[cnum];\n        sfx = c->sfxinfo;\n\n        if (c->sfxinfo)\n        {\n            if (I_SoundIsPlaying(c->handle))\n            {\n                // initialize parameters\n                volume = snd_SfxVolume;\n                sep = NORM_SEP;\n\n                if (sfx->link)\n                {\n                    volume += sfx->volume;\n                    if (volume < 1)\n                    {\n                        S_StopChannel(cnum);\n                        continue;\n                    }\n                    else if (volume > snd_SfxVolume)\n                    {\n                        volume = snd_SfxVolume;\n                    }\n                }\n\n                // check non-local sounds for distance clipping\n                //  or modify their params\n                if (c->origin && listener != c->origin)\n                {\n                    audible = S_AdjustSoundParams(listener,\n                                                  c->origin,\n                                                  &volume,\n                                                  &sep);\n                    \n                    if (!audible)\n                    {\n                        S_StopChannel(cnum);\n                    }\n                    else\n                    {\n                        I_UpdateSoundParams(c->handle, volume, sep);\n                    }\n                }\n            }\n            else\n            {\n                // if channel is allocated but sound has stopped,\n                //  free it\n                S_StopChannel(cnum);\n            }\n        }\n    }\n}\n\nvoid S_SetMusicVolume(int volume)\n{\n    if (volume < 0 || volume > 127)\n    {\n        I_Error(\"Attempt to set music volume at %d\",\n                volume);\n    }    \n\n    I_SetMusicVolume(volume);\n}\n\nvoid S_SetSfxVolume(int volume)\n{\n    if (volume < 0 || volume > 127)\n    {\n        I_Error(\"Attempt to set sfx volume at %d\", volume);\n    }\n\n    snd_SfxVolume = volume;\n}\n\n//\n// Starts some music with the music id found in sounds.h.\n//\n\nvoid S_StartMusic(int m_id)\n{\n    S_ChangeMusic(m_id, false);\n}\n\nvoid S_ChangeMusic(int musicnum, int looping)\n{\n    musicinfo_t *music = NULL;\n    char namebuf[9];\n    void *handle;\n\n    // The Doom IWAD file has two versions of the intro music: d_intro\n    // and d_introa.  The latter is used for OPL playback.\n\n    if (musicnum == mus_intro && (snd_musicdevice == SNDDEVICE_ADLIB\n                               || snd_musicdevice == SNDDEVICE_SB))\n    {\n        musicnum = mus_introa;\n    }\n\n    if (musicnum <= mus_None || musicnum >= NUMMUSIC)\n    {\n        I_Error(\"Bad music number %d\", musicnum);\n    }\n    else\n    {\n        music = &S_music[musicnum];\n    }\n\n    if (mus_playing == music)\n    {\n        return;\n    }\n\n    // shutdown old music\n    S_StopMusic();\n\n    // get lumpnum if neccessary\n    if (!music->lumpnum)\n    {\n        M_snprintf(namebuf, sizeof(namebuf), \"d_%s\", DEH_String(music->name));\n        music->lumpnum = W_GetNumForName(namebuf);\n    }\n\n    music->data = W_CacheLumpNum(music->lumpnum, PU_STATIC);\n\n    handle = I_RegisterSong(music->data, W_LumpLength(music->lumpnum));\n    music->handle = handle;\n    I_PlaySong(handle, looping);\n\n    mus_playing = music;\n}\n\nboolean S_MusicPlaying(void)\n{\n    return I_MusicIsPlaying();\n}\n\nvoid S_StopMusic(void)\n{\n    if (mus_playing)\n    {\n        if (mus_paused)\n        {\n            I_ResumeSong();\n        }\n\n        I_StopSong();\n        I_UnRegisterSong(mus_playing->handle);\n        W_ReleaseLumpNum(mus_playing->lumpnum);\n        mus_playing->data = NULL;\n        mus_playing = NULL;\n    }\n}\n\n"
  },
  {
    "path": "fbdoom/s_sound.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tThe not so system specific sound interface.\n//\n\n\n#ifndef __S_SOUND__\n#define __S_SOUND__\n\n#include \"p_mobj.h\"\n#include \"sounds.h\"\n\n//\n// Initializes sound stuff, including volume\n// Sets channels, SFX and music volume,\n//  allocates channel buffer, sets S_sfx lookup.\n//\n\nvoid S_Init(int sfxVolume, int musicVolume);\n\n\n// Shut down sound \n\nvoid S_Shutdown(void);\n\n\n\n//\n// Per level startup code.\n// Kills playing sounds at start of level,\n//  determines music if any, changes music.\n//\n\nvoid S_Start(void);\n\n//\n// Start sound for thing at <origin>\n//  using <sound_id> from sounds.h\n//\n\nvoid S_StartSound(void *origin, int sound_id);\n\n// Stop sound for thing at <origin>\nvoid S_StopSound(mobj_t *origin);\n\n\n// Start music using <music_id> from sounds.h\nvoid S_StartMusic(int music_id);\n\n// Start music using <music_id> from sounds.h,\n//  and set whether looping\nvoid S_ChangeMusic(int music_id, int looping);\n\n// query if music is playing\nboolean S_MusicPlaying(void);\n\n// Stops the music fer sure.\nvoid S_StopMusic(void);\n\n// Stop and resume music, during game PAUSE.\nvoid S_PauseSound(void);\nvoid S_ResumeSound(void);\n\n\n//\n// Updates music & sounds\n//\nvoid S_UpdateSounds(mobj_t *listener);\n\nvoid S_SetMusicVolume(int volume);\nvoid S_SetSfxVolume(int volume);\n\nextern int snd_channels;\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/sha1.c",
    "content": "/* sha1.c - SHA1 hash function\n *\tCopyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.\n *\n * Please see below for more legal information!\n *\n * This file is part of GnuPG.\n *\n * GnuPG is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 3 of the License, or\n * (at your option) any later version.\n *\n * GnuPG is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, see <http://www.gnu.org/licenses/>.\n */\n\n\n/*  Test vectors:\n *\n *  \"abc\"\n *  A999 3E36 4706 816A BA3E  2571 7850 C26C 9CD0 D89D\n *\n *  \"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\"\n *  8498 3E44 1C3B D26E BAAE  4AA1 F951 29E5 E546 70F1\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n#include \"i_swap.h\"\n#include \"sha1.h\"\n\nvoid SHA1_Init(sha1_context_t *hd)\n{\n    hd->h0 = 0x67452301;\n    hd->h1 = 0xefcdab89;\n    hd->h2 = 0x98badcfe;\n    hd->h3 = 0x10325476;\n    hd->h4 = 0xc3d2e1f0;\n    hd->nblocks = 0;\n    hd->count = 0;\n}\n\n\n/****************\n * Transform the message X which consists of 16 32-bit-words\n */\nstatic void Transform(sha1_context_t *hd, byte *data)\n{\n    uint32_t a,b,c,d,e,tm;\n    uint32_t x[16];\n\n    /* get values from the chaining vars */\n    a = hd->h0;\n    b = hd->h1;\n    c = hd->h2;\n    d = hd->h3;\n    e = hd->h4;\n\n#ifdef SYS_BIG_ENDIAN\n    memcpy(x, data, 64);\n#else\n    {\n        int i;\n        byte *p2;\n        for(i=0, p2=(byte*)x; i < 16; i++, p2 += 4 )\n        {\n            p2[3] = *data++;\n            p2[2] = *data++;\n            p2[1] = *data++;\n            p2[0] = *data++;\n        }\n    }\n#endif\n\n\n#define K1  0x5A827999L\n#define K2  0x6ED9EBA1L\n#define K3  0x8F1BBCDCL\n#define K4  0xCA62C1D6L\n#define F1(x,y,z)   ( z ^ ( x & ( y ^ z ) ) )\n#define F2(x,y,z)   ( x ^ y ^ z )\n#define F3(x,y,z)   ( ( x & y ) | ( z & ( x | y ) ) )\n#define F4(x,y,z)   ( x ^ y ^ z )\n\n#define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) )\n\n#define M(i) ( tm =   x[i&0x0f] ^ x[(i-14)&0x0f] \\\n\t\t    ^ x[(i-8)&0x0f] ^ x[(i-3)&0x0f] \\\n\t       , (x[i&0x0f] = rol(tm,1)) )\n\n#define R(a,b,c,d,e,f,k,m)  do { e += rol( a, 5 )     \\\n\t\t\t\t      + f( b, c, d )  \\\n\t\t\t\t      + k\t      \\\n\t\t\t\t      + m;\t      \\\n\t\t\t\t b = rol( b, 30 );    \\\n\t\t\t       } while(0)\n    R( a, b, c, d, e, F1, K1, x[ 0] );\n    R( e, a, b, c, d, F1, K1, x[ 1] );\n    R( d, e, a, b, c, F1, K1, x[ 2] );\n    R( c, d, e, a, b, F1, K1, x[ 3] );\n    R( b, c, d, e, a, F1, K1, x[ 4] );\n    R( a, b, c, d, e, F1, K1, x[ 5] );\n    R( e, a, b, c, d, F1, K1, x[ 6] );\n    R( d, e, a, b, c, F1, K1, x[ 7] );\n    R( c, d, e, a, b, F1, K1, x[ 8] );\n    R( b, c, d, e, a, F1, K1, x[ 9] );\n    R( a, b, c, d, e, F1, K1, x[10] );\n    R( e, a, b, c, d, F1, K1, x[11] );\n    R( d, e, a, b, c, F1, K1, x[12] );\n    R( c, d, e, a, b, F1, K1, x[13] );\n    R( b, c, d, e, a, F1, K1, x[14] );\n    R( a, b, c, d, e, F1, K1, x[15] );\n    R( e, a, b, c, d, F1, K1, M(16) );\n    R( d, e, a, b, c, F1, K1, M(17) );\n    R( c, d, e, a, b, F1, K1, M(18) );\n    R( b, c, d, e, a, F1, K1, M(19) );\n    R( a, b, c, d, e, F2, K2, M(20) );\n    R( e, a, b, c, d, F2, K2, M(21) );\n    R( d, e, a, b, c, F2, K2, M(22) );\n    R( c, d, e, a, b, F2, K2, M(23) );\n    R( b, c, d, e, a, F2, K2, M(24) );\n    R( a, b, c, d, e, F2, K2, M(25) );\n    R( e, a, b, c, d, F2, K2, M(26) );\n    R( d, e, a, b, c, F2, K2, M(27) );\n    R( c, d, e, a, b, F2, K2, M(28) );\n    R( b, c, d, e, a, F2, K2, M(29) );\n    R( a, b, c, d, e, F2, K2, M(30) );\n    R( e, a, b, c, d, F2, K2, M(31) );\n    R( d, e, a, b, c, F2, K2, M(32) );\n    R( c, d, e, a, b, F2, K2, M(33) );\n    R( b, c, d, e, a, F2, K2, M(34) );\n    R( a, b, c, d, e, F2, K2, M(35) );\n    R( e, a, b, c, d, F2, K2, M(36) );\n    R( d, e, a, b, c, F2, K2, M(37) );\n    R( c, d, e, a, b, F2, K2, M(38) );\n    R( b, c, d, e, a, F2, K2, M(39) );\n    R( a, b, c, d, e, F3, K3, M(40) );\n    R( e, a, b, c, d, F3, K3, M(41) );\n    R( d, e, a, b, c, F3, K3, M(42) );\n    R( c, d, e, a, b, F3, K3, M(43) );\n    R( b, c, d, e, a, F3, K3, M(44) );\n    R( a, b, c, d, e, F3, K3, M(45) );\n    R( e, a, b, c, d, F3, K3, M(46) );\n    R( d, e, a, b, c, F3, K3, M(47) );\n    R( c, d, e, a, b, F3, K3, M(48) );\n    R( b, c, d, e, a, F3, K3, M(49) );\n    R( a, b, c, d, e, F3, K3, M(50) );\n    R( e, a, b, c, d, F3, K3, M(51) );\n    R( d, e, a, b, c, F3, K3, M(52) );\n    R( c, d, e, a, b, F3, K3, M(53) );\n    R( b, c, d, e, a, F3, K3, M(54) );\n    R( a, b, c, d, e, F3, K3, M(55) );\n    R( e, a, b, c, d, F3, K3, M(56) );\n    R( d, e, a, b, c, F3, K3, M(57) );\n    R( c, d, e, a, b, F3, K3, M(58) );\n    R( b, c, d, e, a, F3, K3, M(59) );\n    R( a, b, c, d, e, F4, K4, M(60) );\n    R( e, a, b, c, d, F4, K4, M(61) );\n    R( d, e, a, b, c, F4, K4, M(62) );\n    R( c, d, e, a, b, F4, K4, M(63) );\n    R( b, c, d, e, a, F4, K4, M(64) );\n    R( a, b, c, d, e, F4, K4, M(65) );\n    R( e, a, b, c, d, F4, K4, M(66) );\n    R( d, e, a, b, c, F4, K4, M(67) );\n    R( c, d, e, a, b, F4, K4, M(68) );\n    R( b, c, d, e, a, F4, K4, M(69) );\n    R( a, b, c, d, e, F4, K4, M(70) );\n    R( e, a, b, c, d, F4, K4, M(71) );\n    R( d, e, a, b, c, F4, K4, M(72) );\n    R( c, d, e, a, b, F4, K4, M(73) );\n    R( b, c, d, e, a, F4, K4, M(74) );\n    R( a, b, c, d, e, F4, K4, M(75) );\n    R( e, a, b, c, d, F4, K4, M(76) );\n    R( d, e, a, b, c, F4, K4, M(77) );\n    R( c, d, e, a, b, F4, K4, M(78) );\n    R( b, c, d, e, a, F4, K4, M(79) );\n\n    /* update chainig vars */\n    hd->h0 += a;\n    hd->h1 += b;\n    hd->h2 += c;\n    hd->h3 += d;\n    hd->h4 += e;\n}\n\n\n/* Update the message digest with the contents\n * of INBUF with length INLEN.\n */\nvoid SHA1_Update(sha1_context_t *hd, byte *inbuf, size_t inlen)\n{\n    if (hd->count == 64)\n    {\n        /* flush the buffer */\n\tTransform(hd, hd->buf);\n\thd->count = 0;\n\thd->nblocks++;\n    }\n    if (!inbuf)\n\treturn;\n    if (hd->count)\n    {\n\tfor (; inlen && hd->count < 64; inlen--)\n\t    hd->buf[hd->count++] = *inbuf++;\n\tSHA1_Update(hd, NULL, 0);\n\tif (!inlen)\n\t    return;\n    }\n\n    while (inlen >= 64)\n    {\n\tTransform(hd, inbuf);\n\thd->count = 0;\n\thd->nblocks++;\n\tinlen -= 64;\n\tinbuf += 64;\n    }\n    for (; inlen && hd->count < 64; inlen--)\n\thd->buf[hd->count++] = *inbuf++;\n}\n\n\n/* The routine final terminates the computation and\n * returns the digest.\n * The handle is prepared for a new cycle, but adding bytes to the\n * handle will the destroy the returned buffer.\n * Returns: 20 bytes representing the digest.\n */\n\nvoid SHA1_Final(sha1_digest_t digest, sha1_context_t *hd)\n{\n    uint32_t t, msb, lsb;\n    byte *p;\n\n    SHA1_Update(hd, NULL, 0); /* flush */;\n\n    t = hd->nblocks;\n    /* multiply by 64 to make a byte count */\n    lsb = t << 6;\n    msb = t >> 26;\n    /* add the count */\n    t = lsb;\n    if ((lsb += hd->count) < t)\n\tmsb++;\n    /* multiply by 8 to make a bit count */\n    t = lsb;\n    lsb <<= 3;\n    msb <<= 3;\n    msb |= t >> 29;\n\n    if (hd->count < 56)\n    {\n        /* enough room */\n\thd->buf[hd->count++] = 0x80; /* pad */\n\twhile (hd->count < 56)\n\t    hd->buf[hd->count++] = 0;  /* pad */\n    }\n    else\n    {\n        /* need one extra block */\n\thd->buf[hd->count++] = 0x80; /* pad character */\n\twhile (hd->count < 64)\n\t    hd->buf[hd->count++] = 0;\n\tSHA1_Update(hd, NULL, 0);  /* flush */;\n\tmemset(hd->buf, 0, 56 ); /* fill next block with zeroes */\n    }\n    /* append the 64 bit count */\n    hd->buf[56] = msb >> 24;\n    hd->buf[57] = msb >> 16;\n    hd->buf[58] = msb >>  8;\n    hd->buf[59] = msb\t   ;\n    hd->buf[60] = lsb >> 24;\n    hd->buf[61] = lsb >> 16;\n    hd->buf[62] = lsb >>  8;\n    hd->buf[63] = lsb\t   ;\n    Transform(hd, hd->buf);\n\n    p = hd->buf;\n#ifdef SYS_BIG_ENDIAN\n#define X(a) do { *(uint32_t*)p = hd->h##a ; p += 4; } while(0)\n#else /* little endian */\n#define X(a) do { *p++ = hd->h##a >> 24; *p++ = hd->h##a >> 16;\t \\\n\t\t      *p++ = hd->h##a >> 8; *p++ = hd->h##a; } while(0)\n#endif\n    X(0);\n    X(1);\n    X(2);\n    X(3);\n    X(4);\n#undef X\n\n    memcpy(digest, hd->buf, sizeof(sha1_digest_t));\n}\n\nvoid SHA1_UpdateInt32(sha1_context_t *context, unsigned int val)\n{\n    byte buf[4];\n\n    buf[0] = (val >> 24) & 0xff;\n    buf[1] = (val >> 16) & 0xff;\n    buf[2] = (val >> 8) & 0xff;\n    buf[3] = val & 0xff;\n\n    SHA1_Update(context, buf, 4);\n}\n\nvoid SHA1_UpdateString(sha1_context_t *context, char *str)\n{\n    SHA1_Update(context, (byte *) str, strlen(str) + 1);\n}\n\n"
  },
  {
    "path": "fbdoom/sha1.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     SHA-1 digest.\n//\n\n#ifndef __SHA1_H__\n#define __SHA1_H__\n\n#include \"doomtype.h\"\n\ntypedef struct sha1_context_s sha1_context_t;\ntypedef byte sha1_digest_t[20];\n\nstruct sha1_context_s {\n    uint32_t h0,h1,h2,h3,h4;\n    uint32_t nblocks;\n    byte buf[64];\n    int count;\n};\n\nvoid SHA1_Init(sha1_context_t *context);\nvoid SHA1_Update(sha1_context_t *context, byte *buf, size_t len);\nvoid SHA1_Final(sha1_digest_t digest, sha1_context_t *context);\nvoid SHA1_UpdateInt32(sha1_context_t *context, unsigned int val);\nvoid SHA1_UpdateString(sha1_context_t *context, char *str);\n\n#endif /* #ifndef __SHA1_H__ */\n\n"
  },
  {
    "path": "fbdoom/sounds.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tCreated by a sound utility.\n//\tKept as a sample, DOOM2 sounds.\n//\n\n\n#include <stdlib.h>\n\n\n#include \"doomtype.h\"\n#include \"sounds.h\"\n\n//\n// Information about all the music\n//\n\n#define MUSIC(name) \\\n    { name, 0, NULL, NULL }\n\nmusicinfo_t S_music[] =\n{\n    MUSIC(NULL),\n    MUSIC(\"e1m1\"),\n    MUSIC(\"e1m2\"),\n    MUSIC(\"e1m3\"),\n    MUSIC(\"e1m4\"),\n    MUSIC(\"e1m5\"),\n    MUSIC(\"e1m6\"),\n    MUSIC(\"e1m7\"),\n    MUSIC(\"e1m8\"),\n    MUSIC(\"e1m9\"),\n    MUSIC(\"e2m1\"),\n    MUSIC(\"e2m2\"),\n    MUSIC(\"e2m3\"),\n    MUSIC(\"e2m4\"),\n    MUSIC(\"e2m5\"),\n    MUSIC(\"e2m6\"),\n    MUSIC(\"e2m7\"),\n    MUSIC(\"e2m8\"),\n    MUSIC(\"e2m9\"),\n    MUSIC(\"e3m1\"),\n    MUSIC(\"e3m2\"),\n    MUSIC(\"e3m3\"),\n    MUSIC(\"e3m4\"),\n    MUSIC(\"e3m5\"),\n    MUSIC(\"e3m6\"),\n    MUSIC(\"e3m7\"),\n    MUSIC(\"e3m8\"),\n    MUSIC(\"e3m9\"),\n    MUSIC(\"inter\"),\n    MUSIC(\"intro\"),\n    MUSIC(\"bunny\"),\n    MUSIC(\"victor\"),\n    MUSIC(\"introa\"),\n    MUSIC(\"runnin\"),\n    MUSIC(\"stalks\"),\n    MUSIC(\"countd\"),\n    MUSIC(\"betwee\"),\n    MUSIC(\"doom\"),\n    MUSIC(\"the_da\"),\n    MUSIC(\"shawn\"),\n    MUSIC(\"ddtblu\"),\n    MUSIC(\"in_cit\"),\n    MUSIC(\"dead\"),\n    MUSIC(\"stlks2\"),\n    MUSIC(\"theda2\"),\n    MUSIC(\"doom2\"),\n    MUSIC(\"ddtbl2\"),\n    MUSIC(\"runni2\"),\n    MUSIC(\"dead2\"),\n    MUSIC(\"stlks3\"),\n    MUSIC(\"romero\"),\n    MUSIC(\"shawn2\"),\n    MUSIC(\"messag\"),\n    MUSIC(\"count2\"),\n    MUSIC(\"ddtbl3\"),\n    MUSIC(\"ampie\"),\n    MUSIC(\"theda3\"),\n    MUSIC(\"adrian\"),\n    MUSIC(\"messg2\"),\n    MUSIC(\"romer2\"),\n    MUSIC(\"tense\"),\n    MUSIC(\"shawn3\"),\n    MUSIC(\"openin\"),\n    MUSIC(\"evil\"),\n    MUSIC(\"ultima\"),\n    MUSIC(\"read_m\"),\n    MUSIC(\"dm2ttl\"),\n    MUSIC(\"dm2int\") \n};\n\n\n//\n// Information about all the sfx\n//\n\n#define SOUND(name, priority) \\\n  { NULL, name, priority, NULL, -1, -1, 0, 0, -1, NULL }\n#define SOUND_LINK(name, priority, link_id, pitch, volume) \\\n  { NULL, name, priority, &S_sfx[link_id], pitch, volume, 0, 0, -1, NULL }\n\nsfxinfo_t S_sfx[] =\n{\n  // S_sfx[0] needs to be a dummy for odd reasons.\n  SOUND(\"none\",   0),\n  SOUND(\"pistol\", 64),\n  SOUND(\"shotgn\", 64),\n  SOUND(\"sgcock\", 64),\n  SOUND(\"dshtgn\", 64),\n  SOUND(\"dbopn\",  64),\n  SOUND(\"dbcls\",  64),\n  SOUND(\"dbload\", 64),\n  SOUND(\"plasma\", 64),\n  SOUND(\"bfg\",    64),\n  SOUND(\"sawup\",  64),\n  SOUND(\"sawidl\", 118),\n  SOUND(\"sawful\", 64),\n  SOUND(\"sawhit\", 64),\n  SOUND(\"rlaunc\", 64),\n  SOUND(\"rxplod\", 70),\n  SOUND(\"firsht\", 70),\n  SOUND(\"firxpl\", 70),\n  SOUND(\"pstart\", 100),\n  SOUND(\"pstop\",  100),\n  SOUND(\"doropn\", 100),\n  SOUND(\"dorcls\", 100),\n  SOUND(\"stnmov\", 119),\n  SOUND(\"swtchn\", 78),\n  SOUND(\"swtchx\", 78),\n  SOUND(\"plpain\", 96),\n  SOUND(\"dmpain\", 96),\n  SOUND(\"popain\", 96),\n  SOUND(\"vipain\", 96),\n  SOUND(\"mnpain\", 96),\n  SOUND(\"pepain\", 96),\n  SOUND(\"slop\",   78),\n  SOUND(\"itemup\", 78),\n  SOUND(\"wpnup\",  78),\n  SOUND(\"oof\",    96),\n  SOUND(\"telept\", 32),\n  SOUND(\"posit1\", 98),\n  SOUND(\"posit2\", 98),\n  SOUND(\"posit3\", 98),\n  SOUND(\"bgsit1\", 98),\n  SOUND(\"bgsit2\", 98),\n  SOUND(\"sgtsit\", 98),\n  SOUND(\"cacsit\", 98),\n  SOUND(\"brssit\", 94),\n  SOUND(\"cybsit\", 92),\n  SOUND(\"spisit\", 90),\n  SOUND(\"bspsit\", 90),\n  SOUND(\"kntsit\", 90),\n  SOUND(\"vilsit\", 90),\n  SOUND(\"mansit\", 90),\n  SOUND(\"pesit\",  90),\n  SOUND(\"sklatk\", 70),\n  SOUND(\"sgtatk\", 70),\n  SOUND(\"skepch\", 70),\n  SOUND(\"vilatk\", 70),\n  SOUND(\"claw\",   70),\n  SOUND(\"skeswg\", 70),\n  SOUND(\"pldeth\", 32),\n  SOUND(\"pdiehi\", 32),\n  SOUND(\"podth1\", 70),\n  SOUND(\"podth2\", 70),\n  SOUND(\"podth3\", 70),\n  SOUND(\"bgdth1\", 70),\n  SOUND(\"bgdth2\", 70),\n  SOUND(\"sgtdth\", 70),\n  SOUND(\"cacdth\", 70),\n  SOUND(\"skldth\", 70),\n  SOUND(\"brsdth\", 32),\n  SOUND(\"cybdth\", 32),\n  SOUND(\"spidth\", 32),\n  SOUND(\"bspdth\", 32),\n  SOUND(\"vildth\", 32),\n  SOUND(\"kntdth\", 32),\n  SOUND(\"pedth\",  32),\n  SOUND(\"skedth\", 32),\n  SOUND(\"posact\", 120),\n  SOUND(\"bgact\",  120),\n  SOUND(\"dmact\",  120),\n  SOUND(\"bspact\", 100),\n  SOUND(\"bspwlk\", 100),\n  SOUND(\"vilact\", 100),\n  SOUND(\"noway\",  78),\n  SOUND(\"barexp\", 60),\n  SOUND(\"punch\",  64),\n  SOUND(\"hoof\",   70),\n  SOUND(\"metal\",  70),\n  SOUND_LINK(\"chgun\", 64, sfx_pistol, 150, 0),\n  SOUND(\"tink\",   60),\n  SOUND(\"bdopn\",  100),\n  SOUND(\"bdcls\",  100),\n  SOUND(\"itmbk\",  100),\n  SOUND(\"flame\",  32),\n  SOUND(\"flamst\", 32),\n  SOUND(\"getpow\", 60),\n  SOUND(\"bospit\", 70),\n  SOUND(\"boscub\", 70),\n  SOUND(\"bossit\", 70),\n  SOUND(\"bospn\",  70),\n  SOUND(\"bosdth\", 70),\n  SOUND(\"manatk\", 70),\n  SOUND(\"mandth\", 70),\n  SOUND(\"sssit\",  70),\n  SOUND(\"ssdth\",  70),\n  SOUND(\"keenpn\", 70),\n  SOUND(\"keendt\", 70),\n  SOUND(\"skeact\", 70),\n  SOUND(\"skesit\", 70),\n  SOUND(\"skeatk\", 70),\n  SOUND(\"radio\",  60),\n};\n\n"
  },
  {
    "path": "fbdoom/sounds.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tCreated by the sound utility written by Dave Taylor.\n//\tKept as a sample, DOOM2  sounds. Frozen.\n//\n\n#ifndef __SOUNDS__\n#define __SOUNDS__\n\n#include \"i_sound.h\"\n\n// the complete set of sound effects\nextern sfxinfo_t\tS_sfx[];\n\n// the complete set of music\nextern musicinfo_t\tS_music[];\n\n//\n// Identifiers for all music in game.\n//\n\ntypedef enum\n{\n    mus_None,\n    mus_e1m1,\n    mus_e1m2,\n    mus_e1m3,\n    mus_e1m4,\n    mus_e1m5,\n    mus_e1m6,\n    mus_e1m7,\n    mus_e1m8,\n    mus_e1m9,\n    mus_e2m1,\n    mus_e2m2,\n    mus_e2m3,\n    mus_e2m4,\n    mus_e2m5,\n    mus_e2m6,\n    mus_e2m7,\n    mus_e2m8,\n    mus_e2m9,\n    mus_e3m1,\n    mus_e3m2,\n    mus_e3m3,\n    mus_e3m4,\n    mus_e3m5,\n    mus_e3m6,\n    mus_e3m7,\n    mus_e3m8,\n    mus_e3m9,\n    mus_inter,\n    mus_intro,\n    mus_bunny,\n    mus_victor,\n    mus_introa,\n    mus_runnin,\n    mus_stalks,\n    mus_countd,\n    mus_betwee,\n    mus_doom,\n    mus_the_da,\n    mus_shawn,\n    mus_ddtblu,\n    mus_in_cit,\n    mus_dead,\n    mus_stlks2,\n    mus_theda2,\n    mus_doom2,\n    mus_ddtbl2,\n    mus_runni2,\n    mus_dead2,\n    mus_stlks3,\n    mus_romero,\n    mus_shawn2,\n    mus_messag,\n    mus_count2,\n    mus_ddtbl3,\n    mus_ampie,\n    mus_theda3,\n    mus_adrian,\n    mus_messg2,\n    mus_romer2,\n    mus_tense,\n    mus_shawn3,\n    mus_openin,\n    mus_evil,\n    mus_ultima,\n    mus_read_m,\n    mus_dm2ttl,\n    mus_dm2int,\n    NUMMUSIC\n} musicenum_t;\n\n\n//\n// Identifiers for all sfx in game.\n//\n\ntypedef enum\n{\n    sfx_None,\n    sfx_pistol,\n    sfx_shotgn,\n    sfx_sgcock,\n    sfx_dshtgn,\n    sfx_dbopn,\n    sfx_dbcls,\n    sfx_dbload,\n    sfx_plasma,\n    sfx_bfg,\n    sfx_sawup,\n    sfx_sawidl,\n    sfx_sawful,\n    sfx_sawhit,\n    sfx_rlaunc,\n    sfx_rxplod,\n    sfx_firsht,\n    sfx_firxpl,\n    sfx_pstart,\n    sfx_pstop,\n    sfx_doropn,\n    sfx_dorcls,\n    sfx_stnmov,\n    sfx_swtchn,\n    sfx_swtchx,\n    sfx_plpain,\n    sfx_dmpain,\n    sfx_popain,\n    sfx_vipain,\n    sfx_mnpain,\n    sfx_pepain,\n    sfx_slop,\n    sfx_itemup,\n    sfx_wpnup,\n    sfx_oof,\n    sfx_telept,\n    sfx_posit1,\n    sfx_posit2,\n    sfx_posit3,\n    sfx_bgsit1,\n    sfx_bgsit2,\n    sfx_sgtsit,\n    sfx_cacsit,\n    sfx_brssit,\n    sfx_cybsit,\n    sfx_spisit,\n    sfx_bspsit,\n    sfx_kntsit,\n    sfx_vilsit,\n    sfx_mansit,\n    sfx_pesit,\n    sfx_sklatk,\n    sfx_sgtatk,\n    sfx_skepch,\n    sfx_vilatk,\n    sfx_claw,\n    sfx_skeswg,\n    sfx_pldeth,\n    sfx_pdiehi,\n    sfx_podth1,\n    sfx_podth2,\n    sfx_podth3,\n    sfx_bgdth1,\n    sfx_bgdth2,\n    sfx_sgtdth,\n    sfx_cacdth,\n    sfx_skldth,\n    sfx_brsdth,\n    sfx_cybdth,\n    sfx_spidth,\n    sfx_bspdth,\n    sfx_vildth,\n    sfx_kntdth,\n    sfx_pedth,\n    sfx_skedth,\n    sfx_posact,\n    sfx_bgact,\n    sfx_dmact,\n    sfx_bspact,\n    sfx_bspwlk,\n    sfx_vilact,\n    sfx_noway,\n    sfx_barexp,\n    sfx_punch,\n    sfx_hoof,\n    sfx_metal,\n    sfx_chgun,\n    sfx_tink,\n    sfx_bdopn,\n    sfx_bdcls,\n    sfx_itmbk,\n    sfx_flame,\n    sfx_flamst,\n    sfx_getpow,\n    sfx_bospit,\n    sfx_boscub,\n    sfx_bossit,\n    sfx_bospn,\n    sfx_bosdth,\n    sfx_manatk,\n    sfx_mandth,\n    sfx_sssit,\n    sfx_ssdth,\n    sfx_keenpn,\n    sfx_keendt,\n    sfx_skeact,\n    sfx_skesit,\n    sfx_skeatk,\n    sfx_radio,\n    NUMSFX\n} sfxenum_t;\n\n#endif\n"
  },
  {
    "path": "fbdoom/st_lib.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tThe status bar widget code.\n//\n\n\n#include <stdio.h>\n#include <ctype.h>\n\n#include \"deh_main.h\"\n#include \"doomdef.h\"\n\n#include \"z_zone.h\"\n#include \"v_video.h\"\n\n#include \"i_swap.h\"\n#include \"i_system.h\"\n\n#include \"w_wad.h\"\n\n#include \"st_stuff.h\"\n#include \"st_lib.h\"\n#include \"r_local.h\"\n\n\n// in AM_map.c\nextern boolean\t\tautomapactive; \n\n\n\n\n//\n// Hack display negative frags.\n//  Loads and store the stminus lump.\n//\npatch_t*\t\tsttminus;\n\nvoid STlib_init(void)\n{\n    sttminus = (patch_t *) W_CacheLumpName(DEH_String(\"STTMINUS\"), PU_STATIC);\n}\n\n\n// ?\nvoid\nSTlib_initNum\n( st_number_t*\t\tn,\n  int\t\t\tx,\n  int\t\t\ty,\n  patch_t**\t\tpl,\n  int*\t\t\tnum,\n  boolean*\t\ton,\n  int\t\t\twidth )\n{\n    n->x\t= x;\n    n->y\t= y;\n    n->oldnum\t= 0;\n    n->width\t= width;\n    n->num\t= num;\n    n->on\t= on;\n    n->p\t= pl;\n}\n\n\n// \n// A fairly efficient way to draw a number\n//  based on differences from the old number.\n// Note: worth the trouble?\n//\nvoid\nSTlib_drawNum\n( st_number_t*\tn,\n  boolean\trefresh )\n{\n\n    int\t\tnumdigits = n->width;\n    int\t\tnum = *n->num;\n    \n    int\t\tw = SHORT(n->p[0]->width);\n    int\t\th = SHORT(n->p[0]->height);\n    int\t\tx = n->x;\n    \n    int\t\tneg;\n\n    n->oldnum = *n->num;\n\n    neg = num < 0;\n\n    if (neg)\n    {\n\tif (numdigits == 2 && num < -9)\n\t    num = -9;\n\telse if (numdigits == 3 && num < -99)\n\t    num = -99;\n\t\n\tnum = -num;\n    }\n\n    // clear the area\n    x = n->x - numdigits*w;\n\n    if (n->y - ST_Y < 0)\n\tI_Error(\"drawNum: n->y - ST_Y < 0\");\n\n    V_CopyRect(x, n->y - ST_Y, st_backing_screen, w*numdigits, h, x, n->y);\n\n    // if non-number, do not draw it\n    if (num == 1994)\n\treturn;\n\n    x = n->x;\n\n    // in the special case of 0, you draw 0\n    if (!num)\n\tV_DrawPatch(x - w, n->y, n->p[ 0 ]);\n\n    // draw the new number\n    while (num && numdigits--)\n    {\n\tx -= w;\n\tV_DrawPatch(x, n->y, n->p[ num % 10 ]);\n\tnum /= 10;\n    }\n\n    // draw a minus sign if necessary\n    if (neg)\n\tV_DrawPatch(x - 8, n->y, sttminus);\n}\n\n\n//\nvoid\nSTlib_updateNum\n( st_number_t*\t\tn,\n  boolean\t\trefresh )\n{\n    if (*n->on) STlib_drawNum(n, refresh);\n}\n\n\n//\nvoid\nSTlib_initPercent\n( st_percent_t*\t\tp,\n  int\t\t\tx,\n  int\t\t\ty,\n  patch_t**\t\tpl,\n  int*\t\t\tnum,\n  boolean*\t\ton,\n  patch_t*\t\tpercent )\n{\n    STlib_initNum(&p->n, x, y, pl, num, on, 3);\n    p->p = percent;\n}\n\n\n\n\nvoid\nSTlib_updatePercent\n( st_percent_t*\t\tper,\n  int\t\t\trefresh )\n{\n    if (refresh && *per->n.on)\n\tV_DrawPatch(per->n.x, per->n.y, per->p);\n    \n    STlib_updateNum(&per->n, refresh);\n}\n\n\n\nvoid\nSTlib_initMultIcon\n( st_multicon_t*\ti,\n  int\t\t\tx,\n  int\t\t\ty,\n  patch_t**\t\til,\n  int*\t\t\tinum,\n  boolean*\t\ton )\n{\n    i->x\t= x;\n    i->y\t= y;\n    i->oldinum \t= -1;\n    i->inum\t= inum;\n    i->on\t= on;\n    i->p\t= il;\n}\n\n\n\nvoid\nSTlib_updateMultIcon\n( st_multicon_t*\tmi,\n  boolean\t\trefresh )\n{\n    int\t\t\tw;\n    int\t\t\th;\n    int\t\t\tx;\n    int\t\t\ty;\n\n    if (*mi->on && (mi->oldinum != *mi->inum || refresh) && (*mi->inum != -1))\n    {\n\tif (mi->oldinum != -1)\n\t{\n\t    x = mi->x - SHORT(mi->p[mi->oldinum]->leftoffset);\n\t    y = mi->y - SHORT(mi->p[mi->oldinum]->topoffset);\n\t    w = SHORT(mi->p[mi->oldinum]->width);\n\t    h = SHORT(mi->p[mi->oldinum]->height);\n\n\t    if (y - ST_Y < 0)\n\t\tI_Error(\"updateMultIcon: y - ST_Y < 0\");\n\n\t    V_CopyRect(x, y-ST_Y, st_backing_screen, w, h, x, y);\n\t}\n\tV_DrawPatch(mi->x, mi->y, mi->p[*mi->inum]);\n\tmi->oldinum = *mi->inum;\n    }\n}\n\n\n\nvoid\nSTlib_initBinIcon\n( st_binicon_t*\t\tb,\n  int\t\t\tx,\n  int\t\t\ty,\n  patch_t*\t\ti,\n  boolean*\t\tval,\n  boolean*\t\ton )\n{\n    b->x\t= x;\n    b->y\t= y;\n    b->oldval\t= false;\n    b->val\t= val;\n    b->on\t= on;\n    b->p\t= i;\n}\n\n\n\nvoid\nSTlib_updateBinIcon\n( st_binicon_t*\t\tbi,\n  boolean\t\trefresh )\n{\n    int\t\t\tx;\n    int\t\t\ty;\n    int\t\t\tw;\n    int\t\t\th;\n\n    if (*bi->on\n     && (bi->oldval != *bi->val || refresh))\n    {\n\tx = bi->x - SHORT(bi->p->leftoffset);\n\ty = bi->y - SHORT(bi->p->topoffset);\n\tw = SHORT(bi->p->width);\n\th = SHORT(bi->p->height);\n\n\tif (y - ST_Y < 0)\n\t    I_Error(\"updateBinIcon: y - ST_Y < 0\");\n\n\tif (*bi->val)\n\t    V_DrawPatch(bi->x, bi->y, bi->p);\n\telse\n\t    V_CopyRect(x, y-ST_Y, st_backing_screen, w, h, x, y);\n\n\tbi->oldval = *bi->val;\n    }\n\n}\n\n"
  },
  {
    "path": "fbdoom/st_lib.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n// \tThe status bar widget code.\n//\n\n#ifndef __STLIB__\n#define __STLIB__\n\n\n// We are referring to patches.\n#include \"r_defs.h\"\n\n//\n// Typedefs of widgets\n//\n\n// Number widget\n\ntypedef struct\n{\n    // upper right-hand corner\n    //  of the number (right-justified)\n    int\t\tx;\n    int\t\ty;\n\n    // max # of digits in number\n    int width;    \n\n    // last number value\n    int\t\toldnum;\n    \n    // pointer to current value\n    int*\tnum;\n\n    // pointer to boolean stating\n    //  whether to update number\n    boolean*\ton;\n\n    // list of patches for 0-9\n    patch_t**\tp;\n\n    // user data\n    int data;\n    \n} st_number_t;\n\n\n\n// Percent widget (\"child\" of number widget,\n//  or, more precisely, contains a number widget.)\ntypedef struct\n{\n    // number information\n    st_number_t\t\tn;\n\n    // percent sign graphic\n    patch_t*\t\tp;\n    \n} st_percent_t;\n\n\n\n// Multiple Icon widget\ntypedef struct\n{\n     // center-justified location of icons\n    int\t\t\tx;\n    int\t\t\ty;\n\n    // last icon number\n    int\t\t\toldinum;\n\n    // pointer to current icon\n    int*\t\tinum;\n\n    // pointer to boolean stating\n    //  whether to update icon\n    boolean*\t\ton;\n\n    // list of icons\n    patch_t**\t\tp;\n    \n    // user data\n    int\t\t\tdata;\n    \n} st_multicon_t;\n\n\n\n\n// Binary Icon widget\n\ntypedef struct\n{\n    // center-justified location of icon\n    int\t\t\tx;\n    int\t\t\ty;\n\n    // last icon value\n    boolean\t\toldval;\n\n    // pointer to current icon status\n    boolean*\t\tval;\n\n    // pointer to boolean\n    //  stating whether to update icon\n    boolean*\t\ton;  \n\n\n    patch_t*\t\tp;\t// icon\n    int\t\t\tdata;   // user data\n    \n} st_binicon_t;\n\n\n\n//\n// Widget creation, access, and update routines\n//\n\n// Initializes widget library.\n// More precisely, initialize STMINUS,\n//  everything else is done somewhere else.\n//\nvoid STlib_init(void);\n\n\n\n// Number widget routines\nvoid\nSTlib_initNum\n( st_number_t*\t\tn,\n  int\t\t\tx,\n  int\t\t\ty,\n  patch_t**\t\tpl,\n  int*\t\t\tnum,\n  boolean*\t\ton,\n  int\t\t\twidth );\n\nvoid\nSTlib_updateNum\n( st_number_t*\t\tn,\n  boolean\t\trefresh );\n\n\n// Percent widget routines\nvoid\nSTlib_initPercent\n( st_percent_t*\t\tp,\n  int\t\t\tx,\n  int\t\t\ty,\n  patch_t**\t\tpl,\n  int*\t\t\tnum,\n  boolean*\t\ton,\n  patch_t*\t\tpercent );\n\n\nvoid\nSTlib_updatePercent\n( st_percent_t*\t\tper,\n  int\t\t\trefresh );\n\n\n// Multiple Icon widget routines\nvoid\nSTlib_initMultIcon\n( st_multicon_t*\tmi,\n  int\t\t\tx,\n  int\t\t\ty,\n  patch_t**\t\til,\n  int*\t\t\tinum,\n  boolean*\t\ton );\n\n\nvoid\nSTlib_updateMultIcon\n( st_multicon_t*\tmi,\n  boolean\t\trefresh );\n\n// Binary Icon widget routines\n\nvoid\nSTlib_initBinIcon\n( st_binicon_t*\t\tb,\n  int\t\t\tx,\n  int\t\t\ty,\n  patch_t*\t\ti,\n  boolean*\t\tval,\n  boolean*\t\ton );\n\nvoid\nSTlib_updateBinIcon\n( st_binicon_t*\t\tbi,\n  boolean\t\trefresh );\n\n#endif\n"
  },
  {
    "path": "fbdoom/st_stuff.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tStatus bar code.\n//\tDoes the face/direction indicator animatin.\n//\tDoes palette indicators as well (red pain/berserk, bright pickup)\n//\n\n\n\n#include <stdio.h>\n\n#include \"i_system.h\"\n#include \"i_video.h\"\n#include \"z_zone.h\"\n#include \"m_misc.h\"\n#include \"m_random.h\"\n#include \"w_wad.h\"\n\n#include \"deh_main.h\"\n#include \"deh_misc.h\"\n#include \"doomdef.h\"\n#include \"doomkeys.h\"\n\n#include \"g_game.h\"\n\n#include \"st_stuff.h\"\n#include \"st_lib.h\"\n#include \"r_local.h\"\n\n#include \"p_local.h\"\n#include \"p_inter.h\"\n\n#include \"am_map.h\"\n#include \"m_cheat.h\"\n\n#include \"s_sound.h\"\n\n// Needs access to LFB.\n#include \"v_video.h\"\n\n// State.\n#include \"doomstat.h\"\n\n// Data.\n#include \"dstrings.h\"\n#include \"sounds.h\"\n\n//\n// STATUS BAR DATA\n//\n\n\n// Palette indices.\n// For damage/bonus red-/gold-shifts\n#define STARTREDPALS\t\t1\n#define STARTBONUSPALS\t\t9\n#define NUMREDPALS\t\t\t8\n#define NUMBONUSPALS\t\t4\n// Radiation suit, green shift.\n#define RADIATIONPAL\t\t13\n\n// N/256*100% probability\n//  that the normal face state will change\n#define ST_FACEPROBABILITY\t\t96\n\n// For Responder\n#define ST_TOGGLECHAT\t\tKEY_ENTER\n\n// Location of status bar\n#define ST_X\t\t\t\t0\n#define ST_X2\t\t\t\t104\n\n#define ST_FX  \t\t\t143\n#define ST_FY  \t\t\t169\n\n// Should be set to patch width\n//  for tall numbers later on\n#define ST_TALLNUMWIDTH\t\t(tallnum[0]->width)\n\n// Number of status faces.\n#define ST_NUMPAINFACES\t\t5\n#define ST_NUMSTRAIGHTFACES\t3\n#define ST_NUMTURNFACES\t\t2\n#define ST_NUMSPECIALFACES\t\t3\n\n#define ST_FACESTRIDE \\\n          (ST_NUMSTRAIGHTFACES+ST_NUMTURNFACES+ST_NUMSPECIALFACES)\n\n#define ST_NUMEXTRAFACES\t\t2\n\n#define ST_NUMFACES \\\n          (ST_FACESTRIDE*ST_NUMPAINFACES+ST_NUMEXTRAFACES)\n\n#define ST_TURNOFFSET\t\t(ST_NUMSTRAIGHTFACES)\n#define ST_OUCHOFFSET\t\t(ST_TURNOFFSET + ST_NUMTURNFACES)\n#define ST_EVILGRINOFFSET\t\t(ST_OUCHOFFSET + 1)\n#define ST_RAMPAGEOFFSET\t\t(ST_EVILGRINOFFSET + 1)\n#define ST_GODFACE\t\t\t(ST_NUMPAINFACES*ST_FACESTRIDE)\n#define ST_DEADFACE\t\t\t(ST_GODFACE+1)\n\n#define ST_FACESX\t\t\t143\n#define ST_FACESY\t\t\t168\n\n#define ST_EVILGRINCOUNT\t\t(2*TICRATE)\n#define ST_STRAIGHTFACECOUNT\t(TICRATE/2)\n#define ST_TURNCOUNT\t\t(1*TICRATE)\n#define ST_OUCHCOUNT\t\t(1*TICRATE)\n#define ST_RAMPAGEDELAY\t\t(2*TICRATE)\n\n#define ST_MUCHPAIN\t\t\t20\n\n\n// Location and size of statistics,\n//  justified according to widget type.\n// Problem is, within which space? STbar? Screen?\n// Note: this could be read in by a lump.\n//       Problem is, is the stuff rendered\n//       into a buffer,\n//       or into the frame buffer?\n\n// AMMO number pos.\n#define ST_AMMOWIDTH\t\t3\t\n#define ST_AMMOX\t\t\t44\n#define ST_AMMOY\t\t\t171\n\n// HEALTH number pos.\n#define ST_HEALTHWIDTH\t\t3\t\n#define ST_HEALTHX\t\t\t90\n#define ST_HEALTHY\t\t\t171\n\n// Weapon pos.\n#define ST_ARMSX\t\t\t111\n#define ST_ARMSY\t\t\t172\n#define ST_ARMSBGX\t\t\t104\n#define ST_ARMSBGY\t\t\t168\n#define ST_ARMSXSPACE\t\t12\n#define ST_ARMSYSPACE\t\t10\n\n// Frags pos.\n#define ST_FRAGSX\t\t\t138\n#define ST_FRAGSY\t\t\t171\t\n#define ST_FRAGSWIDTH\t\t2\n\n// ARMOR number pos.\n#define ST_ARMORWIDTH\t\t3\n#define ST_ARMORX\t\t\t221\n#define ST_ARMORY\t\t\t171\n\n// Key icon positions.\n#define ST_KEY0WIDTH\t\t8\n#define ST_KEY0HEIGHT\t\t5\n#define ST_KEY0X\t\t\t239\n#define ST_KEY0Y\t\t\t171\n#define ST_KEY1WIDTH\t\tST_KEY0WIDTH\n#define ST_KEY1X\t\t\t239\n#define ST_KEY1Y\t\t\t181\n#define ST_KEY2WIDTH\t\tST_KEY0WIDTH\n#define ST_KEY2X\t\t\t239\n#define ST_KEY2Y\t\t\t191\n\n// Ammunition counter.\n#define ST_AMMO0WIDTH\t\t3\n#define ST_AMMO0HEIGHT\t\t6\n#define ST_AMMO0X\t\t\t288\n#define ST_AMMO0Y\t\t\t173\n#define ST_AMMO1WIDTH\t\tST_AMMO0WIDTH\n#define ST_AMMO1X\t\t\t288\n#define ST_AMMO1Y\t\t\t179\n#define ST_AMMO2WIDTH\t\tST_AMMO0WIDTH\n#define ST_AMMO2X\t\t\t288\n#define ST_AMMO2Y\t\t\t191\n#define ST_AMMO3WIDTH\t\tST_AMMO0WIDTH\n#define ST_AMMO3X\t\t\t288\n#define ST_AMMO3Y\t\t\t185\n\n// Indicate maximum ammunition.\n// Only needed because backpack exists.\n#define ST_MAXAMMO0WIDTH\t\t3\n#define ST_MAXAMMO0HEIGHT\t\t5\n#define ST_MAXAMMO0X\t\t314\n#define ST_MAXAMMO0Y\t\t173\n#define ST_MAXAMMO1WIDTH\t\tST_MAXAMMO0WIDTH\n#define ST_MAXAMMO1X\t\t314\n#define ST_MAXAMMO1Y\t\t179\n#define ST_MAXAMMO2WIDTH\t\tST_MAXAMMO0WIDTH\n#define ST_MAXAMMO2X\t\t314\n#define ST_MAXAMMO2Y\t\t191\n#define ST_MAXAMMO3WIDTH\t\tST_MAXAMMO0WIDTH\n#define ST_MAXAMMO3X\t\t314\n#define ST_MAXAMMO3Y\t\t185\n\n// pistol\n#define ST_WEAPON0X\t\t\t110 \n#define ST_WEAPON0Y\t\t\t172\n\n// shotgun\n#define ST_WEAPON1X\t\t\t122 \n#define ST_WEAPON1Y\t\t\t172\n\n// chain gun\n#define ST_WEAPON2X\t\t\t134 \n#define ST_WEAPON2Y\t\t\t172\n\n// missile launcher\n#define ST_WEAPON3X\t\t\t110 \n#define ST_WEAPON3Y\t\t\t181\n\n// plasma gun\n#define ST_WEAPON4X\t\t\t122 \n#define ST_WEAPON4Y\t\t\t181\n\n // bfg\n#define ST_WEAPON5X\t\t\t134\n#define ST_WEAPON5Y\t\t\t181\n\n// WPNS title\n#define ST_WPNSX\t\t\t109 \n#define ST_WPNSY\t\t\t191\n\n // DETH title\n#define ST_DETHX\t\t\t109\n#define ST_DETHY\t\t\t191\n\n//Incoming messages window location\n//UNUSED\n// #define ST_MSGTEXTX\t   (viewwindowx)\n// #define ST_MSGTEXTY\t   (viewwindowy+viewheight-18)\n#define ST_MSGTEXTX\t\t\t0\n#define ST_MSGTEXTY\t\t\t0\n// Dimensions given in characters.\n#define ST_MSGWIDTH\t\t\t52\n// Or shall I say, in lines?\n#define ST_MSGHEIGHT\t\t1\n\n#define ST_OUTTEXTX\t\t\t0\n#define ST_OUTTEXTY\t\t\t6\n\n// Width, in characters again.\n#define ST_OUTWIDTH\t\t\t52 \n // Height, in lines. \n#define ST_OUTHEIGHT\t\t1\n\n#define ST_MAPTITLEX \\\n    (SCREENWIDTH - ST_MAPWIDTH * ST_CHATFONTWIDTH)\n\n#define ST_MAPTITLEY\t\t0\n#define ST_MAPHEIGHT\t\t1\n\n// graphics are drawn to a backing screen and blitted to the real screen\nbyte                   *st_backing_screen;\n\t    \n// main player in game\nstatic player_t*\tplyr; \n\n// ST_Start() has just been called\nstatic boolean\t\tst_firsttime;\n\n// lump number for PLAYPAL\nstatic int\t\tlu_palette;\n\n// used for timing\nstatic unsigned int\tst_clock;\n\n// used for making messages go away\nstatic int\t\tst_msgcounter=0;\n\n// used when in chat \nstatic st_chatstateenum_t\tst_chatstate;\n\n// whether in automap or first-person\nstatic st_stateenum_t\tst_gamestate;\n\n// whether left-side main status bar is active\nstatic boolean\t\tst_statusbaron;\n\n// whether status bar chat is active\nstatic boolean\t\tst_chat;\n\n// value of st_chat before message popped up\nstatic boolean\t\tst_oldchat;\n\n// whether chat window has the cursor on\nstatic boolean\t\tst_cursoron;\n\n// !deathmatch\nstatic boolean\t\tst_notdeathmatch; \n\n// !deathmatch && st_statusbaron\nstatic boolean\t\tst_armson;\n\n// !deathmatch\nstatic boolean\t\tst_fragson; \n\n// main bar left\nstatic patch_t*\t\tsbar;\n\n// 0-9, tall numbers\nstatic patch_t*\t\ttallnum[10];\n\n// tall % sign\nstatic patch_t*\t\ttallpercent;\n\n// 0-9, short, yellow (,different!) numbers\nstatic patch_t*\t\tshortnum[10];\n\n// 3 key-cards, 3 skulls\nstatic patch_t*\t\tkeys[NUMCARDS]; \n\n// face status patches\nstatic patch_t*\t\tfaces[ST_NUMFACES];\n\n// face background\nstatic patch_t*\t\tfaceback;\n\n // main bar right\nstatic patch_t*\t\tarmsbg;\n\n// weapon ownership patches\nstatic patch_t*\t\tarms[6][2]; \n\n// ready-weapon widget\nstatic st_number_t\tw_ready;\n\n // in deathmatch only, summary of frags stats\nstatic st_number_t\tw_frags;\n\n// health widget\nstatic st_percent_t\tw_health;\n\n// arms background\nstatic st_binicon_t\tw_armsbg; \n\n\n// weapon ownership widgets\nstatic st_multicon_t\tw_arms[6];\n\n// face status widget\nstatic st_multicon_t\tw_faces; \n\n// keycard widgets\nstatic st_multicon_t\tw_keyboxes[3];\n\n// armor widget\nstatic st_percent_t\tw_armor;\n\n// ammo widgets\nstatic st_number_t\tw_ammo[4];\n\n// max ammo widgets\nstatic st_number_t\tw_maxammo[4]; \n\n\n\n // number of frags so far in deathmatch\nstatic int\tst_fragscount;\n\n// used to use appopriately pained face\nstatic int\tst_oldhealth = -1;\n\n// used for evil grin\nstatic boolean\toldweaponsowned[NUMWEAPONS]; \n\n // count until face changes\nstatic int\tst_facecount = 0;\n\n// current face index, used by w_faces\nstatic int\tst_faceindex = 0;\n\n// holds key-type for each key box on bar\nstatic int\tkeyboxes[3]; \n\n// a random number per tick\nstatic int\tst_randomnumber;  \n\ncheatseq_t cheat_mus = CHEAT(\"idmus\", 2);\ncheatseq_t cheat_god = CHEAT(\"iddqd\", 0);\ncheatseq_t cheat_ammo = CHEAT(\"idkfa\", 0);\ncheatseq_t cheat_ammonokey = CHEAT(\"idfa\", 0);\ncheatseq_t cheat_noclip = CHEAT(\"idspispopd\", 0);\ncheatseq_t cheat_commercial_noclip = CHEAT(\"idclip\", 0);\n\ncheatseq_t\tcheat_powerup[7] =\n{\n    CHEAT(\"idbeholdv\", 0),\n    CHEAT(\"idbeholds\", 0),\n    CHEAT(\"idbeholdi\", 0),\n    CHEAT(\"idbeholdr\", 0),\n    CHEAT(\"idbeholda\", 0),\n    CHEAT(\"idbeholdl\", 0),\n    CHEAT(\"idbehold\", 0),\n};\n\ncheatseq_t cheat_choppers = CHEAT(\"idchoppers\", 0);\ncheatseq_t cheat_clev = CHEAT(\"idclev\", 2);\ncheatseq_t cheat_mypos = CHEAT(\"idmypos\", 0);\n\n\n//\n// STATUS BAR CODE\n//\nvoid ST_Stop(void);\n\nvoid ST_refreshBackground(void)\n{\n\n    if (st_statusbaron)\n    {\n        V_UseBuffer(st_backing_screen);\n\n\tV_DrawPatch(ST_X, 0, sbar);\n\n\tif (netgame)\n\t    V_DrawPatch(ST_FX, 0, faceback);\n\n        V_RestoreBuffer();\n\n\tV_CopyRect(ST_X, 0, st_backing_screen, ST_WIDTH, ST_HEIGHT, ST_X, ST_Y);\n    }\n\n}\n\n\n// Respond to keyboard input events,\n//  intercept cheats.\nboolean\nST_Responder (event_t* ev)\n{\n  int\t\ti;\n    \n  // Filter automap on/off.\n  if (ev->type == ev_keyup\n      && ((ev->data1 & 0xffff0000) == AM_MSGHEADER))\n  {\n    switch(ev->data1)\n    {\n      case AM_MSGENTERED:\n\tst_gamestate = AutomapState;\n\tst_firsttime = true;\n\tbreak;\n\t\n      case AM_MSGEXITED:\n\t//\tfprintf(stderr, \"AM exited\\n\");\n\tst_gamestate = FirstPersonState;\n\tbreak;\n    }\n  }\n\n  // if a user keypress...\n  else if (ev->type == ev_keydown)\n  {\n    if (!netgame && gameskill != sk_nightmare)\n    {\n      // 'dqd' cheat for toggleable god mode\n      if (cht_CheckCheat(&cheat_god, ev->data2))\n      {\n\tplyr->cheats ^= CF_GODMODE;\n\tif (plyr->cheats & CF_GODMODE)\n\t{\n\t  if (plyr->mo)\n\t    plyr->mo->health = 100;\n\t  \n\t  plyr->health = deh_god_mode_health;\n\t  plyr->message = DEH_String(STSTR_DQDON);\n\t}\n\telse \n\t  plyr->message = DEH_String(STSTR_DQDOFF);\n      }\n      // 'fa' cheat for killer fucking arsenal\n      else if (cht_CheckCheat(&cheat_ammonokey, ev->data2))\n      {\n\tplyr->armorpoints = deh_idfa_armor;\n\tplyr->armortype = deh_idfa_armor_class;\n\t\n\tfor (i=0;i<NUMWEAPONS;i++)\n\t  plyr->weaponowned[i] = true;\n\t\n\tfor (i=0;i<NUMAMMO;i++)\n\t  plyr->ammo[i] = plyr->maxammo[i];\n\t\n\tplyr->message = DEH_String(STSTR_FAADDED);\n      }\n      // 'kfa' cheat for key full ammo\n      else if (cht_CheckCheat(&cheat_ammo, ev->data2))\n      {\n\tplyr->armorpoints = deh_idkfa_armor;\n\tplyr->armortype = deh_idkfa_armor_class;\n\t\n\tfor (i=0;i<NUMWEAPONS;i++)\n\t  plyr->weaponowned[i] = true;\n\t\n\tfor (i=0;i<NUMAMMO;i++)\n\t  plyr->ammo[i] = plyr->maxammo[i];\n\t\n\tfor (i=0;i<NUMCARDS;i++)\n\t  plyr->cards[i] = true;\n\t\n\tplyr->message = DEH_String(STSTR_KFAADDED);\n      }\n      // 'mus' cheat for changing music\n      else if (cht_CheckCheat(&cheat_mus, ev->data2))\n      {\n\t\n\tchar\tbuf[3];\n\tint\t\tmusnum;\n\t\n\tplyr->message = DEH_String(STSTR_MUS);\n\tcht_GetParam(&cheat_mus, buf);\n\n        // Note: The original v1.9 had a bug that tried to play back\n        // the Doom II music regardless of gamemode.  This was fixed\n        // in the Ultimate Doom executable so that it would work for\n        // the Doom 1 music as well.\n\n\tif (gamemode == commercial || gameversion < exe_ultimate)\n\t{\n\t  musnum = mus_runnin + (buf[0]-'0')*10 + buf[1]-'0' - 1;\n\t  \n\t  if (((buf[0]-'0')*10 + buf[1]-'0') > 35)\n\t    plyr->message = DEH_String(STSTR_NOMUS);\n\t  else\n\t    S_ChangeMusic(musnum, 1);\n\t}\n\telse\n\t{\n\t  musnum = mus_e1m1 + (buf[0]-'1')*9 + (buf[1]-'1');\n\t  \n\t  if (((buf[0]-'1')*9 + buf[1]-'1') > 31)\n\t    plyr->message = DEH_String(STSTR_NOMUS);\n\t  else\n\t    S_ChangeMusic(musnum, 1);\n\t}\n      }\n      else if ( (logical_gamemission == doom \n                 && cht_CheckCheat(&cheat_noclip, ev->data2))\n             || (logical_gamemission != doom \n                 && cht_CheckCheat(&cheat_commercial_noclip,ev->data2)))\n      {\t\n        // Noclip cheat.\n        // For Doom 1, use the idspipsopd cheat; for all others, use\n        // idclip\n\n\tplyr->cheats ^= CF_NOCLIP;\n\t\n\tif (plyr->cheats & CF_NOCLIP)\n\t  plyr->message = DEH_String(STSTR_NCON);\n\telse\n\t  plyr->message = DEH_String(STSTR_NCOFF);\n      }\n      // 'behold?' power-up cheats\n      for (i=0;i<6;i++)\n      {\n\tif (cht_CheckCheat(&cheat_powerup[i], ev->data2))\n\t{\n\t  if (!plyr->powers[i])\n\t    P_GivePower( plyr, i);\n\t  else if (i!=pw_strength)\n\t    plyr->powers[i] = 1;\n\t  else\n\t    plyr->powers[i] = 0;\n\t  \n\t  plyr->message = DEH_String(STSTR_BEHOLDX);\n\t}\n      }\n      \n      // 'behold' power-up menu\n      if (cht_CheckCheat(&cheat_powerup[6], ev->data2))\n      {\n\tplyr->message = DEH_String(STSTR_BEHOLD);\n      }\n      // 'choppers' invulnerability & chainsaw\n      else if (cht_CheckCheat(&cheat_choppers, ev->data2))\n      {\n\tplyr->weaponowned[wp_chainsaw] = true;\n\tplyr->powers[pw_invulnerability] = true;\n\tplyr->message = DEH_String(STSTR_CHOPPERS);\n      }\n      // 'mypos' for player position\n      else if (cht_CheckCheat(&cheat_mypos, ev->data2))\n      {\n        static char buf[ST_MSGWIDTH];\n        M_snprintf(buf, sizeof(buf), \"ang=0x%x;x,y=(0x%x,0x%x)\",\n                   players[consoleplayer].mo->angle,\n                   players[consoleplayer].mo->x,\n                   players[consoleplayer].mo->y);\n        plyr->message = buf;\n      }\n    }\n    \n    // 'clev' change-level cheat\n    if (!netgame && cht_CheckCheat(&cheat_clev, ev->data2))\n    {\n      char\t\tbuf[3];\n      int\t\tepsd;\n      int\t\tmap;\n      \n      cht_GetParam(&cheat_clev, buf);\n      \n      if (gamemode == commercial)\n      {\n\tepsd = 1;\n\tmap = (buf[0] - '0')*10 + buf[1] - '0';\n      }\n      else\n      {\n\tepsd = buf[0] - '0';\n\tmap = buf[1] - '0';\n      }\n\n      // Chex.exe always warps to episode 1.\n\n      if (gameversion == exe_chex)\n      {\n        epsd = 1;\n      }\n\n      // Catch invalid maps.\n      if (epsd < 1)\n\treturn false;\n\n      if (map < 1)\n\treturn false;\n\n      // Ohmygod - this is not going to work.\n      if ((gamemode == retail)\n\t  && ((epsd > 4) || (map > 9)))\n\treturn false;\n\n      if ((gamemode == registered)\n\t  && ((epsd > 3) || (map > 9)))\n\treturn false;\n\n      if ((gamemode == shareware)\n\t  && ((epsd > 1) || (map > 9)))\n\treturn false;\n\n      // The source release has this check as map > 34. However, Vanilla\n      // Doom allows IDCLEV up to MAP40 even though it normally crashes.\n      if ((gamemode == commercial)\n\t&& (( epsd > 1) || (map > 40)))\n\treturn false;\n\n      // So be it.\n      plyr->message = DEH_String(STSTR_CLEV);\n      G_DeferedInitNew(gameskill, epsd, map);\n    }\n  }\n  return false;\n}\n\n\n\nint ST_calcPainOffset(void)\n{\n    int\t\thealth;\n    static int\tlastcalc;\n    static int\toldhealth = -1;\n    \n    health = plyr->health > 100 ? 100 : plyr->health;\n\n    if (health != oldhealth)\n    {\n\tlastcalc = ST_FACESTRIDE * (((100 - health) * ST_NUMPAINFACES) / 101);\n\toldhealth = health;\n    }\n    return lastcalc;\n}\n\n\n//\n// This is a not-very-pretty routine which handles\n//  the face states and their timing.\n// the precedence of expressions is:\n//  dead > evil grin > turned head > straight ahead\n//\nvoid ST_updateFaceWidget(void)\n{\n    int\t\ti;\n    angle_t\tbadguyangle;\n    angle_t\tdiffang;\n    static int\tlastattackdown = -1;\n    static int\tpriority = 0;\n    boolean\tdoevilgrin;\n\n    if (priority < 10)\n    {\n\t// dead\n\tif (!plyr->health)\n\t{\n\t    priority = 9;\n\t    st_faceindex = ST_DEADFACE;\n\t    st_facecount = 1;\n\t}\n    }\n\n    if (priority < 9)\n    {\n\tif (plyr->bonuscount)\n\t{\n\t    // picking up bonus\n\t    doevilgrin = false;\n\n\t    for (i=0;i<NUMWEAPONS;i++)\n\t    {\n\t\tif (oldweaponsowned[i] != plyr->weaponowned[i])\n\t\t{\n\t\t    doevilgrin = true;\n\t\t    oldweaponsowned[i] = plyr->weaponowned[i];\n\t\t}\n\t    }\n\t    if (doevilgrin) \n\t    {\n\t\t// evil grin if just picked up weapon\n\t\tpriority = 8;\n\t\tst_facecount = ST_EVILGRINCOUNT;\n\t\tst_faceindex = ST_calcPainOffset() + ST_EVILGRINOFFSET;\n\t    }\n\t}\n\n    }\n  \n    if (priority < 8)\n    {\n\tif (plyr->damagecount\n\t    && plyr->attacker\n\t    && plyr->attacker != plyr->mo)\n\t{\n\t    // being attacked\n\t    priority = 7;\n\t    \n\t    if (plyr->health - st_oldhealth > ST_MUCHPAIN)\n\t    {\n\t\tst_facecount = ST_TURNCOUNT;\n\t\tst_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET;\n\t    }\n\t    else\n\t    {\n\t\tbadguyangle = R_PointToAngle2(plyr->mo->x,\n\t\t\t\t\t      plyr->mo->y,\n\t\t\t\t\t      plyr->attacker->x,\n\t\t\t\t\t      plyr->attacker->y);\n\t\t\n\t\tif (badguyangle > plyr->mo->angle)\n\t\t{\n\t\t    // whether right or left\n\t\t    diffang = badguyangle - plyr->mo->angle;\n\t\t    i = diffang > ANG180; \n\t\t}\n\t\telse\n\t\t{\n\t\t    // whether left or right\n\t\t    diffang = plyr->mo->angle - badguyangle;\n\t\t    i = diffang <= ANG180; \n\t\t} // confusing, aint it?\n\n\t\t\n\t\tst_facecount = ST_TURNCOUNT;\n\t\tst_faceindex = ST_calcPainOffset();\n\t\t\n\t\tif (diffang < ANG45)\n\t\t{\n\t\t    // head-on    \n\t\t    st_faceindex += ST_RAMPAGEOFFSET;\n\t\t}\n\t\telse if (i)\n\t\t{\n\t\t    // turn face right\n\t\t    st_faceindex += ST_TURNOFFSET;\n\t\t}\n\t\telse\n\t\t{\n\t\t    // turn face left\n\t\t    st_faceindex += ST_TURNOFFSET+1;\n\t\t}\n\t    }\n\t}\n    }\n  \n    if (priority < 7)\n    {\n\t// getting hurt because of your own damn stupidity\n\tif (plyr->damagecount)\n\t{\n\t    if (plyr->health - st_oldhealth > ST_MUCHPAIN)\n\t    {\n\t\tpriority = 7;\n\t\tst_facecount = ST_TURNCOUNT;\n\t\tst_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET;\n\t    }\n\t    else\n\t    {\n\t\tpriority = 6;\n\t\tst_facecount = ST_TURNCOUNT;\n\t\tst_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET;\n\t    }\n\n\t}\n\n    }\n  \n    if (priority < 6)\n    {\n\t// rapid firing\n\tif (plyr->attackdown)\n\t{\n\t    if (lastattackdown==-1)\n\t\tlastattackdown = ST_RAMPAGEDELAY;\n\t    else if (!--lastattackdown)\n\t    {\n\t\tpriority = 5;\n\t\tst_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET;\n\t\tst_facecount = 1;\n\t\tlastattackdown = 1;\n\t    }\n\t}\n\telse\n\t    lastattackdown = -1;\n\n    }\n  \n    if (priority < 5)\n    {\n\t// invulnerability\n\tif ((plyr->cheats & CF_GODMODE)\n\t    || plyr->powers[pw_invulnerability])\n\t{\n\t    priority = 4;\n\n\t    st_faceindex = ST_GODFACE;\n\t    st_facecount = 1;\n\n\t}\n\n    }\n\n    // look left or look right if the facecount has timed out\n    if (!st_facecount)\n    {\n\tst_faceindex = ST_calcPainOffset() + (st_randomnumber % 3);\n\tst_facecount = ST_STRAIGHTFACECOUNT;\n\tpriority = 0;\n    }\n\n    st_facecount--;\n\n}\n\nvoid ST_updateWidgets(void)\n{\n    static int\tlargeammo = 1994; // means \"n/a\"\n    int\t\ti;\n\n    // must redirect the pointer if the ready weapon has changed.\n    //  if (w_ready.data != plyr->readyweapon)\n    //  {\n    if (weaponinfo[plyr->readyweapon].ammo == am_noammo)\n\tw_ready.num = &largeammo;\n    else\n\tw_ready.num = &plyr->ammo[weaponinfo[plyr->readyweapon].ammo];\n    //{\n    // static int tic=0;\n    // static int dir=-1;\n    // if (!(tic&15))\n    //   plyr->ammo[weaponinfo[plyr->readyweapon].ammo]+=dir;\n    // if (plyr->ammo[weaponinfo[plyr->readyweapon].ammo] == -100)\n    //   dir = 1;\n    // tic++;\n    // }\n    w_ready.data = plyr->readyweapon;\n\n    // if (*w_ready.on)\n    //  STlib_updateNum(&w_ready, true);\n    // refresh weapon change\n    //  }\n\n    // update keycard multiple widgets\n    for (i=0;i<3;i++)\n    {\n\tkeyboxes[i] = plyr->cards[i] ? i : -1;\n\n\tif (plyr->cards[i+3])\n\t    keyboxes[i] = i+3;\n    }\n\n    // refresh everything if this is him coming back to life\n    ST_updateFaceWidget();\n\n    // used by the w_armsbg widget\n    st_notdeathmatch = !deathmatch;\n    \n    // used by w_arms[] widgets\n    st_armson = st_statusbaron && !deathmatch; \n\n    // used by w_frags widget\n    st_fragson = deathmatch && st_statusbaron; \n    st_fragscount = 0;\n\n    for (i=0 ; i<MAXPLAYERS ; i++)\n    {\n\tif (i != consoleplayer)\n\t    st_fragscount += plyr->frags[i];\n\telse\n\t    st_fragscount -= plyr->frags[i];\n    }\n\n    // get rid of chat window if up because of message\n    if (!--st_msgcounter)\n\tst_chat = st_oldchat;\n\n}\n\nvoid ST_Ticker (void)\n{\n\n    st_clock++;\n    st_randomnumber = M_Random();\n    ST_updateWidgets();\n    st_oldhealth = plyr->health;\n\n}\n\nstatic int st_palette = 0;\n\nvoid ST_doPaletteStuff(void)\n{\n\n    int\t\tpalette;\n    byte*\tpal;\n    int\t\tcnt;\n    int\t\tbzc;\n\n    cnt = plyr->damagecount;\n\n    if (plyr->powers[pw_strength])\n    {\n\t// slowly fade the berzerk out\n  \tbzc = 12 - (plyr->powers[pw_strength]>>6);\n\n\tif (bzc > cnt)\n\t    cnt = bzc;\n    }\n\t\n    if (cnt)\n    {\n\tpalette = (cnt+7)>>3;\n\t\n\tif (palette >= NUMREDPALS)\n\t    palette = NUMREDPALS-1;\n\n\tpalette += STARTREDPALS;\n    }\n\n    else if (plyr->bonuscount)\n    {\n\tpalette = (plyr->bonuscount+7)>>3;\n\n\tif (palette >= NUMBONUSPALS)\n\t    palette = NUMBONUSPALS-1;\n\n\tpalette += STARTBONUSPALS;\n    }\n\n    else if ( plyr->powers[pw_ironfeet] > 4*32\n\t      || plyr->powers[pw_ironfeet]&8)\n\tpalette = RADIATIONPAL;\n    else\n\tpalette = 0;\n\n    // In Chex Quest, the player never sees red.  Instead, the\n    // radiation suit palette is used to tint the screen green,\n    // as though the player is being covered in goo by an\n    // attacking flemoid.\n\n    if (gameversion == exe_chex\n     && palette >= STARTREDPALS && palette < STARTREDPALS + NUMREDPALS)\n    {\n        palette = RADIATIONPAL;\n    }\n\n    if (palette != st_palette)\n    {\n\tst_palette = palette;\n\tpal = (byte *) W_CacheLumpNum (lu_palette, PU_CACHE)+palette*768;\n\tI_SetPalette (pal);\n    }\n\n}\n\nvoid ST_drawWidgets(boolean refresh)\n{\n    int\t\ti;\n\n    // used by w_arms[] widgets\n    st_armson = st_statusbaron && !deathmatch;\n\n    // used by w_frags widget\n    st_fragson = deathmatch && st_statusbaron; \n\n    STlib_updateNum(&w_ready, refresh);\n\n    for (i=0;i<4;i++)\n    {\n\tSTlib_updateNum(&w_ammo[i], refresh);\n\tSTlib_updateNum(&w_maxammo[i], refresh);\n    }\n\n    STlib_updatePercent(&w_health, refresh);\n    STlib_updatePercent(&w_armor, refresh);\n\n    STlib_updateBinIcon(&w_armsbg, refresh);\n\n    for (i=0;i<6;i++)\n\tSTlib_updateMultIcon(&w_arms[i], refresh);\n\n    STlib_updateMultIcon(&w_faces, refresh);\n\n    for (i=0;i<3;i++)\n\tSTlib_updateMultIcon(&w_keyboxes[i], refresh);\n\n    STlib_updateNum(&w_frags, refresh);\n\n}\n\nvoid ST_doRefresh(void)\n{\n\n    st_firsttime = false;\n\n    // draw status bar background to off-screen buff\n    ST_refreshBackground();\n\n    // and refresh all widgets\n    ST_drawWidgets(true);\n\n}\n\nvoid ST_diffDraw(void)\n{\n    // update all widgets\n    ST_drawWidgets(false);\n}\n\nvoid ST_Drawer (boolean fullscreen, boolean refresh)\n{\n  \n    st_statusbaron = (!fullscreen) || automapactive;\n    st_firsttime = st_firsttime || refresh;\n\n    // Do red-/gold-shifts from damage/items\n    ST_doPaletteStuff();\n\n    // If just after ST_Start(), refresh all\n    if (st_firsttime) ST_doRefresh();\n    // Otherwise, update as little as possible\n    else ST_diffDraw();\n\n}\n\ntypedef void (*load_callback_t)(char *lumpname, patch_t **variable); \n\n// Iterates through all graphics to be loaded or unloaded, along with\n// the variable they use, invoking the specified callback function.\n\nstatic void ST_loadUnloadGraphics(load_callback_t callback)\n{\n\n    int\t\ti;\n    int\t\tj;\n    int\t\tfacenum;\n    \n    char\tnamebuf[9];\n\n    // Load the numbers, tall and short\n    for (i=0;i<10;i++)\n    {\n\tDEH_snprintf(namebuf, 9, \"STTNUM%d\", i);\n        callback(namebuf, &tallnum[i]);\n\n\tDEH_snprintf(namebuf, 9, \"STYSNUM%d\", i);\n        callback(namebuf, &shortnum[i]);\n    }\n\n    // Load percent key.\n    //Note: why not load STMINUS here, too?\n\n    callback(DEH_String(\"STTPRCNT\"), &tallpercent);\n\n    // key cards\n    for (i=0;i<NUMCARDS;i++)\n    {\n    \tDEH_snprintf(namebuf, 9, \"STKEYS%d\", i);\n        callback(namebuf, &keys[i]);\n    }\n\n    // arms background\n    callback(DEH_String(\"STARMS\"), &armsbg);\n\n    // arms ownership widgets\n    for (i=0; i<6; i++)\n    {\n    \tDEH_snprintf(namebuf, 9, \"STGNUM%d\", i+2);\n\n    \t// gray #\n        callback(namebuf, &arms[i][0]);\n\n        // yellow #\n        arms[i][1] = shortnum[i+2];\n    }\n\n    // face backgrounds for different color players\n    DEH_snprintf(namebuf, 9, \"STFB%d\", consoleplayer);\n    callback(namebuf, &faceback);\n\n    // status bar background bits\n    callback(DEH_String(\"STBAR\"), &sbar);\n\n    // face states\n    facenum = 0;\n    for (i=0; i<ST_NUMPAINFACES; i++)\n    {\n\tfor (j=0; j<ST_NUMSTRAIGHTFACES; j++)\n\t{\n\t    DEH_snprintf(namebuf, 9, \"STFST%d%d\", i, j);\n            callback(namebuf, &faces[facenum]);\n            ++facenum;\n\t}\n\tDEH_snprintf(namebuf, 9, \"STFTR%d0\", i);\t// turn right\n        callback(namebuf, &faces[facenum]);\n        ++facenum;\n\tDEH_snprintf(namebuf, 9, \"STFTL%d0\", i);\t// turn left\n        callback(namebuf, &faces[facenum]);\n        ++facenum;\n\tDEH_snprintf(namebuf, 9, \"STFOUCH%d\", i);\t// ouch!\n        callback(namebuf, &faces[facenum]);\n        ++facenum;\n\tDEH_snprintf(namebuf, 9, \"STFEVL%d\", i);\t// evil grin ;)\n        callback(namebuf, &faces[facenum]);\n        ++facenum;\n\tDEH_snprintf(namebuf, 9, \"STFKILL%d\", i);\t// pissed off\n        callback(namebuf, &faces[facenum]);\n        ++facenum;\n    }\n\n    callback(DEH_String(\"STFGOD0\"), &faces[facenum]);\n    ++facenum;\n    callback(DEH_String(\"STFDEAD0\"), &faces[facenum]);\n    ++facenum;\n}\n\nstatic void ST_loadCallback(char *lumpname, patch_t **variable)\n{\n    *variable = W_CacheLumpName(lumpname, PU_STATIC);\n}\n\nvoid ST_loadGraphics(void)\n{\n    ST_loadUnloadGraphics(ST_loadCallback);\n}\n\nvoid ST_loadData(void)\n{\n    lu_palette = W_GetNumForName (DEH_String(\"PLAYPAL\"));\n    ST_loadGraphics();\n}\n\nstatic void ST_unloadCallback(char *lumpname, patch_t **variable)\n{\n    W_ReleaseLumpName(lumpname);\n    *variable = NULL;\n}\n\nvoid ST_unloadGraphics(void)\n{\n    ST_loadUnloadGraphics(ST_unloadCallback);\n}\n\nvoid ST_unloadData(void)\n{\n    ST_unloadGraphics();\n}\n\nvoid ST_initData(void)\n{\n\n    int\t\ti;\n\n    st_firsttime = true;\n    plyr = &players[consoleplayer];\n\n    st_clock = 0;\n    st_chatstate = StartChatState;\n    st_gamestate = FirstPersonState;\n\n    st_statusbaron = true;\n    st_oldchat = st_chat = false;\n    st_cursoron = false;\n\n    st_faceindex = 0;\n    st_palette = -1;\n\n    st_oldhealth = -1;\n\n    for (i=0;i<NUMWEAPONS;i++)\n\toldweaponsowned[i] = plyr->weaponowned[i];\n\n    for (i=0;i<3;i++)\n\tkeyboxes[i] = -1;\n\n    STlib_init();\n\n}\n\n\n\nvoid ST_createWidgets(void)\n{\n\n    int i;\n\n    // ready weapon ammo\n    STlib_initNum(&w_ready,\n\t\t  ST_AMMOX,\n\t\t  ST_AMMOY,\n\t\t  tallnum,\n\t\t  &plyr->ammo[weaponinfo[plyr->readyweapon].ammo],\n\t\t  &st_statusbaron,\n\t\t  ST_AMMOWIDTH );\n\n    // the last weapon type\n    w_ready.data = plyr->readyweapon; \n\n    // health percentage\n    STlib_initPercent(&w_health,\n\t\t      ST_HEALTHX,\n\t\t      ST_HEALTHY,\n\t\t      tallnum,\n\t\t      &plyr->health,\n\t\t      &st_statusbaron,\n\t\t      tallpercent);\n\n    // arms background\n    STlib_initBinIcon(&w_armsbg,\n\t\t      ST_ARMSBGX,\n\t\t      ST_ARMSBGY,\n\t\t      armsbg,\n\t\t      &st_notdeathmatch,\n\t\t      &st_statusbaron);\n\n    // weapons owned\n    for(i=0;i<6;i++)\n    {\n\tSTlib_initMultIcon(&w_arms[i],\n\t\t\t   ST_ARMSX+(i%3)*ST_ARMSXSPACE,\n\t\t\t   ST_ARMSY+(i/3)*ST_ARMSYSPACE,\n\t\t\t   arms[i], (int *) &plyr->weaponowned[i+1],\n\t\t\t   &st_armson);\n    }\n\n    // frags sum\n    STlib_initNum(&w_frags,\n\t\t  ST_FRAGSX,\n\t\t  ST_FRAGSY,\n\t\t  tallnum,\n\t\t  &st_fragscount,\n\t\t  &st_fragson,\n\t\t  ST_FRAGSWIDTH);\n\n    // faces\n    STlib_initMultIcon(&w_faces,\n\t\t       ST_FACESX,\n\t\t       ST_FACESY,\n\t\t       faces,\n\t\t       &st_faceindex,\n\t\t       &st_statusbaron);\n\n    // armor percentage - should be colored later\n    STlib_initPercent(&w_armor,\n\t\t      ST_ARMORX,\n\t\t      ST_ARMORY,\n\t\t      tallnum,\n\t\t      &plyr->armorpoints,\n\t\t      &st_statusbaron, tallpercent);\n\n    // keyboxes 0-2\n    STlib_initMultIcon(&w_keyboxes[0],\n\t\t       ST_KEY0X,\n\t\t       ST_KEY0Y,\n\t\t       keys,\n\t\t       &keyboxes[0],\n\t\t       &st_statusbaron);\n    \n    STlib_initMultIcon(&w_keyboxes[1],\n\t\t       ST_KEY1X,\n\t\t       ST_KEY1Y,\n\t\t       keys,\n\t\t       &keyboxes[1],\n\t\t       &st_statusbaron);\n\n    STlib_initMultIcon(&w_keyboxes[2],\n\t\t       ST_KEY2X,\n\t\t       ST_KEY2Y,\n\t\t       keys,\n\t\t       &keyboxes[2],\n\t\t       &st_statusbaron);\n\n    // ammo count (all four kinds)\n    STlib_initNum(&w_ammo[0],\n\t\t  ST_AMMO0X,\n\t\t  ST_AMMO0Y,\n\t\t  shortnum,\n\t\t  &plyr->ammo[0],\n\t\t  &st_statusbaron,\n\t\t  ST_AMMO0WIDTH);\n\n    STlib_initNum(&w_ammo[1],\n\t\t  ST_AMMO1X,\n\t\t  ST_AMMO1Y,\n\t\t  shortnum,\n\t\t  &plyr->ammo[1],\n\t\t  &st_statusbaron,\n\t\t  ST_AMMO1WIDTH);\n\n    STlib_initNum(&w_ammo[2],\n\t\t  ST_AMMO2X,\n\t\t  ST_AMMO2Y,\n\t\t  shortnum,\n\t\t  &plyr->ammo[2],\n\t\t  &st_statusbaron,\n\t\t  ST_AMMO2WIDTH);\n    \n    STlib_initNum(&w_ammo[3],\n\t\t  ST_AMMO3X,\n\t\t  ST_AMMO3Y,\n\t\t  shortnum,\n\t\t  &plyr->ammo[3],\n\t\t  &st_statusbaron,\n\t\t  ST_AMMO3WIDTH);\n\n    // max ammo count (all four kinds)\n    STlib_initNum(&w_maxammo[0],\n\t\t  ST_MAXAMMO0X,\n\t\t  ST_MAXAMMO0Y,\n\t\t  shortnum,\n\t\t  &plyr->maxammo[0],\n\t\t  &st_statusbaron,\n\t\t  ST_MAXAMMO0WIDTH);\n\n    STlib_initNum(&w_maxammo[1],\n\t\t  ST_MAXAMMO1X,\n\t\t  ST_MAXAMMO1Y,\n\t\t  shortnum,\n\t\t  &plyr->maxammo[1],\n\t\t  &st_statusbaron,\n\t\t  ST_MAXAMMO1WIDTH);\n\n    STlib_initNum(&w_maxammo[2],\n\t\t  ST_MAXAMMO2X,\n\t\t  ST_MAXAMMO2Y,\n\t\t  shortnum,\n\t\t  &plyr->maxammo[2],\n\t\t  &st_statusbaron,\n\t\t  ST_MAXAMMO2WIDTH);\n    \n    STlib_initNum(&w_maxammo[3],\n\t\t  ST_MAXAMMO3X,\n\t\t  ST_MAXAMMO3Y,\n\t\t  shortnum,\n\t\t  &plyr->maxammo[3],\n\t\t  &st_statusbaron,\n\t\t  ST_MAXAMMO3WIDTH);\n\n}\n\nstatic boolean\tst_stopped = true;\n\n\nvoid ST_Start (void)\n{\n\n    if (!st_stopped)\n\tST_Stop();\n\n    ST_initData();\n    ST_createWidgets();\n    st_stopped = false;\n\n}\n\nvoid ST_Stop (void)\n{\n    if (st_stopped)\n\treturn;\n\n    I_SetPalette (W_CacheLumpNum (lu_palette, PU_CACHE));\n\n    st_stopped = true;\n}\n\nvoid ST_Init (void)\n{\n    ST_loadData();\n    st_backing_screen = (byte *) Z_Malloc(ST_WIDTH * ST_HEIGHT, PU_STATIC, 0);\n}\n\n"
  },
  {
    "path": "fbdoom/st_stuff.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tStatus bar code.\n//\tDoes the face/direction indicator animatin.\n//\tDoes palette indicators as well (red pain/berserk, bright pickup)\n//\n\n#ifndef __STSTUFF_H__\n#define __STSTUFF_H__\n\n#include \"doomtype.h\"\n#include \"d_event.h\"\n#include \"m_cheat.h\"\n\n// Size of statusbar.\n// Now sensitive for scaling.\n#define ST_HEIGHT\t32\n#define ST_WIDTH\tSCREENWIDTH\n#define ST_Y\t\t(SCREENHEIGHT - ST_HEIGHT)\n\n\n//\n// STATUS BAR\n//\n\n// Called by main loop.\nboolean ST_Responder (event_t* ev);\n\n// Called by main loop.\nvoid ST_Ticker (void);\n\n// Called by main loop.\nvoid ST_Drawer (boolean fullscreen, boolean refresh);\n\n// Called when the console player is spawned on each level.\nvoid ST_Start (void);\n\n// Called by startup code.\nvoid ST_Init (void);\n\n\n\n// States for status bar code.\ntypedef enum\n{\n    AutomapState,\n    FirstPersonState\n    \n} st_stateenum_t;\n\n\n// States for the chat code.\ntypedef enum\n{\n    StartChatState,\n    WaitDestState,\n    GetChatState\n    \n} st_chatstateenum_t;\n\n\n\nextern byte *st_backing_screen;\nextern cheatseq_t cheat_mus;\nextern cheatseq_t cheat_god;\nextern cheatseq_t cheat_ammo;\nextern cheatseq_t cheat_ammonokey;\nextern cheatseq_t cheat_noclip;\nextern cheatseq_t cheat_commercial_noclip;\nextern cheatseq_t cheat_powerup[7];\nextern cheatseq_t cheat_choppers;\nextern cheatseq_t cheat_clev;\nextern cheatseq_t cheat_mypos;\n\n\n#endif\n"
  },
  {
    "path": "fbdoom/statdump.c",
    "content": " /*\n\n Copyright(C) 2005-2014 Simon Howard\n\n This program is free software; you can redistribute it and/or\n modify it under the terms of the GNU General Public License\n as published by the Free Software Foundation; either version 2\n of the License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n --\n\n Functions for presenting the information captured from the statistics\n buffer to a file.\n\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"d_player.h\"\n#include \"d_mode.h\"\n#include \"m_argv.h\"\n\n#include \"statdump.h\"\n\n/* Par times for E1M1-E1M9. */\nstatic const int doom1_par_times[] =\n{\n    30, 75, 120, 90, 165, 180, 180, 30, 165,\n};\n\n/* Par times for MAP01-MAP09. */\nstatic const int doom2_par_times[] =\n{\n    30, 90, 120, 120, 90, 150, 120, 120, 270,\n};\n\n#if ORIGCODE\n\n/* Player colors. */\nstatic const char *player_colors[] =\n{\n    \"Green\", \"Indigo\", \"Brown\", \"Red\"\n};\n\n#endif\n\n// Array of end-of-level statistics that have been captured.\n\n#define MAX_CAPTURES 32\nstatic wbstartstruct_t captured_stats[MAX_CAPTURES];\nstatic int num_captured_stats = 0;\n\n#if ORIGCODE\nstatic GameMission_t discovered_gamemission = none;\n#endif\n\n#if ORIGCODE\n\n/* Try to work out whether this is a Doom 1 or Doom 2 game, by looking\n * at the episode and map, and the par times.  This is used to decide\n * how to format the level name.  Unfortunately, in some cases it is\n * impossible to determine whether this is Doom 1 or Doom 2. */\n\nstatic void DiscoverGamemode(wbstartstruct_t *stats, int num_stats)\n{\n    int partime;\n    int level;\n    int i;\n\n    if (discovered_gamemission != none)\n    {\n        return;\n    }\n\n    for (i=0; i<num_stats; ++i)\n    {\n        level = stats[i].last;\n\n        /* If episode 2, 3 or 4, this is Doom 1. */\n\n        if (stats[i].epsd > 0)\n        {\n            discovered_gamemission = doom;\n            return;\n        }\n\n        /* This is episode 1.  If this is level 10 or higher,\n           it must be Doom 2. */\n\n        if (level >= 9)\n        {\n            discovered_gamemission = doom2;\n            return;\n        }\n\n        /* Try to work out if this is Doom 1 or Doom 2 by looking\n           at the par time. */\n\n        partime = stats[i].partime;\n\n        if (partime == doom1_par_times[level] * TICRATE\n         && partime != doom2_par_times[level] * TICRATE)\n        {\n            discovered_gamemission = doom;\n            return;\n        }\n\n        if (partime != doom1_par_times[level] * TICRATE\n         && partime == doom2_par_times[level] * TICRATE)\n        {\n            discovered_gamemission = doom2;\n            return;\n        }\n    }\n}\n\n#endif\n\n#if ORIGCODE\n\n/* Returns the number of players active in the given stats buffer. */\n\nstatic int GetNumPlayers(wbstartstruct_t *stats)\n{\n    int i;\n    int num_players = 0;\n\n    for (i=0; i<MAXPLAYERS; ++i)\n    {\n        if (stats->plyr[i].in)\n        {\n            ++num_players;\n        }\n    }\n\n    return num_players;\n}\n\n#endif\n\n#if ORIGCODE\n\nstatic void PrintBanner(FILE *stream)\n{\n    fprintf(stream, \"===========================================\\n\");\n}\n\nstatic void PrintPercentage(FILE *stream, int amount, int total)\n{\n    if (total == 0)\n    {\n        fprintf(stream, \"0\");\n    }\n    else\n    {\n        fprintf(stream, \"%i / %i\", amount, total);\n\n        // statdump.exe is a 16-bit program, so very occasionally an\n        // integer overflow can occur when doing this calculation with\n        // a large value. Therefore, cast to short to give the same\n        // output.\n\n        fprintf(stream, \" (%i%%)\", (short) (amount * 100) / total);\n    }\n}\n\n#endif\n\n#if ORIGCODE\n\n/* Display statistics for a single player. */\n\nstatic void PrintPlayerStats(FILE *stream, wbstartstruct_t *stats,\n        int player_num)\n{\n    wbplayerstruct_t *player = &stats->plyr[player_num];\n\n    fprintf(stream, \"Player %i (%s):\\n\", player_num + 1,\n            player_colors[player_num]);\n\n    /* Kills percentage */\n\n    fprintf(stream, \"\\tKills: \");\n    PrintPercentage(stream, player->skills, stats->maxkills);\n    fprintf(stream, \"\\n\");\n\n    /* Items percentage */\n\n    fprintf(stream, \"\\tItems: \");\n    PrintPercentage(stream, player->sitems, stats->maxitems);\n    fprintf(stream, \"\\n\");\n\n    /* Secrets percentage */\n\n    fprintf(stream, \"\\tSecrets: \");\n    PrintPercentage(stream, player->ssecret, stats->maxsecret);\n    fprintf(stream, \"\\n\");\n}\n\n#endif\n\n#if ORIGCODE\n\n/* Frags table for multiplayer games. */\n\nstatic void PrintFragsTable(FILE *stream, wbstartstruct_t *stats)\n{\n    int x, y;\n\n    fprintf(stream, \"Frags:\\n\");\n\n    /* Print header */\n\n    fprintf(stream, \"\\t\\t\");\n\n    for (x=0; x<MAXPLAYERS; ++x)\n    {\n\n        if (!stats->plyr[x].in)\n        {\n            continue;\n        }\n\n        fprintf(stream, \"%s\\t\", player_colors[x]);\n    }\n\n    fprintf(stream, \"\\n\");\n\n    fprintf(stream, \"\\t\\t-------------------------------- VICTIMS\\n\");\n\n    /* Print table */\n\n    for (y=0; y<MAXPLAYERS; ++y)\n    {\n        if (!stats->plyr[y].in)\n        {\n            continue;\n        }\n\n        fprintf(stream, \"\\t%s\\t|\", player_colors[y]);\n\n        for (x=0; x<MAXPLAYERS; ++x)\n        {\n            if (!stats->plyr[x].in)\n            {\n                continue;\n            }\n\n            fprintf(stream, \"%i\\t\", stats->plyr[y].frags[x]);\n        }\n\n        fprintf(stream, \"\\n\");\n    }\n\n    fprintf(stream, \"\\t\\t|\\n\");\n    fprintf(stream, \"\\t     KILLERS\\n\");\n}\n\n#endif\n\n#if ORIGCODE\n\n/* Displays the level name: MAPxy or ExMy, depending on game mode. */\n\nstatic void PrintLevelName(FILE *stream, int episode, int level)\n{\n    PrintBanner(stream);\n\n    switch (discovered_gamemission)\n    {\n\n        case doom:\n            fprintf(stream, \"E%iM%i\\n\", episode + 1, level + 1);\n            break;\n        case doom2:\n            fprintf(stream, \"MAP%02i\\n\", level + 1);\n            break;\n        default:\n        case none:\n            fprintf(stream, \"E%iM%i / MAP%02i\\n\", \n                    episode + 1, level + 1, level + 1);\n            break;\n    }\n\n    PrintBanner(stream);\n}\n\n#endif\n\n#if ORIGCODE\n\n/* Print details of a statistics buffer to the given file. */\n\nstatic void PrintStats(FILE *stream, wbstartstruct_t *stats)\n{\n    int leveltime, partime;\n    int i;\n\n    PrintLevelName(stream, stats->epsd, stats->last);\n    fprintf(stream, \"\\n\");\n\n    leveltime = stats->plyr[0].stime / TICRATE;\n    partime = stats->partime / TICRATE;\n    fprintf(stream, \"Time: %i:%02i\", leveltime / 60, leveltime % 60);\n    fprintf(stream, \" (par: %i:%02i)\\n\", partime / 60, partime % 60);\n    fprintf(stream, \"\\n\");\n\n    for (i=0; i<MAXPLAYERS; ++i)\n    {\n        if (stats->plyr[i].in)\n        {\n            PrintPlayerStats(stream, stats, i);\n        }\n    }\n\n    if (GetNumPlayers(stats) >= 2)\n    {\n        PrintFragsTable(stream, stats);\n    }\n\n    fprintf(stream, \"\\n\");\n}\n\n#endif\n\nvoid StatCopy(wbstartstruct_t *stats)\n{\n    if (M_ParmExists(\"-statdump\") && num_captured_stats < MAX_CAPTURES)\n    {\n        memcpy(&captured_stats[num_captured_stats], stats,\n               sizeof(wbstartstruct_t));\n        ++num_captured_stats;\n    }\n}\n\nvoid StatDump(void)\n{\n#if ORIGCODE\n    FILE *dumpfile;\n    int i;\n\n    //!\n    // @category compat\n    // @arg <filename>\n    //\n    // Dump statistics information to the specified file on the levels\n    // that were played. The output from this option matches the output\n    // from statdump.exe (see ctrlapi.zip in the /idgames archive).\n    //\n\n    i = M_CheckParmWithArgs(\"-statdump\", 1);\n\n    if (i > 0)\n    {\n        printf(\"Statistics captured for %i level(s)\\n\", num_captured_stats);\n\n        // We actually know what the real gamemission is, but this has\n        // to match the output from statdump.exe.\n\n        DiscoverGamemode(captured_stats, num_captured_stats);\n\n        // Allow \"-\" as output file, for stdout.\n\n        if (strcmp(myargv[i + 1], \"-\") != 0)\n        {\n            dumpfile = fopen(myargv[i + 1], \"w\");\n        }\n        else\n        {\n            dumpfile = NULL;\n        }\n\n        for (i = 0; i < num_captured_stats; ++i)\n        {\n            PrintStats(dumpfile, &captured_stats[i]);\n        }\n\n        if (dumpfile != NULL)\n        {\n            fclose(dumpfile);\n        }\n    }\n#endif\n}\n\n"
  },
  {
    "path": "fbdoom/statdump.h",
    "content": " /*\n\n Copyright(C) 2005-2014 Simon Howard\n\n This program is free software; you can redistribute it and/or\n modify it under the terms of the GNU General Public License\n as published by the Free Software Foundation; either version 2\n of the License, or (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n */\n\n#ifndef DOOM_STATDUMP_H\n#define DOOM_STATDUMP_H\n\nvoid StatCopy(wbstartstruct_t *stats);\nvoid StatDump(void);\n\n#endif /* #ifndef DOOM_STATDUMP_H */\n"
  },
  {
    "path": "fbdoom/stubs.c",
    "content": "\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <string.h>\n\n//XXX FIXME\nin_addr_t inet_addr(const char *cp)\n{\n    return (in_addr_t)NULL;\n}\n\n"
  },
  {
    "path": "fbdoom/tables.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tLookup tables.\n//\tDo not try to look them up :-).\n//\tIn the order of appearance: \n//\n//\tint finetangent[4096]\t- Tangens LUT.\n//\t Should work with BAM fairly well (12 of 16bit,\n//      effectively, by shifting).\n//\n//\tint finesine[10240]\t\t- Sine lookup.\n//\t Guess what, serves as cosine, too.\n//\t Remarkable thing is, how to use BAMs with this? \n//\n//\tint tantoangle[2049]\t- ArcTan LUT,\n//\t  maps tan(angle) to angle fast. Gotta search.\n//\t\n//    \n\n#include \"tables.h\"\n\n// to get a global angle from cartesian coordinates, the coordinates are\n// flipped until they are in the first octant of the coordinate system, then\n// the y (<=x) is scaled and divided by x to get a tangent (slope) value\n// which is looked up in the tantoangle[] table.  The +1 size is to handle\n// the case when x==y without additional checking.\n\nint SlopeDiv(unsigned int num, unsigned int den)\n{\n    unsigned ans;\n    \n    if (den < 512)\n    {\n        return SLOPERANGE;\n    }\n    else\n    {\n        ans = (num << 3) / (den >> 8);\n\n        if (ans <= SLOPERANGE)\n        {\n            return ans;\n        }\n        else\n        {\n            return SLOPERANGE;\n        }\n    }\n}\n\nconst int finetangent[4096] =\n{\n    -170910304,-56965752,-34178904,-24413316,-18988036,-15535599,-13145455,-11392683,\n    -10052327,-8994149,-8137527,-7429880,-6835455,-6329090,-5892567,-5512368,\n    -5178251,-4882318,-4618375,-4381502,-4167737,-3973855,-3797206,-3635590,\n    -3487165,-3350381,-3223918,-3106651,-2997613,-2895966,-2800983,-2712030,\n    -2628549,-2550052,-2476104,-2406322,-2340362,-2277919,-2218719,-2162516,\n    -2109087,-2058233,-2009771,-1963536,-1919378,-1877161,-1836758,-1798063,\n    -1760956,-1725348,-1691149,-1658278,-1626658,-1596220,-1566898,-1538632,\n    -1511367,-1485049,-1459630,-1435065,-1411312,-1388330,-1366084,-1344537,\n    -1323658,-1303416,-1283783,-1264730,-1246234,-1228269,-1210813,-1193846,\n    -1177345,-1161294,-1145673,-1130465,-1115654,-1101225,-1087164,-1073455,\n    -1060087,-1047046,-1034322,-1021901,-1009774,-997931,-986361,-975054,\n    -964003,-953199,-942633,-932298,-922186,-912289,-902602,-893117,\n    -883829,-874730,-865817,-857081,-848520,-840127,-831898,-823827,\n    -815910,-808143,-800521,-793041,-785699,-778490,-771411,-764460,\n    -757631,-750922,-744331,-737853,-731486,-725227,-719074,-713023,\n    -707072,-701219,-695462,-689797,-684223,-678737,-673338,-668024,\n    -662792,-657640,-652568,-647572,-642651,-637803,-633028,-628323,\n    -623686,-619117,-614613,-610174,-605798,-601483,-597229,-593033,\n    -588896,-584815,-580789,-576818,-572901,-569035,-565221,-561456,\n    -557741,-554074,-550455,-546881,-543354,-539870,-536431,-533034,\n    -529680,-526366,-523094,-519861,-516667,-513512,-510394,-507313,\n    -504269,-501261,-498287,-495348,-492443,-489571,-486732,-483925,\n    -481150,-478406,-475692,-473009,-470355,-467730,-465133,-462565,\n    -460024,-457511,-455024,-452564,-450129,-447720,-445337,-442978,\n    -440643,-438332,-436045,-433781,-431540,-429321,-427125,-424951,\n    -422798,-420666,-418555,-416465,-414395,-412344,-410314,-408303,\n    -406311,-404338,-402384,-400448,-398530,-396630,-394747,-392882,\n    -391034,-389202,-387387,-385589,-383807,-382040,-380290,-378555,\n    -376835,-375130,-373440,-371765,-370105,-368459,-366826,-365208,\n    -363604,-362013,-360436,-358872,-357321,-355783,-354257,-352744,\n    -351244,-349756,-348280,-346816,-345364,-343924,-342495,-341078,\n    -339671,-338276,-336892,-335519,-334157,-332805,-331464,-330133,\n    -328812,-327502,-326201,-324910,-323629,-322358,-321097,-319844,\n    -318601,-317368,-316143,-314928,-313721,-312524,-311335,-310154,\n    -308983,-307819,-306664,-305517,-304379,-303248,-302126,-301011,\n    -299904,-298805,-297714,-296630,-295554,-294485,-293423,-292369,\n    -291322,-290282,-289249,-288223,-287204,-286192,-285186,-284188,\n    -283195,-282210,-281231,-280258,-279292,-278332,-277378,-276430,\n    -275489,-274553,-273624,-272700,-271782,-270871,-269965,-269064,\n    -268169,-267280,-266397,-265519,-264646,-263779,-262917,-262060,\n    -261209,-260363,-259522,-258686,-257855,-257029,-256208,-255392,\n    -254581,-253774,-252973,-252176,-251384,-250596,-249813,-249035,\n    -248261,-247492,-246727,-245966,-245210,-244458,-243711,-242967,\n    -242228,-241493,-240763,-240036,-239314,-238595,-237881,-237170,\n    -236463,-235761,-235062,-234367,-233676,-232988,-232304,-231624,\n    -230948,-230275,-229606,-228941,-228279,-227621,-226966,-226314,\n    -225666,-225022,-224381,-223743,-223108,-222477,-221849,-221225,\n    -220603,-219985,-219370,-218758,-218149,-217544,-216941,-216341,\n    -215745,-215151,-214561,-213973,-213389,-212807,-212228,-211652,\n    -211079,-210509,-209941,-209376,-208815,-208255,-207699,-207145,\n    -206594,-206045,-205500,-204956,-204416,-203878,-203342,-202809,\n    -202279,-201751,-201226,-200703,-200182,-199664,-199149,-198636,\n    -198125,-197616,-197110,-196606,-196105,-195606,-195109,-194614,\n    -194122,-193631,-193143,-192658,-192174,-191693,-191213,-190736,\n    -190261,-189789,-189318,-188849,-188382,-187918,-187455,-186995,\n    -186536,-186080,-185625,-185173,-184722,-184274,-183827,-183382,\n    -182939,-182498,-182059,-181622,-181186,-180753,-180321,-179891,\n    -179463,-179037,-178612,-178190,-177769,-177349,-176932,-176516,\n    -176102,-175690,-175279,-174870,-174463,-174057,-173653,-173251,\n    -172850,-172451,-172053,-171657,-171263,-170870,-170479,-170089,\n    -169701,-169315,-168930,-168546,-168164,-167784,-167405,-167027,\n    -166651,-166277,-165904,-165532,-165162,-164793,-164426,-164060,\n    -163695,-163332,-162970,-162610,-162251,-161893,-161537,-161182,\n    -160828,-160476,-160125,-159775,-159427,-159079,-158734,-158389,\n    -158046,-157704,-157363,-157024,-156686,-156349,-156013,-155678,\n    -155345,-155013,-154682,-154352,-154024,-153697,-153370,-153045,\n    -152722,-152399,-152077,-151757,-151438,-151120,-150803,-150487,\n    -150172,-149859,-149546,-149235,-148924,-148615,-148307,-148000,\n    -147693,-147388,-147084,-146782,-146480,-146179,-145879,-145580,\n    -145282,-144986,-144690,-144395,-144101,-143808,-143517,-143226,\n    -142936,-142647,-142359,-142072,-141786,-141501,-141217,-140934,\n    -140651,-140370,-140090,-139810,-139532,-139254,-138977,-138701,\n    -138426,-138152,-137879,-137607,-137335,-137065,-136795,-136526,\n    -136258,-135991,-135725,-135459,-135195,-134931,-134668,-134406,\n    -134145,-133884,-133625,-133366,-133108,-132851,-132594,-132339,\n    -132084,-131830,-131576,-131324,-131072,-130821,-130571,-130322,\n    -130073,-129825,-129578,-129332,-129086,-128841,-128597,-128353,\n    -128111,-127869,-127627,-127387,-127147,-126908,-126669,-126432,\n    -126195,-125959,-125723,-125488,-125254,-125020,-124787,-124555,\n    -124324,-124093,-123863,-123633,-123404,-123176,-122949,-122722,\n    -122496,-122270,-122045,-121821,-121597,-121374,-121152,-120930,\n    -120709,-120489,-120269,-120050,-119831,-119613,-119396,-119179,\n    -118963,-118747,-118532,-118318,-118104,-117891,-117678,-117466,\n    -117254,-117044,-116833,-116623,-116414,-116206,-115998,-115790,\n    -115583,-115377,-115171,-114966,-114761,-114557,-114354,-114151,\n    -113948,-113746,-113545,-113344,-113143,-112944,-112744,-112546,\n    -112347,-112150,-111952,-111756,-111560,-111364,-111169,-110974,\n    -110780,-110586,-110393,-110200,-110008,-109817,-109626,-109435,\n    -109245,-109055,-108866,-108677,-108489,-108301,-108114,-107927,\n    -107741,-107555,-107369,-107184,-107000,-106816,-106632,-106449,\n    -106266,-106084,-105902,-105721,-105540,-105360,-105180,-105000,\n    -104821,-104643,-104465,-104287,-104109,-103933,-103756,-103580,\n    -103404,-103229,-103054,-102880,-102706,-102533,-102360,-102187,\n    -102015,-101843,-101671,-101500,-101330,-101159,-100990,-100820,\n    -100651,-100482,-100314,-100146,-99979,-99812,-99645,-99479,\n    -99313,-99148,-98982,-98818,-98653,-98489,-98326,-98163,\n    -98000,-97837,-97675,-97513,-97352,-97191,-97030,-96870,\n    -96710,-96551,-96391,-96233,-96074,-95916,-95758,-95601,\n    -95444,-95287,-95131,-94975,-94819,-94664,-94509,-94354,\n    -94200,-94046,-93892,-93739,-93586,-93434,-93281,-93129,\n    -92978,-92826,-92675,-92525,-92375,-92225,-92075,-91926,\n    -91777,-91628,-91480,-91332,-91184,-91036,-90889,-90742,\n    -90596,-90450,-90304,-90158,-90013,-89868,-89724,-89579,\n    -89435,-89292,-89148,-89005,-88862,-88720,-88577,-88435,\n    -88294,-88152,-88011,-87871,-87730,-87590,-87450,-87310,\n    -87171,-87032,-86893,-86755,-86616,-86479,-86341,-86204,\n    -86066,-85930,-85793,-85657,-85521,-85385,-85250,-85114,\n    -84980,-84845,-84710,-84576,-84443,-84309,-84176,-84043,\n    -83910,-83777,-83645,-83513,-83381,-83250,-83118,-82987,\n    -82857,-82726,-82596,-82466,-82336,-82207,-82078,-81949,\n    -81820,-81691,-81563,-81435,-81307,-81180,-81053,-80925,\n    -80799,-80672,-80546,-80420,-80294,-80168,-80043,-79918,\n    -79793,-79668,-79544,-79420,-79296,-79172,-79048,-78925,\n    -78802,-78679,-78557,-78434,-78312,-78190,-78068,-77947,\n    -77826,-77705,-77584,-77463,-77343,-77223,-77103,-76983,\n    -76864,-76744,-76625,-76506,-76388,-76269,-76151,-76033,\n    -75915,-75797,-75680,-75563,-75446,-75329,-75213,-75096,\n    -74980,-74864,-74748,-74633,-74517,-74402,-74287,-74172,\n    -74058,-73944,-73829,-73715,-73602,-73488,-73375,-73262,\n    -73149,-73036,-72923,-72811,-72699,-72587,-72475,-72363,\n    -72252,-72140,-72029,-71918,-71808,-71697,-71587,-71477,\n    -71367,-71257,-71147,-71038,-70929,-70820,-70711,-70602,\n    -70494,-70385,-70277,-70169,-70061,-69954,-69846,-69739,\n    -69632,-69525,-69418,-69312,-69205,-69099,-68993,-68887,\n    -68781,-68676,-68570,-68465,-68360,-68255,-68151,-68046,\n    -67942,-67837,-67733,-67629,-67526,-67422,-67319,-67216,\n    -67113,-67010,-66907,-66804,-66702,-66600,-66498,-66396,\n    -66294,-66192,-66091,-65989,-65888,-65787,-65686,-65586,\n    -65485,-65385,-65285,-65185,-65085,-64985,-64885,-64786,\n    -64687,-64587,-64488,-64389,-64291,-64192,-64094,-63996,\n    -63897,-63799,-63702,-63604,-63506,-63409,-63312,-63215,\n    -63118,-63021,-62924,-62828,-62731,-62635,-62539,-62443,\n    -62347,-62251,-62156,-62060,-61965,-61870,-61775,-61680,\n    -61585,-61491,-61396,-61302,-61208,-61114,-61020,-60926,\n    -60833,-60739,-60646,-60552,-60459,-60366,-60273,-60181,\n    -60088,-59996,-59903,-59811,-59719,-59627,-59535,-59444,\n    -59352,-59261,-59169,-59078,-58987,-58896,-58805,-58715,\n    -58624,-58534,-58443,-58353,-58263,-58173,-58083,-57994,\n    -57904,-57815,-57725,-57636,-57547,-57458,-57369,-57281,\n    -57192,-57104,-57015,-56927,-56839,-56751,-56663,-56575,\n    -56487,-56400,-56312,-56225,-56138,-56051,-55964,-55877,\n    -55790,-55704,-55617,-55531,-55444,-55358,-55272,-55186,\n    -55100,-55015,-54929,-54843,-54758,-54673,-54587,-54502,\n    -54417,-54333,-54248,-54163,-54079,-53994,-53910,-53826,\n    -53741,-53657,-53574,-53490,-53406,-53322,-53239,-53156,\n    -53072,-52989,-52906,-52823,-52740,-52657,-52575,-52492,\n    -52410,-52327,-52245,-52163,-52081,-51999,-51917,-51835,\n    -51754,-51672,-51591,-51509,-51428,-51347,-51266,-51185,\n    -51104,-51023,-50942,-50862,-50781,-50701,-50621,-50540,\n    -50460,-50380,-50300,-50221,-50141,-50061,-49982,-49902,\n    -49823,-49744,-49664,-49585,-49506,-49427,-49349,-49270,\n    -49191,-49113,-49034,-48956,-48878,-48799,-48721,-48643,\n    -48565,-48488,-48410,-48332,-48255,-48177,-48100,-48022,\n    -47945,-47868,-47791,-47714,-47637,-47560,-47484,-47407,\n    -47331,-47254,-47178,-47102,-47025,-46949,-46873,-46797,\n    -46721,-46646,-46570,-46494,-46419,-46343,-46268,-46193,\n    -46118,-46042,-45967,-45892,-45818,-45743,-45668,-45593,\n    -45519,-45444,-45370,-45296,-45221,-45147,-45073,-44999,\n    -44925,-44851,-44778,-44704,-44630,-44557,-44483,-44410,\n    -44337,-44263,-44190,-44117,-44044,-43971,-43898,-43826,\n    -43753,-43680,-43608,-43535,-43463,-43390,-43318,-43246,\n    -43174,-43102,-43030,-42958,-42886,-42814,-42743,-42671,\n    -42600,-42528,-42457,-42385,-42314,-42243,-42172,-42101,\n    -42030,-41959,-41888,-41817,-41747,-41676,-41605,-41535,\n    -41465,-41394,-41324,-41254,-41184,-41113,-41043,-40973,\n    -40904,-40834,-40764,-40694,-40625,-40555,-40486,-40416,\n    -40347,-40278,-40208,-40139,-40070,-40001,-39932,-39863,\n    -39794,-39726,-39657,-39588,-39520,-39451,-39383,-39314,\n    -39246,-39178,-39110,-39042,-38973,-38905,-38837,-38770,\n    -38702,-38634,-38566,-38499,-38431,-38364,-38296,-38229,\n    -38161,-38094,-38027,-37960,-37893,-37826,-37759,-37692,\n    -37625,-37558,-37491,-37425,-37358,-37291,-37225,-37158,\n    -37092,-37026,-36959,-36893,-36827,-36761,-36695,-36629,\n    -36563,-36497,-36431,-36365,-36300,-36234,-36168,-36103,\n    -36037,-35972,-35907,-35841,-35776,-35711,-35646,-35580,\n    -35515,-35450,-35385,-35321,-35256,-35191,-35126,-35062,\n    -34997,-34932,-34868,-34803,-34739,-34675,-34610,-34546,\n    -34482,-34418,-34354,-34289,-34225,-34162,-34098,-34034,\n    -33970,-33906,-33843,-33779,-33715,-33652,-33588,-33525,\n    -33461,-33398,-33335,-33272,-33208,-33145,-33082,-33019,\n    -32956,-32893,-32830,-32767,-32705,-32642,-32579,-32516,\n    -32454,-32391,-32329,-32266,-32204,-32141,-32079,-32017,\n    -31955,-31892,-31830,-31768,-31706,-31644,-31582,-31520,\n    -31458,-31396,-31335,-31273,-31211,-31150,-31088,-31026,\n    -30965,-30904,-30842,-30781,-30719,-30658,-30597,-30536,\n    -30474,-30413,-30352,-30291,-30230,-30169,-30108,-30048,\n    -29987,-29926,-29865,-29805,-29744,-29683,-29623,-29562,\n    -29502,-29441,-29381,-29321,-29260,-29200,-29140,-29080,\n    -29020,-28959,-28899,-28839,-28779,-28719,-28660,-28600,\n    -28540,-28480,-28420,-28361,-28301,-28241,-28182,-28122,\n    -28063,-28003,-27944,-27884,-27825,-27766,-27707,-27647,\n    -27588,-27529,-27470,-27411,-27352,-27293,-27234,-27175,\n    -27116,-27057,-26998,-26940,-26881,-26822,-26763,-26705,\n    -26646,-26588,-26529,-26471,-26412,-26354,-26295,-26237,\n    -26179,-26120,-26062,-26004,-25946,-25888,-25830,-25772,\n    -25714,-25656,-25598,-25540,-25482,-25424,-25366,-25308,\n    -25251,-25193,-25135,-25078,-25020,-24962,-24905,-24847,\n    -24790,-24732,-24675,-24618,-24560,-24503,-24446,-24389,\n    -24331,-24274,-24217,-24160,-24103,-24046,-23989,-23932,\n    -23875,-23818,-23761,-23704,-23647,-23591,-23534,-23477,\n    -23420,-23364,-23307,-23250,-23194,-23137,-23081,-23024,\n    -22968,-22911,-22855,-22799,-22742,-22686,-22630,-22573,\n    -22517,-22461,-22405,-22349,-22293,-22237,-22181,-22125,\n    -22069,-22013,-21957,-21901,-21845,-21789,-21733,-21678,\n    -21622,-21566,-21510,-21455,-21399,-21343,-21288,-21232,\n    -21177,-21121,-21066,-21010,-20955,-20900,-20844,-20789,\n    -20734,-20678,-20623,-20568,-20513,-20457,-20402,-20347,\n    -20292,-20237,-20182,-20127,-20072,-20017,-19962,-19907,\n    -19852,-19797,-19742,-19688,-19633,-19578,-19523,-19469,\n    -19414,-19359,-19305,-19250,-19195,-19141,-19086,-19032,\n    -18977,-18923,-18868,-18814,-18760,-18705,-18651,-18597,\n    -18542,-18488,-18434,-18380,-18325,-18271,-18217,-18163,\n    -18109,-18055,-18001,-17946,-17892,-17838,-17784,-17731,\n    -17677,-17623,-17569,-17515,-17461,-17407,-17353,-17300,\n    -17246,-17192,-17138,-17085,-17031,-16977,-16924,-16870,\n    -16817,-16763,-16710,-16656,-16603,-16549,-16496,-16442,\n    -16389,-16335,-16282,-16229,-16175,-16122,-16069,-16015,\n    -15962,-15909,-15856,-15802,-15749,-15696,-15643,-15590,\n    -15537,-15484,-15431,-15378,-15325,-15272,-15219,-15166,\n    -15113,-15060,-15007,-14954,-14901,-14848,-14795,-14743,\n    -14690,-14637,-14584,-14531,-14479,-14426,-14373,-14321,\n    -14268,-14215,-14163,-14110,-14057,-14005,-13952,-13900,\n    -13847,-13795,-13742,-13690,-13637,-13585,-13533,-13480,\n    -13428,-13375,-13323,-13271,-13218,-13166,-13114,-13062,\n    -13009,-12957,-12905,-12853,-12800,-12748,-12696,-12644,\n    -12592,-12540,-12488,-12436,-12383,-12331,-12279,-12227,\n    -12175,-12123,-12071,-12019,-11967,-11916,-11864,-11812,\n    -11760,-11708,-11656,-11604,-11552,-11501,-11449,-11397,\n    -11345,-11293,-11242,-11190,-11138,-11086,-11035,-10983,\n    -10931,-10880,-10828,-10777,-10725,-10673,-10622,-10570,\n    -10519,-10467,-10415,-10364,-10312,-10261,-10209,-10158,\n    -10106,-10055,-10004,-9952,-9901,-9849,-9798,-9747,\n    -9695,-9644,-9592,-9541,-9490,-9438,-9387,-9336,\n    -9285,-9233,-9182,-9131,-9080,-9028,-8977,-8926,\n    -8875,-8824,-8772,-8721,-8670,-8619,-8568,-8517,\n    -8466,-8414,-8363,-8312,-8261,-8210,-8159,-8108,\n    -8057,-8006,-7955,-7904,-7853,-7802,-7751,-7700,\n    -7649,-7598,-7547,-7496,-7445,-7395,-7344,-7293,\n    -7242,-7191,-7140,-7089,-7038,-6988,-6937,-6886,\n    -6835,-6784,-6733,-6683,-6632,-6581,-6530,-6480,\n    -6429,-6378,-6327,-6277,-6226,-6175,-6124,-6074,\n    -6023,-5972,-5922,-5871,-5820,-5770,-5719,-5668,\n    -5618,-5567,-5517,-5466,-5415,-5365,-5314,-5264,\n    -5213,-5162,-5112,-5061,-5011,-4960,-4910,-4859,\n    -4808,-4758,-4707,-4657,-4606,-4556,-4505,-4455,\n    -4404,-4354,-4303,-4253,-4202,-4152,-4101,-4051,\n    -4001,-3950,-3900,-3849,-3799,-3748,-3698,-3648,\n    -3597,-3547,-3496,-3446,-3395,-3345,-3295,-3244,\n    -3194,-3144,-3093,-3043,-2992,-2942,-2892,-2841,\n    -2791,-2741,-2690,-2640,-2590,-2539,-2489,-2439,\n    -2388,-2338,-2288,-2237,-2187,-2137,-2086,-2036,\n    -1986,-1935,-1885,-1835,-1784,-1734,-1684,-1633,\n    -1583,-1533,-1483,-1432,-1382,-1332,-1281,-1231,\n    -1181,-1131,-1080,-1030,-980,-929,-879,-829,\n    -779,-728,-678,-628,-578,-527,-477,-427,\n    -376,-326,-276,-226,-175,-125,-75,-25,\n    25,75,125,175,226,276,326,376,\n    427,477,527,578,628,678,728,779,\n    829,879,929,980,1030,1080,1131,1181,\n    1231,1281,1332,1382,1432,1483,1533,1583,\n    1633,1684,1734,1784,1835,1885,1935,1986,\n    2036,2086,2137,2187,2237,2288,2338,2388,\n    2439,2489,2539,2590,2640,2690,2741,2791,\n    2841,2892,2942,2992,3043,3093,3144,3194,\n    3244,3295,3345,3395,3446,3496,3547,3597,\n    3648,3698,3748,3799,3849,3900,3950,4001,\n    4051,4101,4152,4202,4253,4303,4354,4404,\n    4455,4505,4556,4606,4657,4707,4758,4808,\n    4859,4910,4960,5011,5061,5112,5162,5213,\n    5264,5314,5365,5415,5466,5517,5567,5618,\n    5668,5719,5770,5820,5871,5922,5972,6023,\n    6074,6124,6175,6226,6277,6327,6378,6429,\n    6480,6530,6581,6632,6683,6733,6784,6835,\n    6886,6937,6988,7038,7089,7140,7191,7242,\n    7293,7344,7395,7445,7496,7547,7598,7649,\n    7700,7751,7802,7853,7904,7955,8006,8057,\n    8108,8159,8210,8261,8312,8363,8414,8466,\n    8517,8568,8619,8670,8721,8772,8824,8875,\n    8926,8977,9028,9080,9131,9182,9233,9285,\n    9336,9387,9438,9490,9541,9592,9644,9695,\n    9747,9798,9849,9901,9952,10004,10055,10106,\n    10158,10209,10261,10312,10364,10415,10467,10519,\n    10570,10622,10673,10725,10777,10828,10880,10931,\n    10983,11035,11086,11138,11190,11242,11293,11345,\n    11397,11449,11501,11552,11604,11656,11708,11760,\n    11812,11864,11916,11967,12019,12071,12123,12175,\n    12227,12279,12331,12383,12436,12488,12540,12592,\n    12644,12696,12748,12800,12853,12905,12957,13009,\n    13062,13114,13166,13218,13271,13323,13375,13428,\n    13480,13533,13585,13637,13690,13742,13795,13847,\n    13900,13952,14005,14057,14110,14163,14215,14268,\n    14321,14373,14426,14479,14531,14584,14637,14690,\n    14743,14795,14848,14901,14954,15007,15060,15113,\n    15166,15219,15272,15325,15378,15431,15484,15537,\n    15590,15643,15696,15749,15802,15856,15909,15962,\n    16015,16069,16122,16175,16229,16282,16335,16389,\n    16442,16496,16549,16603,16656,16710,16763,16817,\n    16870,16924,16977,17031,17085,17138,17192,17246,\n    17300,17353,17407,17461,17515,17569,17623,17677,\n    17731,17784,17838,17892,17946,18001,18055,18109,\n    18163,18217,18271,18325,18380,18434,18488,18542,\n    18597,18651,18705,18760,18814,18868,18923,18977,\n    19032,19086,19141,19195,19250,19305,19359,19414,\n    19469,19523,19578,19633,19688,19742,19797,19852,\n    19907,19962,20017,20072,20127,20182,20237,20292,\n    20347,20402,20457,20513,20568,20623,20678,20734,\n    20789,20844,20900,20955,21010,21066,21121,21177,\n    21232,21288,21343,21399,21455,21510,21566,21622,\n    21678,21733,21789,21845,21901,21957,22013,22069,\n    22125,22181,22237,22293,22349,22405,22461,22517,\n    22573,22630,22686,22742,22799,22855,22911,22968,\n    23024,23081,23137,23194,23250,23307,23364,23420,\n    23477,23534,23591,23647,23704,23761,23818,23875,\n    23932,23989,24046,24103,24160,24217,24274,24331,\n    24389,24446,24503,24560,24618,24675,24732,24790,\n    24847,24905,24962,25020,25078,25135,25193,25251,\n    25308,25366,25424,25482,25540,25598,25656,25714,\n    25772,25830,25888,25946,26004,26062,26120,26179,\n    26237,26295,26354,26412,26471,26529,26588,26646,\n    26705,26763,26822,26881,26940,26998,27057,27116,\n    27175,27234,27293,27352,27411,27470,27529,27588,\n    27647,27707,27766,27825,27884,27944,28003,28063,\n    28122,28182,28241,28301,28361,28420,28480,28540,\n    28600,28660,28719,28779,28839,28899,28959,29020,\n    29080,29140,29200,29260,29321,29381,29441,29502,\n    29562,29623,29683,29744,29805,29865,29926,29987,\n    30048,30108,30169,30230,30291,30352,30413,30474,\n    30536,30597,30658,30719,30781,30842,30904,30965,\n    31026,31088,31150,31211,31273,31335,31396,31458,\n    31520,31582,31644,31706,31768,31830,31892,31955,\n    32017,32079,32141,32204,32266,32329,32391,32454,\n    32516,32579,32642,32705,32767,32830,32893,32956,\n    33019,33082,33145,33208,33272,33335,33398,33461,\n    33525,33588,33652,33715,33779,33843,33906,33970,\n    34034,34098,34162,34225,34289,34354,34418,34482,\n    34546,34610,34675,34739,34803,34868,34932,34997,\n    35062,35126,35191,35256,35321,35385,35450,35515,\n    35580,35646,35711,35776,35841,35907,35972,36037,\n    36103,36168,36234,36300,36365,36431,36497,36563,\n    36629,36695,36761,36827,36893,36959,37026,37092,\n    37158,37225,37291,37358,37425,37491,37558,37625,\n    37692,37759,37826,37893,37960,38027,38094,38161,\n    38229,38296,38364,38431,38499,38566,38634,38702,\n    38770,38837,38905,38973,39042,39110,39178,39246,\n    39314,39383,39451,39520,39588,39657,39726,39794,\n    39863,39932,40001,40070,40139,40208,40278,40347,\n    40416,40486,40555,40625,40694,40764,40834,40904,\n    40973,41043,41113,41184,41254,41324,41394,41465,\n    41535,41605,41676,41747,41817,41888,41959,42030,\n    42101,42172,42243,42314,42385,42457,42528,42600,\n    42671,42743,42814,42886,42958,43030,43102,43174,\n    43246,43318,43390,43463,43535,43608,43680,43753,\n    43826,43898,43971,44044,44117,44190,44263,44337,\n    44410,44483,44557,44630,44704,44778,44851,44925,\n    44999,45073,45147,45221,45296,45370,45444,45519,\n    45593,45668,45743,45818,45892,45967,46042,46118,\n    46193,46268,46343,46419,46494,46570,46646,46721,\n    46797,46873,46949,47025,47102,47178,47254,47331,\n    47407,47484,47560,47637,47714,47791,47868,47945,\n    48022,48100,48177,48255,48332,48410,48488,48565,\n    48643,48721,48799,48878,48956,49034,49113,49191,\n    49270,49349,49427,49506,49585,49664,49744,49823,\n    49902,49982,50061,50141,50221,50300,50380,50460,\n    50540,50621,50701,50781,50862,50942,51023,51104,\n    51185,51266,51347,51428,51509,51591,51672,51754,\n    51835,51917,51999,52081,52163,52245,52327,52410,\n    52492,52575,52657,52740,52823,52906,52989,53072,\n    53156,53239,53322,53406,53490,53574,53657,53741,\n    53826,53910,53994,54079,54163,54248,54333,54417,\n    54502,54587,54673,54758,54843,54929,55015,55100,\n    55186,55272,55358,55444,55531,55617,55704,55790,\n    55877,55964,56051,56138,56225,56312,56400,56487,\n    56575,56663,56751,56839,56927,57015,57104,57192,\n    57281,57369,57458,57547,57636,57725,57815,57904,\n    57994,58083,58173,58263,58353,58443,58534,58624,\n    58715,58805,58896,58987,59078,59169,59261,59352,\n    59444,59535,59627,59719,59811,59903,59996,60088,\n    60181,60273,60366,60459,60552,60646,60739,60833,\n    60926,61020,61114,61208,61302,61396,61491,61585,\n    61680,61775,61870,61965,62060,62156,62251,62347,\n    62443,62539,62635,62731,62828,62924,63021,63118,\n    63215,63312,63409,63506,63604,63702,63799,63897,\n    63996,64094,64192,64291,64389,64488,64587,64687,\n    64786,64885,64985,65085,65185,65285,65385,65485,\n    65586,65686,65787,65888,65989,66091,66192,66294,\n    66396,66498,66600,66702,66804,66907,67010,67113,\n    67216,67319,67422,67526,67629,67733,67837,67942,\n    68046,68151,68255,68360,68465,68570,68676,68781,\n    68887,68993,69099,69205,69312,69418,69525,69632,\n    69739,69846,69954,70061,70169,70277,70385,70494,\n    70602,70711,70820,70929,71038,71147,71257,71367,\n    71477,71587,71697,71808,71918,72029,72140,72252,\n    72363,72475,72587,72699,72811,72923,73036,73149,\n    73262,73375,73488,73602,73715,73829,73944,74058,\n    74172,74287,74402,74517,74633,74748,74864,74980,\n    75096,75213,75329,75446,75563,75680,75797,75915,\n    76033,76151,76269,76388,76506,76625,76744,76864,\n    76983,77103,77223,77343,77463,77584,77705,77826,\n    77947,78068,78190,78312,78434,78557,78679,78802,\n    78925,79048,79172,79296,79420,79544,79668,79793,\n    79918,80043,80168,80294,80420,80546,80672,80799,\n    80925,81053,81180,81307,81435,81563,81691,81820,\n    81949,82078,82207,82336,82466,82596,82726,82857,\n    82987,83118,83250,83381,83513,83645,83777,83910,\n    84043,84176,84309,84443,84576,84710,84845,84980,\n    85114,85250,85385,85521,85657,85793,85930,86066,\n    86204,86341,86479,86616,86755,86893,87032,87171,\n    87310,87450,87590,87730,87871,88011,88152,88294,\n    88435,88577,88720,88862,89005,89148,89292,89435,\n    89579,89724,89868,90013,90158,90304,90450,90596,\n    90742,90889,91036,91184,91332,91480,91628,91777,\n    91926,92075,92225,92375,92525,92675,92826,92978,\n    93129,93281,93434,93586,93739,93892,94046,94200,\n    94354,94509,94664,94819,94975,95131,95287,95444,\n    95601,95758,95916,96074,96233,96391,96551,96710,\n    96870,97030,97191,97352,97513,97675,97837,98000,\n    98163,98326,98489,98653,98818,98982,99148,99313,\n    99479,99645,99812,99979,100146,100314,100482,100651,\n    100820,100990,101159,101330,101500,101671,101843,102015,\n    102187,102360,102533,102706,102880,103054,103229,103404,\n    103580,103756,103933,104109,104287,104465,104643,104821,\n    105000,105180,105360,105540,105721,105902,106084,106266,\n    106449,106632,106816,107000,107184,107369,107555,107741,\n    107927,108114,108301,108489,108677,108866,109055,109245,\n    109435,109626,109817,110008,110200,110393,110586,110780,\n    110974,111169,111364,111560,111756,111952,112150,112347,\n    112546,112744,112944,113143,113344,113545,113746,113948,\n    114151,114354,114557,114761,114966,115171,115377,115583,\n    115790,115998,116206,116414,116623,116833,117044,117254,\n    117466,117678,117891,118104,118318,118532,118747,118963,\n    119179,119396,119613,119831,120050,120269,120489,120709,\n    120930,121152,121374,121597,121821,122045,122270,122496,\n    122722,122949,123176,123404,123633,123863,124093,124324,\n    124555,124787,125020,125254,125488,125723,125959,126195,\n    126432,126669,126908,127147,127387,127627,127869,128111,\n    128353,128597,128841,129086,129332,129578,129825,130073,\n    130322,130571,130821,131072,131324,131576,131830,132084,\n    132339,132594,132851,133108,133366,133625,133884,134145,\n    134406,134668,134931,135195,135459,135725,135991,136258,\n    136526,136795,137065,137335,137607,137879,138152,138426,\n    138701,138977,139254,139532,139810,140090,140370,140651,\n    140934,141217,141501,141786,142072,142359,142647,142936,\n    143226,143517,143808,144101,144395,144690,144986,145282,\n    145580,145879,146179,146480,146782,147084,147388,147693,\n    148000,148307,148615,148924,149235,149546,149859,150172,\n    150487,150803,151120,151438,151757,152077,152399,152722,\n    153045,153370,153697,154024,154352,154682,155013,155345,\n    155678,156013,156349,156686,157024,157363,157704,158046,\n    158389,158734,159079,159427,159775,160125,160476,160828,\n    161182,161537,161893,162251,162610,162970,163332,163695,\n    164060,164426,164793,165162,165532,165904,166277,166651,\n    167027,167405,167784,168164,168546,168930,169315,169701,\n    170089,170479,170870,171263,171657,172053,172451,172850,\n    173251,173653,174057,174463,174870,175279,175690,176102,\n    176516,176932,177349,177769,178190,178612,179037,179463,\n    179891,180321,180753,181186,181622,182059,182498,182939,\n    183382,183827,184274,184722,185173,185625,186080,186536,\n    186995,187455,187918,188382,188849,189318,189789,190261,\n    190736,191213,191693,192174,192658,193143,193631,194122,\n    194614,195109,195606,196105,196606,197110,197616,198125,\n    198636,199149,199664,200182,200703,201226,201751,202279,\n    202809,203342,203878,204416,204956,205500,206045,206594,\n    207145,207699,208255,208815,209376,209941,210509,211079,\n    211652,212228,212807,213389,213973,214561,215151,215745,\n    216341,216941,217544,218149,218758,219370,219985,220603,\n    221225,221849,222477,223108,223743,224381,225022,225666,\n    226314,226966,227621,228279,228941,229606,230275,230948,\n    231624,232304,232988,233676,234367,235062,235761,236463,\n    237170,237881,238595,239314,240036,240763,241493,242228,\n    242967,243711,244458,245210,245966,246727,247492,248261,\n    249035,249813,250596,251384,252176,252973,253774,254581,\n    255392,256208,257029,257855,258686,259522,260363,261209,\n    262060,262917,263779,264646,265519,266397,267280,268169,\n    269064,269965,270871,271782,272700,273624,274553,275489,\n    276430,277378,278332,279292,280258,281231,282210,283195,\n    284188,285186,286192,287204,288223,289249,290282,291322,\n    292369,293423,294485,295554,296630,297714,298805,299904,\n    301011,302126,303248,304379,305517,306664,307819,308983,\n    310154,311335,312524,313721,314928,316143,317368,318601,\n    319844,321097,322358,323629,324910,326201,327502,328812,\n    330133,331464,332805,334157,335519,336892,338276,339671,\n    341078,342495,343924,345364,346816,348280,349756,351244,\n    352744,354257,355783,357321,358872,360436,362013,363604,\n    365208,366826,368459,370105,371765,373440,375130,376835,\n    378555,380290,382040,383807,385589,387387,389202,391034,\n    392882,394747,396630,398530,400448,402384,404338,406311,\n    408303,410314,412344,414395,416465,418555,420666,422798,\n    424951,427125,429321,431540,433781,436045,438332,440643,\n    442978,445337,447720,450129,452564,455024,457511,460024,\n    462565,465133,467730,470355,473009,475692,478406,481150,\n    483925,486732,489571,492443,495348,498287,501261,504269,\n    507313,510394,513512,516667,519861,523094,526366,529680,\n    533034,536431,539870,543354,546881,550455,554074,557741,\n    561456,565221,569035,572901,576818,580789,584815,588896,\n    593033,597229,601483,605798,610174,614613,619117,623686,\n    628323,633028,637803,642651,647572,652568,657640,662792,\n    668024,673338,678737,684223,689797,695462,701219,707072,\n    713023,719074,725227,731486,737853,744331,750922,757631,\n    764460,771411,778490,785699,793041,800521,808143,815910,\n    823827,831898,840127,848520,857081,865817,874730,883829,\n    893117,902602,912289,922186,932298,942633,953199,964003,\n    975054,986361,997931,1009774,1021901,1034322,1047046,1060087,\n    1073455,1087164,1101225,1115654,1130465,1145673,1161294,1177345,\n    1193846,1210813,1228269,1246234,1264730,1283783,1303416,1323658,\n    1344537,1366084,1388330,1411312,1435065,1459630,1485049,1511367,\n    1538632,1566898,1596220,1626658,1658278,1691149,1725348,1760956,\n    1798063,1836758,1877161,1919378,1963536,2009771,2058233,2109087,\n    2162516,2218719,2277919,2340362,2406322,2476104,2550052,2628549,\n    2712030,2800983,2895966,2997613,3106651,3223918,3350381,3487165,\n    3635590,3797206,3973855,4167737,4381502,4618375,4882318,5178251,\n    5512368,5892567,6329090,6835455,7429880,8137527,8994149,10052327,\n    11392683,13145455,15535599,18988036,24413316,34178904,56965752,170910304\n};\n\n\nconst int finesine[10240] =\n{\n    25,75,125,175,226,276,326,376,\n    427,477,527,578,628,678,728,779,\n    829,879,929,980,1030,1080,1130,1181,\n    1231,1281,1331,1382,1432,1482,1532,1583,\n    1633,1683,1733,1784,1834,1884,1934,1985,\n    2035,2085,2135,2186,2236,2286,2336,2387,\n    2437,2487,2537,2587,2638,2688,2738,2788,\n    2839,2889,2939,2989,3039,3090,3140,3190,\n    3240,3291,3341,3391,3441,3491,3541,3592,\n    3642,3692,3742,3792,3843,3893,3943,3993,\n    4043,4093,4144,4194,4244,4294,4344,4394,\n    4445,4495,4545,4595,4645,4695,4745,4796,\n    4846,4896,4946,4996,5046,5096,5146,5197,\n    5247,5297,5347,5397,5447,5497,5547,5597,\n    5647,5697,5748,5798,5848,5898,5948,5998,\n    6048,6098,6148,6198,6248,6298,6348,6398,\n    6448,6498,6548,6598,6648,6698,6748,6798,\n    6848,6898,6948,6998,7048,7098,7148,7198,\n    7248,7298,7348,7398,7448,7498,7548,7598,\n    7648,7697,7747,7797,7847,7897,7947,7997,\n    8047,8097,8147,8196,8246,8296,8346,8396,\n    8446,8496,8545,8595,8645,8695,8745,8794,\n    8844,8894,8944,8994,9043,9093,9143,9193,\n    9243,9292,9342,9392,9442,9491,9541,9591,\n    9640,9690,9740,9790,9839,9889,9939,9988,\n    10038,10088,10137,10187,10237,10286,10336,10386,\n    10435,10485,10534,10584,10634,10683,10733,10782,\n    10832,10882,10931,10981,11030,11080,11129,11179,\n    11228,11278,11327,11377,11426,11476,11525,11575,\n    11624,11674,11723,11773,11822,11872,11921,11970,\n    12020,12069,12119,12168,12218,12267,12316,12366,\n    12415,12464,12514,12563,12612,12662,12711,12760,\n    12810,12859,12908,12957,13007,13056,13105,13154,\n    13204,13253,13302,13351,13401,13450,13499,13548,\n    13597,13647,13696,13745,13794,13843,13892,13941,\n    13990,14040,14089,14138,14187,14236,14285,14334,\n    14383,14432,14481,14530,14579,14628,14677,14726,\n    14775,14824,14873,14922,14971,15020,15069,15118,\n    15167,15215,15264,15313,15362,15411,15460,15509,\n    15557,15606,15655,15704,15753,15802,15850,15899,\n    15948,15997,16045,16094,16143,16191,16240,16289,\n    16338,16386,16435,16484,16532,16581,16629,16678,\n    16727,16775,16824,16872,16921,16970,17018,17067,\n    17115,17164,17212,17261,17309,17358,17406,17455,\n    17503,17551,17600,17648,17697,17745,17793,17842,\n    17890,17939,17987,18035,18084,18132,18180,18228,\n    18277,18325,18373,18421,18470,18518,18566,18614,\n    18663,18711,18759,18807,18855,18903,18951,19000,\n    19048,19096,19144,19192,19240,19288,19336,19384,\n    19432,19480,19528,19576,19624,19672,19720,19768,\n    19816,19864,19912,19959,20007,20055,20103,20151,\n    20199,20246,20294,20342,20390,20438,20485,20533,\n    20581,20629,20676,20724,20772,20819,20867,20915,\n    20962,21010,21057,21105,21153,21200,21248,21295,\n    21343,21390,21438,21485,21533,21580,21628,21675,\n    21723,21770,21817,21865,21912,21960,22007,22054,\n    22102,22149,22196,22243,22291,22338,22385,22433,\n    22480,22527,22574,22621,22668,22716,22763,22810,\n    22857,22904,22951,22998,23045,23092,23139,23186,\n    23233,23280,23327,23374,23421,23468,23515,23562,\n    23609,23656,23703,23750,23796,23843,23890,23937,\n    23984,24030,24077,24124,24171,24217,24264,24311,\n    24357,24404,24451,24497,24544,24591,24637,24684,\n    24730,24777,24823,24870,24916,24963,25009,25056,\n    25102,25149,25195,25241,25288,25334,25381,25427,\n    25473,25520,25566,25612,25658,25705,25751,25797,\n    25843,25889,25936,25982,26028,26074,26120,26166,\n    26212,26258,26304,26350,26396,26442,26488,26534,\n    26580,26626,26672,26718,26764,26810,26856,26902,\n    26947,26993,27039,27085,27131,27176,27222,27268,\n    27313,27359,27405,27450,27496,27542,27587,27633,\n    27678,27724,27770,27815,27861,27906,27952,27997,\n    28042,28088,28133,28179,28224,28269,28315,28360,\n    28405,28451,28496,28541,28586,28632,28677,28722,\n    28767,28812,28858,28903,28948,28993,29038,29083,\n    29128,29173,29218,29263,29308,29353,29398,29443,\n    29488,29533,29577,29622,29667,29712,29757,29801,\n    29846,29891,29936,29980,30025,30070,30114,30159,\n    30204,30248,30293,30337,30382,30426,30471,30515,\n    30560,30604,30649,30693,30738,30782,30826,30871,\n    30915,30959,31004,31048,31092,31136,31181,31225,\n    31269,31313,31357,31402,31446,31490,31534,31578,\n    31622,31666,31710,31754,31798,31842,31886,31930,\n    31974,32017,32061,32105,32149,32193,32236,32280,\n    32324,32368,32411,32455,32499,32542,32586,32630,\n    32673,32717,32760,32804,32847,32891,32934,32978,\n    33021,33065,33108,33151,33195,33238,33281,33325,\n    33368,33411,33454,33498,33541,33584,33627,33670,\n    33713,33756,33799,33843,33886,33929,33972,34015,\n    34057,34100,34143,34186,34229,34272,34315,34358,\n    34400,34443,34486,34529,34571,34614,34657,34699,\n    34742,34785,34827,34870,34912,34955,34997,35040,\n    35082,35125,35167,35210,35252,35294,35337,35379,\n    35421,35464,35506,35548,35590,35633,35675,35717,\n    35759,35801,35843,35885,35927,35969,36011,36053,\n    36095,36137,36179,36221,36263,36305,36347,36388,\n    36430,36472,36514,36555,36597,36639,36681,36722,\n    36764,36805,36847,36889,36930,36972,37013,37055,\n    37096,37137,37179,37220,37262,37303,37344,37386,\n    37427,37468,37509,37551,37592,37633,37674,37715,\n    37756,37797,37838,37879,37920,37961,38002,38043,\n    38084,38125,38166,38207,38248,38288,38329,38370,\n    38411,38451,38492,38533,38573,38614,38655,38695,\n    38736,38776,38817,38857,38898,38938,38979,39019,\n    39059,39100,39140,39180,39221,39261,39301,39341,\n    39382,39422,39462,39502,39542,39582,39622,39662,\n    39702,39742,39782,39822,39862,39902,39942,39982,\n    40021,40061,40101,40141,40180,40220,40260,40300,\n    40339,40379,40418,40458,40497,40537,40576,40616,\n    40655,40695,40734,40773,40813,40852,40891,40931,\n    40970,41009,41048,41087,41127,41166,41205,41244,\n    41283,41322,41361,41400,41439,41478,41517,41556,\n    41595,41633,41672,41711,41750,41788,41827,41866,\n    41904,41943,41982,42020,42059,42097,42136,42174,\n    42213,42251,42290,42328,42366,42405,42443,42481,\n    42520,42558,42596,42634,42672,42711,42749,42787,\n    42825,42863,42901,42939,42977,43015,43053,43091,\n    43128,43166,43204,43242,43280,43317,43355,43393,\n    43430,43468,43506,43543,43581,43618,43656,43693,\n    43731,43768,43806,43843,43880,43918,43955,43992,\n    44029,44067,44104,44141,44178,44215,44252,44289,\n    44326,44363,44400,44437,44474,44511,44548,44585,\n    44622,44659,44695,44732,44769,44806,44842,44879,\n    44915,44952,44989,45025,45062,45098,45135,45171,\n    45207,45244,45280,45316,45353,45389,45425,45462,\n    45498,45534,45570,45606,45642,45678,45714,45750,\n    45786,45822,45858,45894,45930,45966,46002,46037,\n    46073,46109,46145,46180,46216,46252,46287,46323,\n    46358,46394,46429,46465,46500,46536,46571,46606,\n    46642,46677,46712,46747,46783,46818,46853,46888,\n    46923,46958,46993,47028,47063,47098,47133,47168,\n    47203,47238,47273,47308,47342,47377,47412,47446,\n    47481,47516,47550,47585,47619,47654,47688,47723,\n    47757,47792,47826,47860,47895,47929,47963,47998,\n    48032,48066,48100,48134,48168,48202,48237,48271,\n    48305,48338,48372,48406,48440,48474,48508,48542,\n    48575,48609,48643,48676,48710,48744,48777,48811,\n    48844,48878,48911,48945,48978,49012,49045,49078,\n    49112,49145,49178,49211,49244,49278,49311,49344,\n    49377,49410,49443,49476,49509,49542,49575,49608,\n    49640,49673,49706,49739,49771,49804,49837,49869,\n    49902,49935,49967,50000,50032,50065,50097,50129,\n    50162,50194,50226,50259,50291,50323,50355,50387,\n    50420,50452,50484,50516,50548,50580,50612,50644,\n    50675,50707,50739,50771,50803,50834,50866,50898,\n    50929,50961,50993,51024,51056,51087,51119,51150,\n    51182,51213,51244,51276,51307,51338,51369,51401,\n    51432,51463,51494,51525,51556,51587,51618,51649,\n    51680,51711,51742,51773,51803,51834,51865,51896,\n    51926,51957,51988,52018,52049,52079,52110,52140,\n    52171,52201,52231,52262,52292,52322,52353,52383,\n    52413,52443,52473,52503,52534,52564,52594,52624,\n    52653,52683,52713,52743,52773,52803,52832,52862,\n    52892,52922,52951,52981,53010,53040,53069,53099,\n    53128,53158,53187,53216,53246,53275,53304,53334,\n    53363,53392,53421,53450,53479,53508,53537,53566,\n    53595,53624,53653,53682,53711,53739,53768,53797,\n    53826,53854,53883,53911,53940,53969,53997,54026,\n    54054,54082,54111,54139,54167,54196,54224,54252,\n    54280,54308,54337,54365,54393,54421,54449,54477,\n    54505,54533,54560,54588,54616,54644,54672,54699,\n    54727,54755,54782,54810,54837,54865,54892,54920,\n    54947,54974,55002,55029,55056,55084,55111,55138,\n    55165,55192,55219,55246,55274,55300,55327,55354,\n    55381,55408,55435,55462,55489,55515,55542,55569,\n    55595,55622,55648,55675,55701,55728,55754,55781,\n    55807,55833,55860,55886,55912,55938,55965,55991,\n    56017,56043,56069,56095,56121,56147,56173,56199,\n    56225,56250,56276,56302,56328,56353,56379,56404,\n    56430,56456,56481,56507,56532,56557,56583,56608,\n    56633,56659,56684,56709,56734,56760,56785,56810,\n    56835,56860,56885,56910,56935,56959,56984,57009,\n    57034,57059,57083,57108,57133,57157,57182,57206,\n    57231,57255,57280,57304,57329,57353,57377,57402,\n    57426,57450,57474,57498,57522,57546,57570,57594,\n    57618,57642,57666,57690,57714,57738,57762,57785,\n    57809,57833,57856,57880,57903,57927,57950,57974,\n    57997,58021,58044,58067,58091,58114,58137,58160,\n    58183,58207,58230,58253,58276,58299,58322,58345,\n    58367,58390,58413,58436,58459,58481,58504,58527,\n    58549,58572,58594,58617,58639,58662,58684,58706,\n    58729,58751,58773,58795,58818,58840,58862,58884,\n    58906,58928,58950,58972,58994,59016,59038,59059,\n    59081,59103,59125,59146,59168,59190,59211,59233,\n    59254,59276,59297,59318,59340,59361,59382,59404,\n    59425,59446,59467,59488,59509,59530,59551,59572,\n    59593,59614,59635,59656,59677,59697,59718,59739,\n    59759,59780,59801,59821,59842,59862,59883,59903,\n    59923,59944,59964,59984,60004,60025,60045,60065,\n    60085,60105,60125,60145,60165,60185,60205,60225,\n    60244,60264,60284,60304,60323,60343,60363,60382,\n    60402,60421,60441,60460,60479,60499,60518,60537,\n    60556,60576,60595,60614,60633,60652,60671,60690,\n    60709,60728,60747,60766,60785,60803,60822,60841,\n    60859,60878,60897,60915,60934,60952,60971,60989,\n    61007,61026,61044,61062,61081,61099,61117,61135,\n    61153,61171,61189,61207,61225,61243,61261,61279,\n    61297,61314,61332,61350,61367,61385,61403,61420,\n    61438,61455,61473,61490,61507,61525,61542,61559,\n    61577,61594,61611,61628,61645,61662,61679,61696,\n    61713,61730,61747,61764,61780,61797,61814,61831,\n    61847,61864,61880,61897,61913,61930,61946,61963,\n    61979,61995,62012,62028,62044,62060,62076,62092,\n    62108,62125,62141,62156,62172,62188,62204,62220,\n    62236,62251,62267,62283,62298,62314,62329,62345,\n    62360,62376,62391,62407,62422,62437,62453,62468,\n    62483,62498,62513,62528,62543,62558,62573,62588,\n    62603,62618,62633,62648,62662,62677,62692,62706,\n    62721,62735,62750,62764,62779,62793,62808,62822,\n    62836,62850,62865,62879,62893,62907,62921,62935,\n    62949,62963,62977,62991,63005,63019,63032,63046,\n    63060,63074,63087,63101,63114,63128,63141,63155,\n    63168,63182,63195,63208,63221,63235,63248,63261,\n    63274,63287,63300,63313,63326,63339,63352,63365,\n    63378,63390,63403,63416,63429,63441,63454,63466,\n    63479,63491,63504,63516,63528,63541,63553,63565,\n    63578,63590,63602,63614,63626,63638,63650,63662,\n    63674,63686,63698,63709,63721,63733,63745,63756,\n    63768,63779,63791,63803,63814,63825,63837,63848,\n    63859,63871,63882,63893,63904,63915,63927,63938,\n    63949,63960,63971,63981,63992,64003,64014,64025,\n    64035,64046,64057,64067,64078,64088,64099,64109,\n    64120,64130,64140,64151,64161,64171,64181,64192,\n    64202,64212,64222,64232,64242,64252,64261,64271,\n    64281,64291,64301,64310,64320,64330,64339,64349,\n    64358,64368,64377,64387,64396,64405,64414,64424,\n    64433,64442,64451,64460,64469,64478,64487,64496,\n    64505,64514,64523,64532,64540,64549,64558,64566,\n    64575,64584,64592,64601,64609,64617,64626,64634,\n    64642,64651,64659,64667,64675,64683,64691,64699,\n    64707,64715,64723,64731,64739,64747,64754,64762,\n    64770,64777,64785,64793,64800,64808,64815,64822,\n    64830,64837,64844,64852,64859,64866,64873,64880,\n    64887,64895,64902,64908,64915,64922,64929,64936,\n    64943,64949,64956,64963,64969,64976,64982,64989,\n    64995,65002,65008,65015,65021,65027,65033,65040,\n    65046,65052,65058,65064,65070,65076,65082,65088,\n    65094,65099,65105,65111,65117,65122,65128,65133,\n    65139,65144,65150,65155,65161,65166,65171,65177,\n    65182,65187,65192,65197,65202,65207,65212,65217,\n    65222,65227,65232,65237,65242,65246,65251,65256,\n    65260,65265,65270,65274,65279,65283,65287,65292,\n    65296,65300,65305,65309,65313,65317,65321,65325,\n    65329,65333,65337,65341,65345,65349,65352,65356,\n    65360,65363,65367,65371,65374,65378,65381,65385,\n    65388,65391,65395,65398,65401,65404,65408,65411,\n    65414,65417,65420,65423,65426,65429,65431,65434,\n    65437,65440,65442,65445,65448,65450,65453,65455,\n    65458,65460,65463,65465,65467,65470,65472,65474,\n    65476,65478,65480,65482,65484,65486,65488,65490,\n    65492,65494,65496,65497,65499,65501,65502,65504,\n    65505,65507,65508,65510,65511,65513,65514,65515,\n    65516,65518,65519,65520,65521,65522,65523,65524,\n    65525,65526,65527,65527,65528,65529,65530,65530,\n    65531,65531,65532,65532,65533,65533,65534,65534,\n    65534,65535,65535,65535,65535,65535,65535,65535,\n    65535,65535,65535,65535,65535,65535,65535,65534,\n    65534,65534,65533,65533,65532,65532,65531,65531,\n    65530,65530,65529,65528,65527,65527,65526,65525,\n    65524,65523,65522,65521,65520,65519,65518,65516,\n    65515,65514,65513,65511,65510,65508,65507,65505,\n    65504,65502,65501,65499,65497,65496,65494,65492,\n    65490,65488,65486,65484,65482,65480,65478,65476,\n    65474,65472,65470,65467,65465,65463,65460,65458,\n    65455,65453,65450,65448,65445,65442,65440,65437,\n    65434,65431,65429,65426,65423,65420,65417,65414,\n    65411,65408,65404,65401,65398,65395,65391,65388,\n    65385,65381,65378,65374,65371,65367,65363,65360,\n    65356,65352,65349,65345,65341,65337,65333,65329,\n    65325,65321,65317,65313,65309,65305,65300,65296,\n    65292,65287,65283,65279,65274,65270,65265,65260,\n    65256,65251,65246,65242,65237,65232,65227,65222,\n    65217,65212,65207,65202,65197,65192,65187,65182,\n    65177,65171,65166,65161,65155,65150,65144,65139,\n    65133,65128,65122,65117,65111,65105,65099,65094,\n    65088,65082,65076,65070,65064,65058,65052,65046,\n    65040,65033,65027,65021,65015,65008,65002,64995,\n    64989,64982,64976,64969,64963,64956,64949,64943,\n    64936,64929,64922,64915,64908,64902,64895,64887,\n    64880,64873,64866,64859,64852,64844,64837,64830,\n    64822,64815,64808,64800,64793,64785,64777,64770,\n    64762,64754,64747,64739,64731,64723,64715,64707,\n    64699,64691,64683,64675,64667,64659,64651,64642,\n    64634,64626,64617,64609,64600,64592,64584,64575,\n    64566,64558,64549,64540,64532,64523,64514,64505,\n    64496,64487,64478,64469,64460,64451,64442,64433,\n    64424,64414,64405,64396,64387,64377,64368,64358,\n    64349,64339,64330,64320,64310,64301,64291,64281,\n    64271,64261,64252,64242,64232,64222,64212,64202,\n    64192,64181,64171,64161,64151,64140,64130,64120,\n    64109,64099,64088,64078,64067,64057,64046,64035,\n    64025,64014,64003,63992,63981,63971,63960,63949,\n    63938,63927,63915,63904,63893,63882,63871,63859,\n    63848,63837,63825,63814,63803,63791,63779,63768,\n    63756,63745,63733,63721,63709,63698,63686,63674,\n    63662,63650,63638,63626,63614,63602,63590,63578,\n    63565,63553,63541,63528,63516,63504,63491,63479,\n    63466,63454,63441,63429,63416,63403,63390,63378,\n    63365,63352,63339,63326,63313,63300,63287,63274,\n    63261,63248,63235,63221,63208,63195,63182,63168,\n    63155,63141,63128,63114,63101,63087,63074,63060,\n    63046,63032,63019,63005,62991,62977,62963,62949,\n    62935,62921,62907,62893,62879,62865,62850,62836,\n    62822,62808,62793,62779,62764,62750,62735,62721,\n    62706,62692,62677,62662,62648,62633,62618,62603,\n    62588,62573,62558,62543,62528,62513,62498,62483,\n    62468,62453,62437,62422,62407,62391,62376,62360,\n    62345,62329,62314,62298,62283,62267,62251,62236,\n    62220,62204,62188,62172,62156,62141,62125,62108,\n    62092,62076,62060,62044,62028,62012,61995,61979,\n    61963,61946,61930,61913,61897,61880,61864,61847,\n    61831,61814,61797,61780,61764,61747,61730,61713,\n    61696,61679,61662,61645,61628,61611,61594,61577,\n    61559,61542,61525,61507,61490,61473,61455,61438,\n    61420,61403,61385,61367,61350,61332,61314,61297,\n    61279,61261,61243,61225,61207,61189,61171,61153,\n    61135,61117,61099,61081,61062,61044,61026,61007,\n    60989,60971,60952,60934,60915,60897,60878,60859,\n    60841,60822,60803,60785,60766,60747,60728,60709,\n    60690,60671,60652,60633,60614,60595,60576,60556,\n    60537,60518,60499,60479,60460,60441,60421,60402,\n    60382,60363,60343,60323,60304,60284,60264,60244,\n    60225,60205,60185,60165,60145,60125,60105,60085,\n    60065,60045,60025,60004,59984,59964,59944,59923,\n    59903,59883,59862,59842,59821,59801,59780,59759,\n    59739,59718,59697,59677,59656,59635,59614,59593,\n    59572,59551,59530,59509,59488,59467,59446,59425,\n    59404,59382,59361,59340,59318,59297,59276,59254,\n    59233,59211,59190,59168,59146,59125,59103,59081,\n    59059,59038,59016,58994,58972,58950,58928,58906,\n    58884,58862,58840,58818,58795,58773,58751,58729,\n    58706,58684,58662,58639,58617,58594,58572,58549,\n    58527,58504,58481,58459,58436,58413,58390,58367,\n    58345,58322,58299,58276,58253,58230,58207,58183,\n    58160,58137,58114,58091,58067,58044,58021,57997,\n    57974,57950,57927,57903,57880,57856,57833,57809,\n    57785,57762,57738,57714,57690,57666,57642,57618,\n    57594,57570,57546,57522,57498,57474,57450,57426,\n    57402,57377,57353,57329,57304,57280,57255,57231,\n    57206,57182,57157,57133,57108,57083,57059,57034,\n    57009,56984,56959,56935,56910,56885,56860,56835,\n    56810,56785,56760,56734,56709,56684,56659,56633,\n    56608,56583,56557,56532,56507,56481,56456,56430,\n    56404,56379,56353,56328,56302,56276,56250,56225,\n    56199,56173,56147,56121,56095,56069,56043,56017,\n    55991,55965,55938,55912,55886,55860,55833,55807,\n    55781,55754,55728,55701,55675,55648,55622,55595,\n    55569,55542,55515,55489,55462,55435,55408,55381,\n    55354,55327,55300,55274,55246,55219,55192,55165,\n    55138,55111,55084,55056,55029,55002,54974,54947,\n    54920,54892,54865,54837,54810,54782,54755,54727,\n    54699,54672,54644,54616,54588,54560,54533,54505,\n    54477,54449,54421,54393,54365,54337,54308,54280,\n    54252,54224,54196,54167,54139,54111,54082,54054,\n    54026,53997,53969,53940,53911,53883,53854,53826,\n    53797,53768,53739,53711,53682,53653,53624,53595,\n    53566,53537,53508,53479,53450,53421,53392,53363,\n    53334,53304,53275,53246,53216,53187,53158,53128,\n    53099,53069,53040,53010,52981,52951,52922,52892,\n    52862,52832,52803,52773,52743,52713,52683,52653,\n    52624,52594,52564,52534,52503,52473,52443,52413,\n    52383,52353,52322,52292,52262,52231,52201,52171,\n    52140,52110,52079,52049,52018,51988,51957,51926,\n    51896,51865,51834,51803,51773,51742,51711,51680,\n    51649,51618,51587,51556,51525,51494,51463,51432,\n    51401,51369,51338,51307,51276,51244,51213,51182,\n    51150,51119,51087,51056,51024,50993,50961,50929,\n    50898,50866,50834,50803,50771,50739,50707,50675,\n    50644,50612,50580,50548,50516,50484,50452,50420,\n    50387,50355,50323,50291,50259,50226,50194,50162,\n    50129,50097,50065,50032,50000,49967,49935,49902,\n    49869,49837,49804,49771,49739,49706,49673,49640,\n    49608,49575,49542,49509,49476,49443,49410,49377,\n    49344,49311,49278,49244,49211,49178,49145,49112,\n    49078,49045,49012,48978,48945,48911,48878,48844,\n    48811,48777,48744,48710,48676,48643,48609,48575,\n    48542,48508,48474,48440,48406,48372,48338,48304,\n    48271,48237,48202,48168,48134,48100,48066,48032,\n    47998,47963,47929,47895,47860,47826,47792,47757,\n    47723,47688,47654,47619,47585,47550,47516,47481,\n    47446,47412,47377,47342,47308,47273,47238,47203,\n    47168,47133,47098,47063,47028,46993,46958,46923,\n    46888,46853,46818,46783,46747,46712,46677,46642,\n    46606,46571,46536,46500,46465,46429,46394,46358,\n    46323,46287,46252,46216,46180,46145,46109,46073,\n    46037,46002,45966,45930,45894,45858,45822,45786,\n    45750,45714,45678,45642,45606,45570,45534,45498,\n    45462,45425,45389,45353,45316,45280,45244,45207,\n    45171,45135,45098,45062,45025,44989,44952,44915,\n    44879,44842,44806,44769,44732,44695,44659,44622,\n    44585,44548,44511,44474,44437,44400,44363,44326,\n    44289,44252,44215,44178,44141,44104,44067,44029,\n    43992,43955,43918,43880,43843,43806,43768,43731,\n    43693,43656,43618,43581,43543,43506,43468,43430,\n    43393,43355,43317,43280,43242,43204,43166,43128,\n    43091,43053,43015,42977,42939,42901,42863,42825,\n    42787,42749,42711,42672,42634,42596,42558,42520,\n    42481,42443,42405,42366,42328,42290,42251,42213,\n    42174,42136,42097,42059,42020,41982,41943,41904,\n    41866,41827,41788,41750,41711,41672,41633,41595,\n    41556,41517,41478,41439,41400,41361,41322,41283,\n    41244,41205,41166,41127,41088,41048,41009,40970,\n    40931,40891,40852,40813,40773,40734,40695,40655,\n    40616,40576,40537,40497,40458,40418,40379,40339,\n    40300,40260,40220,40180,40141,40101,40061,40021,\n    39982,39942,39902,39862,39822,39782,39742,39702,\n    39662,39622,39582,39542,39502,39462,39422,39382,\n    39341,39301,39261,39221,39180,39140,39100,39059,\n    39019,38979,38938,38898,38857,38817,38776,38736,\n    38695,38655,38614,38573,38533,38492,38451,38411,\n    38370,38329,38288,38248,38207,38166,38125,38084,\n    38043,38002,37961,37920,37879,37838,37797,37756,\n    37715,37674,37633,37592,37551,37509,37468,37427,\n    37386,37344,37303,37262,37220,37179,37137,37096,\n    37055,37013,36972,36930,36889,36847,36805,36764,\n    36722,36681,36639,36597,36556,36514,36472,36430,\n    36388,36347,36305,36263,36221,36179,36137,36095,\n    36053,36011,35969,35927,35885,35843,35801,35759,\n    35717,35675,35633,35590,35548,35506,35464,35421,\n    35379,35337,35294,35252,35210,35167,35125,35082,\n    35040,34997,34955,34912,34870,34827,34785,34742,\n    34699,34657,34614,34571,34529,34486,34443,34400,\n    34358,34315,34272,34229,34186,34143,34100,34057,\n    34015,33972,33929,33886,33843,33799,33756,33713,\n    33670,33627,33584,33541,33498,33454,33411,33368,\n    33325,33281,33238,33195,33151,33108,33065,33021,\n    32978,32934,32891,32847,32804,32760,32717,32673,\n    32630,32586,32542,32499,32455,32411,32368,32324,\n    32280,32236,32193,32149,32105,32061,32017,31974,\n    31930,31886,31842,31798,31754,31710,31666,31622,\n    31578,31534,31490,31446,31402,31357,31313,31269,\n    31225,31181,31136,31092,31048,31004,30959,30915,\n    30871,30826,30782,30738,30693,30649,30604,30560,\n    30515,30471,30426,30382,30337,30293,30248,30204,\n    30159,30114,30070,30025,29980,29936,29891,29846,\n    29801,29757,29712,29667,29622,29577,29533,29488,\n    29443,29398,29353,29308,29263,29218,29173,29128,\n    29083,29038,28993,28948,28903,28858,28812,28767,\n    28722,28677,28632,28586,28541,28496,28451,28405,\n    28360,28315,28269,28224,28179,28133,28088,28042,\n    27997,27952,27906,27861,27815,27770,27724,27678,\n    27633,27587,27542,27496,27450,27405,27359,27313,\n    27268,27222,27176,27131,27085,27039,26993,26947,\n    26902,26856,26810,26764,26718,26672,26626,26580,\n    26534,26488,26442,26396,26350,26304,26258,26212,\n    26166,26120,26074,26028,25982,25936,25889,25843,\n    25797,25751,25705,25658,25612,25566,25520,25473,\n    25427,25381,25334,25288,25241,25195,25149,25102,\n    25056,25009,24963,24916,24870,24823,24777,24730,\n    24684,24637,24591,24544,24497,24451,24404,24357,\n    24311,24264,24217,24171,24124,24077,24030,23984,\n    23937,23890,23843,23796,23750,23703,23656,23609,\n    23562,23515,23468,23421,23374,23327,23280,23233,\n    23186,23139,23092,23045,22998,22951,22904,22857,\n    22810,22763,22716,22668,22621,22574,22527,22480,\n    22433,22385,22338,22291,22243,22196,22149,22102,\n    22054,22007,21960,21912,21865,21817,21770,21723,\n    21675,21628,21580,21533,21485,21438,21390,21343,\n    21295,21248,21200,21153,21105,21057,21010,20962,\n    20915,20867,20819,20772,20724,20676,20629,20581,\n    20533,20485,20438,20390,20342,20294,20246,20199,\n    20151,20103,20055,20007,19959,19912,19864,19816,\n    19768,19720,19672,19624,19576,19528,19480,19432,\n    19384,19336,19288,19240,19192,19144,19096,19048,\n    19000,18951,18903,18855,18807,18759,18711,18663,\n    18614,18566,18518,18470,18421,18373,18325,18277,\n    18228,18180,18132,18084,18035,17987,17939,17890,\n    17842,17793,17745,17697,17648,17600,17551,17503,\n    17455,17406,17358,17309,17261,17212,17164,17115,\n    17067,17018,16970,16921,16872,16824,16775,16727,\n    16678,16629,16581,16532,16484,16435,16386,16338,\n    16289,16240,16191,16143,16094,16045,15997,15948,\n    15899,15850,15802,15753,15704,15655,15606,15557,\n    15509,15460,15411,15362,15313,15264,15215,15167,\n    15118,15069,15020,14971,14922,14873,14824,14775,\n    14726,14677,14628,14579,14530,14481,14432,14383,\n    14334,14285,14236,14187,14138,14089,14040,13990,\n    13941,13892,13843,13794,13745,13696,13646,13597,\n    13548,13499,13450,13401,13351,13302,13253,13204,\n    13154,13105,13056,13007,12957,12908,12859,12810,\n    12760,12711,12662,12612,12563,12514,12464,12415,\n    12366,12316,12267,12218,12168,12119,12069,12020,\n    11970,11921,11872,11822,11773,11723,11674,11624,\n    11575,11525,11476,11426,11377,11327,11278,11228,\n    11179,11129,11080,11030,10981,10931,10882,10832,\n    10782,10733,10683,10634,10584,10534,10485,10435,\n    10386,10336,10286,10237,10187,10137,10088,10038,\n    9988,9939,9889,9839,9790,9740,9690,9640,\n    9591,9541,9491,9442,9392,9342,9292,9243,\n    9193,9143,9093,9043,8994,8944,8894,8844,\n    8794,8745,8695,8645,8595,8545,8496,8446,\n    8396,8346,8296,8246,8196,8147,8097,8047,\n    7997,7947,7897,7847,7797,7747,7697,7648,\n    7598,7548,7498,7448,7398,7348,7298,7248,\n    7198,7148,7098,7048,6998,6948,6898,6848,\n    6798,6748,6698,6648,6598,6548,6498,6448,\n    6398,6348,6298,6248,6198,6148,6098,6048,\n    5998,5948,5898,5848,5798,5748,5697,5647,\n    5597,5547,5497,5447,5397,5347,5297,5247,\n    5197,5146,5096,5046,4996,4946,4896,4846,\n    4796,4745,4695,4645,4595,4545,4495,4445,\n    4394,4344,4294,4244,4194,4144,4093,4043,\n    3993,3943,3893,3843,3792,3742,3692,3642,\n    3592,3541,3491,3441,3391,3341,3291,3240,\n    3190,3140,3090,3039,2989,2939,2889,2839,\n    2788,2738,2688,2638,2587,2537,2487,2437,\n    2387,2336,2286,2236,2186,2135,2085,2035,\n    1985,1934,1884,1834,1784,1733,1683,1633,\n    1583,1532,1482,1432,1382,1331,1281,1231,\n    1181,1130,1080,1030,980,929,879,829,\n    779,728,678,628,578,527,477,427,\n    376,326,276,226,175,125,75,25,\n    -25,-75,-125,-175,-226,-276,-326,-376,\n    -427,-477,-527,-578,-628,-678,-728,-779,\n    -829,-879,-929,-980,-1030,-1080,-1130,-1181,\n    -1231,-1281,-1331,-1382,-1432,-1482,-1532,-1583,\n    -1633,-1683,-1733,-1784,-1834,-1884,-1934,-1985,\n    -2035,-2085,-2135,-2186,-2236,-2286,-2336,-2387,\n    -2437,-2487,-2537,-2588,-2638,-2688,-2738,-2788,\n    -2839,-2889,-2939,-2989,-3039,-3090,-3140,-3190,\n    -3240,-3291,-3341,-3391,-3441,-3491,-3541,-3592,\n    -3642,-3692,-3742,-3792,-3843,-3893,-3943,-3993,\n    -4043,-4093,-4144,-4194,-4244,-4294,-4344,-4394,\n    -4445,-4495,-4545,-4595,-4645,-4695,-4745,-4796,\n    -4846,-4896,-4946,-4996,-5046,-5096,-5146,-5197,\n    -5247,-5297,-5347,-5397,-5447,-5497,-5547,-5597,\n    -5647,-5697,-5748,-5798,-5848,-5898,-5948,-5998,\n    -6048,-6098,-6148,-6198,-6248,-6298,-6348,-6398,\n    -6448,-6498,-6548,-6598,-6648,-6698,-6748,-6798,\n    -6848,-6898,-6948,-6998,-7048,-7098,-7148,-7198,\n    -7248,-7298,-7348,-7398,-7448,-7498,-7548,-7598,\n    -7648,-7697,-7747,-7797,-7847,-7897,-7947,-7997,\n    -8047,-8097,-8147,-8196,-8246,-8296,-8346,-8396,\n    -8446,-8496,-8545,-8595,-8645,-8695,-8745,-8794,\n    -8844,-8894,-8944,-8994,-9043,-9093,-9143,-9193,\n    -9243,-9292,-9342,-9392,-9442,-9491,-9541,-9591,\n    -9640,-9690,-9740,-9790,-9839,-9889,-9939,-9988,\n    -10038,-10088,-10137,-10187,-10237,-10286,-10336,-10386,\n    -10435,-10485,-10534,-10584,-10634,-10683,-10733,-10782,\n    -10832,-10882,-10931,-10981,-11030,-11080,-11129,-11179,\n    -11228,-11278,-11327,-11377,-11426,-11476,-11525,-11575,\n    -11624,-11674,-11723,-11773,-11822,-11872,-11921,-11970,\n    -12020,-12069,-12119,-12168,-12218,-12267,-12316,-12366,\n    -12415,-12464,-12514,-12563,-12612,-12662,-12711,-12760,\n    -12810,-12859,-12908,-12957,-13007,-13056,-13105,-13154,\n    -13204,-13253,-13302,-13351,-13401,-13450,-13499,-13548,\n    -13597,-13647,-13696,-13745,-13794,-13843,-13892,-13941,\n    -13990,-14040,-14089,-14138,-14187,-14236,-14285,-14334,\n    -14383,-14432,-14481,-14530,-14579,-14628,-14677,-14726,\n    -14775,-14824,-14873,-14922,-14971,-15020,-15069,-15118,\n    -15167,-15215,-15264,-15313,-15362,-15411,-15460,-15509,\n    -15557,-15606,-15655,-15704,-15753,-15802,-15850,-15899,\n    -15948,-15997,-16045,-16094,-16143,-16191,-16240,-16289,\n    -16338,-16386,-16435,-16484,-16532,-16581,-16629,-16678,\n    -16727,-16775,-16824,-16872,-16921,-16970,-17018,-17067,\n    -17115,-17164,-17212,-17261,-17309,-17358,-17406,-17455,\n    -17503,-17551,-17600,-17648,-17697,-17745,-17793,-17842,\n    -17890,-17939,-17987,-18035,-18084,-18132,-18180,-18228,\n    -18277,-18325,-18373,-18421,-18470,-18518,-18566,-18614,\n    -18663,-18711,-18759,-18807,-18855,-18903,-18951,-19000,\n    -19048,-19096,-19144,-19192,-19240,-19288,-19336,-19384,\n    -19432,-19480,-19528,-19576,-19624,-19672,-19720,-19768,\n    -19816,-19864,-19912,-19959,-20007,-20055,-20103,-20151,\n    -20199,-20246,-20294,-20342,-20390,-20438,-20485,-20533,\n    -20581,-20629,-20676,-20724,-20772,-20819,-20867,-20915,\n    -20962,-21010,-21057,-21105,-21153,-21200,-21248,-21295,\n    -21343,-21390,-21438,-21485,-21533,-21580,-21628,-21675,\n    -21723,-21770,-21817,-21865,-21912,-21960,-22007,-22054,\n    -22102,-22149,-22196,-22243,-22291,-22338,-22385,-22433,\n    -22480,-22527,-22574,-22621,-22668,-22716,-22763,-22810,\n    -22857,-22904,-22951,-22998,-23045,-23092,-23139,-23186,\n    -23233,-23280,-23327,-23374,-23421,-23468,-23515,-23562,\n    -23609,-23656,-23703,-23750,-23796,-23843,-23890,-23937,\n    -23984,-24030,-24077,-24124,-24171,-24217,-24264,-24311,\n    -24357,-24404,-24451,-24497,-24544,-24591,-24637,-24684,\n    -24730,-24777,-24823,-24870,-24916,-24963,-25009,-25056,\n    -25102,-25149,-25195,-25241,-25288,-25334,-25381,-25427,\n    -25473,-25520,-25566,-25612,-25658,-25705,-25751,-25797,\n    -25843,-25889,-25936,-25982,-26028,-26074,-26120,-26166,\n    -26212,-26258,-26304,-26350,-26396,-26442,-26488,-26534,\n    -26580,-26626,-26672,-26718,-26764,-26810,-26856,-26902,\n    -26947,-26993,-27039,-27085,-27131,-27176,-27222,-27268,\n    -27313,-27359,-27405,-27450,-27496,-27542,-27587,-27633,\n    -27678,-27724,-27770,-27815,-27861,-27906,-27952,-27997,\n    -28042,-28088,-28133,-28179,-28224,-28269,-28315,-28360,\n    -28405,-28451,-28496,-28541,-28586,-28632,-28677,-28722,\n    -28767,-28812,-28858,-28903,-28948,-28993,-29038,-29083,\n    -29128,-29173,-29218,-29263,-29308,-29353,-29398,-29443,\n    -29488,-29533,-29577,-29622,-29667,-29712,-29757,-29801,\n    -29846,-29891,-29936,-29980,-30025,-30070,-30114,-30159,\n    -30204,-30248,-30293,-30337,-30382,-30426,-30471,-30515,\n    -30560,-30604,-30649,-30693,-30738,-30782,-30826,-30871,\n    -30915,-30959,-31004,-31048,-31092,-31136,-31181,-31225,\n    -31269,-31313,-31357,-31402,-31446,-31490,-31534,-31578,\n    -31622,-31666,-31710,-31754,-31798,-31842,-31886,-31930,\n    -31974,-32017,-32061,-32105,-32149,-32193,-32236,-32280,\n    -32324,-32368,-32411,-32455,-32499,-32542,-32586,-32630,\n    -32673,-32717,-32760,-32804,-32847,-32891,-32934,-32978,\n    -33021,-33065,-33108,-33151,-33195,-33238,-33281,-33325,\n    -33368,-33411,-33454,-33498,-33541,-33584,-33627,-33670,\n    -33713,-33756,-33799,-33843,-33886,-33929,-33972,-34015,\n    -34057,-34100,-34143,-34186,-34229,-34272,-34315,-34358,\n    -34400,-34443,-34486,-34529,-34571,-34614,-34657,-34699,\n    -34742,-34785,-34827,-34870,-34912,-34955,-34997,-35040,\n    -35082,-35125,-35167,-35210,-35252,-35294,-35337,-35379,\n    -35421,-35464,-35506,-35548,-35590,-35633,-35675,-35717,\n    -35759,-35801,-35843,-35885,-35927,-35969,-36011,-36053,\n    -36095,-36137,-36179,-36221,-36263,-36305,-36347,-36388,\n    -36430,-36472,-36514,-36555,-36597,-36639,-36681,-36722,\n    -36764,-36805,-36847,-36889,-36930,-36972,-37013,-37055,\n    -37096,-37137,-37179,-37220,-37262,-37303,-37344,-37386,\n    -37427,-37468,-37509,-37551,-37592,-37633,-37674,-37715,\n    -37756,-37797,-37838,-37879,-37920,-37961,-38002,-38043,\n    -38084,-38125,-38166,-38207,-38248,-38288,-38329,-38370,\n    -38411,-38451,-38492,-38533,-38573,-38614,-38655,-38695,\n    -38736,-38776,-38817,-38857,-38898,-38938,-38979,-39019,\n    -39059,-39100,-39140,-39180,-39221,-39261,-39301,-39341,\n    -39382,-39422,-39462,-39502,-39542,-39582,-39622,-39662,\n    -39702,-39742,-39782,-39822,-39862,-39902,-39942,-39982,\n    -40021,-40061,-40101,-40141,-40180,-40220,-40260,-40299,\n    -40339,-40379,-40418,-40458,-40497,-40537,-40576,-40616,\n    -40655,-40695,-40734,-40773,-40813,-40852,-40891,-40931,\n    -40970,-41009,-41048,-41087,-41127,-41166,-41205,-41244,\n    -41283,-41322,-41361,-41400,-41439,-41478,-41517,-41556,\n    -41595,-41633,-41672,-41711,-41750,-41788,-41827,-41866,\n    -41904,-41943,-41982,-42020,-42059,-42097,-42136,-42174,\n    -42213,-42251,-42290,-42328,-42366,-42405,-42443,-42481,\n    -42520,-42558,-42596,-42634,-42672,-42711,-42749,-42787,\n    -42825,-42863,-42901,-42939,-42977,-43015,-43053,-43091,\n    -43128,-43166,-43204,-43242,-43280,-43317,-43355,-43393,\n    -43430,-43468,-43506,-43543,-43581,-43618,-43656,-43693,\n    -43731,-43768,-43806,-43843,-43880,-43918,-43955,-43992,\n    -44029,-44067,-44104,-44141,-44178,-44215,-44252,-44289,\n    -44326,-44363,-44400,-44437,-44474,-44511,-44548,-44585,\n    -44622,-44659,-44695,-44732,-44769,-44806,-44842,-44879,\n    -44915,-44952,-44989,-45025,-45062,-45098,-45135,-45171,\n    -45207,-45244,-45280,-45316,-45353,-45389,-45425,-45462,\n    -45498,-45534,-45570,-45606,-45642,-45678,-45714,-45750,\n    -45786,-45822,-45858,-45894,-45930,-45966,-46002,-46037,\n    -46073,-46109,-46145,-46180,-46216,-46252,-46287,-46323,\n    -46358,-46394,-46429,-46465,-46500,-46536,-46571,-46606,\n    -46642,-46677,-46712,-46747,-46783,-46818,-46853,-46888,\n    -46923,-46958,-46993,-47028,-47063,-47098,-47133,-47168,\n    -47203,-47238,-47273,-47308,-47342,-47377,-47412,-47446,\n    -47481,-47516,-47550,-47585,-47619,-47654,-47688,-47723,\n    -47757,-47792,-47826,-47860,-47895,-47929,-47963,-47998,\n    -48032,-48066,-48100,-48134,-48168,-48202,-48236,-48271,\n    -48304,-48338,-48372,-48406,-48440,-48474,-48508,-48542,\n    -48575,-48609,-48643,-48676,-48710,-48744,-48777,-48811,\n    -48844,-48878,-48911,-48945,-48978,-49012,-49045,-49078,\n    -49112,-49145,-49178,-49211,-49244,-49278,-49311,-49344,\n    -49377,-49410,-49443,-49476,-49509,-49542,-49575,-49608,\n    -49640,-49673,-49706,-49739,-49771,-49804,-49837,-49869,\n    -49902,-49935,-49967,-50000,-50032,-50065,-50097,-50129,\n    -50162,-50194,-50226,-50259,-50291,-50323,-50355,-50387,\n    -50420,-50452,-50484,-50516,-50548,-50580,-50612,-50644,\n    -50675,-50707,-50739,-50771,-50803,-50834,-50866,-50898,\n    -50929,-50961,-50993,-51024,-51056,-51087,-51119,-51150,\n    -51182,-51213,-51244,-51276,-51307,-51338,-51369,-51401,\n    -51432,-51463,-51494,-51525,-51556,-51587,-51618,-51649,\n    -51680,-51711,-51742,-51773,-51803,-51834,-51865,-51896,\n    -51926,-51957,-51988,-52018,-52049,-52079,-52110,-52140,\n    -52171,-52201,-52231,-52262,-52292,-52322,-52353,-52383,\n    -52413,-52443,-52473,-52503,-52534,-52564,-52594,-52624,\n    -52653,-52683,-52713,-52743,-52773,-52803,-52832,-52862,\n    -52892,-52922,-52951,-52981,-53010,-53040,-53069,-53099,\n    -53128,-53158,-53187,-53216,-53246,-53275,-53304,-53334,\n    -53363,-53392,-53421,-53450,-53479,-53508,-53537,-53566,\n    -53595,-53624,-53653,-53682,-53711,-53739,-53768,-53797,\n    -53826,-53854,-53883,-53911,-53940,-53969,-53997,-54026,\n    -54054,-54082,-54111,-54139,-54167,-54196,-54224,-54252,\n    -54280,-54308,-54337,-54365,-54393,-54421,-54449,-54477,\n    -54505,-54533,-54560,-54588,-54616,-54644,-54672,-54699,\n    -54727,-54755,-54782,-54810,-54837,-54865,-54892,-54920,\n    -54947,-54974,-55002,-55029,-55056,-55084,-55111,-55138,\n    -55165,-55192,-55219,-55246,-55274,-55300,-55327,-55354,\n    -55381,-55408,-55435,-55462,-55489,-55515,-55542,-55569,\n    -55595,-55622,-55648,-55675,-55701,-55728,-55754,-55781,\n    -55807,-55833,-55860,-55886,-55912,-55938,-55965,-55991,\n    -56017,-56043,-56069,-56095,-56121,-56147,-56173,-56199,\n    -56225,-56250,-56276,-56302,-56328,-56353,-56379,-56404,\n    -56430,-56456,-56481,-56507,-56532,-56557,-56583,-56608,\n    -56633,-56659,-56684,-56709,-56734,-56760,-56785,-56810,\n    -56835,-56860,-56885,-56910,-56935,-56959,-56984,-57009,\n    -57034,-57059,-57083,-57108,-57133,-57157,-57182,-57206,\n    -57231,-57255,-57280,-57304,-57329,-57353,-57377,-57402,\n    -57426,-57450,-57474,-57498,-57522,-57546,-57570,-57594,\n    -57618,-57642,-57666,-57690,-57714,-57738,-57762,-57785,\n    -57809,-57833,-57856,-57880,-57903,-57927,-57950,-57974,\n    -57997,-58021,-58044,-58067,-58091,-58114,-58137,-58160,\n    -58183,-58207,-58230,-58253,-58276,-58299,-58322,-58345,\n    -58367,-58390,-58413,-58436,-58459,-58481,-58504,-58527,\n    -58549,-58572,-58594,-58617,-58639,-58662,-58684,-58706,\n    -58729,-58751,-58773,-58795,-58818,-58840,-58862,-58884,\n    -58906,-58928,-58950,-58972,-58994,-59016,-59038,-59059,\n    -59081,-59103,-59125,-59146,-59168,-59190,-59211,-59233,\n    -59254,-59276,-59297,-59318,-59340,-59361,-59382,-59404,\n    -59425,-59446,-59467,-59488,-59509,-59530,-59551,-59572,\n    -59593,-59614,-59635,-59656,-59677,-59697,-59718,-59739,\n    -59759,-59780,-59801,-59821,-59842,-59862,-59883,-59903,\n    -59923,-59944,-59964,-59984,-60004,-60025,-60045,-60065,\n    -60085,-60105,-60125,-60145,-60165,-60185,-60205,-60225,\n    -60244,-60264,-60284,-60304,-60323,-60343,-60363,-60382,\n    -60402,-60421,-60441,-60460,-60479,-60499,-60518,-60537,\n    -60556,-60576,-60595,-60614,-60633,-60652,-60671,-60690,\n    -60709,-60728,-60747,-60766,-60785,-60803,-60822,-60841,\n    -60859,-60878,-60897,-60915,-60934,-60952,-60971,-60989,\n    -61007,-61026,-61044,-61062,-61081,-61099,-61117,-61135,\n    -61153,-61171,-61189,-61207,-61225,-61243,-61261,-61279,\n    -61297,-61314,-61332,-61350,-61367,-61385,-61403,-61420,\n    -61438,-61455,-61473,-61490,-61507,-61525,-61542,-61559,\n    -61577,-61594,-61611,-61628,-61645,-61662,-61679,-61696,\n    -61713,-61730,-61747,-61764,-61780,-61797,-61814,-61831,\n    -61847,-61864,-61880,-61897,-61913,-61930,-61946,-61963,\n    -61979,-61995,-62012,-62028,-62044,-62060,-62076,-62092,\n    -62108,-62125,-62141,-62156,-62172,-62188,-62204,-62220,\n    -62236,-62251,-62267,-62283,-62298,-62314,-62329,-62345,\n    -62360,-62376,-62391,-62407,-62422,-62437,-62453,-62468,\n    -62483,-62498,-62513,-62528,-62543,-62558,-62573,-62588,\n    -62603,-62618,-62633,-62648,-62662,-62677,-62692,-62706,\n    -62721,-62735,-62750,-62764,-62779,-62793,-62808,-62822,\n    -62836,-62850,-62865,-62879,-62893,-62907,-62921,-62935,\n    -62949,-62963,-62977,-62991,-63005,-63019,-63032,-63046,\n    -63060,-63074,-63087,-63101,-63114,-63128,-63141,-63155,\n    -63168,-63182,-63195,-63208,-63221,-63235,-63248,-63261,\n    -63274,-63287,-63300,-63313,-63326,-63339,-63352,-63365,\n    -63378,-63390,-63403,-63416,-63429,-63441,-63454,-63466,\n    -63479,-63491,-63504,-63516,-63528,-63541,-63553,-63565,\n    -63578,-63590,-63602,-63614,-63626,-63638,-63650,-63662,\n    -63674,-63686,-63698,-63709,-63721,-63733,-63745,-63756,\n    -63768,-63779,-63791,-63803,-63814,-63825,-63837,-63848,\n    -63859,-63871,-63882,-63893,-63904,-63915,-63927,-63938,\n    -63949,-63960,-63971,-63981,-63992,-64003,-64014,-64025,\n    -64035,-64046,-64057,-64067,-64078,-64088,-64099,-64109,\n    -64120,-64130,-64140,-64151,-64161,-64171,-64181,-64192,\n    -64202,-64212,-64222,-64232,-64242,-64252,-64261,-64271,\n    -64281,-64291,-64301,-64310,-64320,-64330,-64339,-64349,\n    -64358,-64368,-64377,-64387,-64396,-64405,-64414,-64424,\n    -64433,-64442,-64451,-64460,-64469,-64478,-64487,-64496,\n    -64505,-64514,-64523,-64532,-64540,-64549,-64558,-64566,\n    -64575,-64584,-64592,-64601,-64609,-64617,-64626,-64634,\n    -64642,-64651,-64659,-64667,-64675,-64683,-64691,-64699,\n    -64707,-64715,-64723,-64731,-64739,-64747,-64754,-64762,\n    -64770,-64777,-64785,-64793,-64800,-64808,-64815,-64822,\n    -64830,-64837,-64844,-64852,-64859,-64866,-64873,-64880,\n    -64887,-64895,-64902,-64908,-64915,-64922,-64929,-64936,\n    -64943,-64949,-64956,-64963,-64969,-64976,-64982,-64989,\n    -64995,-65002,-65008,-65015,-65021,-65027,-65033,-65040,\n    -65046,-65052,-65058,-65064,-65070,-65076,-65082,-65088,\n    -65094,-65099,-65105,-65111,-65117,-65122,-65128,-65133,\n    -65139,-65144,-65150,-65155,-65161,-65166,-65171,-65177,\n    -65182,-65187,-65192,-65197,-65202,-65207,-65212,-65217,\n    -65222,-65227,-65232,-65237,-65242,-65246,-65251,-65256,\n    -65260,-65265,-65270,-65274,-65279,-65283,-65287,-65292,\n    -65296,-65300,-65305,-65309,-65313,-65317,-65321,-65325,\n    -65329,-65333,-65337,-65341,-65345,-65349,-65352,-65356,\n    -65360,-65363,-65367,-65371,-65374,-65378,-65381,-65385,\n    -65388,-65391,-65395,-65398,-65401,-65404,-65408,-65411,\n    -65414,-65417,-65420,-65423,-65426,-65429,-65431,-65434,\n    -65437,-65440,-65442,-65445,-65448,-65450,-65453,-65455,\n    -65458,-65460,-65463,-65465,-65467,-65470,-65472,-65474,\n    -65476,-65478,-65480,-65482,-65484,-65486,-65488,-65490,\n    -65492,-65494,-65496,-65497,-65499,-65501,-65502,-65504,\n    -65505,-65507,-65508,-65510,-65511,-65513,-65514,-65515,\n    -65516,-65518,-65519,-65520,-65521,-65522,-65523,-65524,\n    -65525,-65526,-65527,-65527,-65528,-65529,-65530,-65530,\n    -65531,-65531,-65532,-65532,-65533,-65533,-65534,-65534,\n    -65534,-65535,-65535,-65535,-65535,-65535,-65535,-65535,\n    -65535,-65535,-65535,-65535,-65535,-65535,-65535,-65534,\n    -65534,-65534,-65533,-65533,-65532,-65532,-65531,-65531,\n    -65530,-65530,-65529,-65528,-65527,-65527,-65526,-65525,\n    -65524,-65523,-65522,-65521,-65520,-65519,-65518,-65516,\n    -65515,-65514,-65513,-65511,-65510,-65508,-65507,-65505,\n    -65504,-65502,-65501,-65499,-65497,-65496,-65494,-65492,\n    -65490,-65488,-65486,-65484,-65482,-65480,-65478,-65476,\n    -65474,-65472,-65470,-65467,-65465,-65463,-65460,-65458,\n    -65455,-65453,-65450,-65448,-65445,-65442,-65440,-65437,\n    -65434,-65431,-65429,-65426,-65423,-65420,-65417,-65414,\n    -65411,-65408,-65404,-65401,-65398,-65395,-65391,-65388,\n    -65385,-65381,-65378,-65374,-65371,-65367,-65363,-65360,\n    -65356,-65352,-65349,-65345,-65341,-65337,-65333,-65329,\n    -65325,-65321,-65317,-65313,-65309,-65305,-65300,-65296,\n    -65292,-65287,-65283,-65279,-65274,-65270,-65265,-65260,\n    -65256,-65251,-65246,-65242,-65237,-65232,-65227,-65222,\n    -65217,-65212,-65207,-65202,-65197,-65192,-65187,-65182,\n    -65177,-65171,-65166,-65161,-65155,-65150,-65144,-65139,\n    -65133,-65128,-65122,-65117,-65111,-65105,-65099,-65094,\n    -65088,-65082,-65076,-65070,-65064,-65058,-65052,-65046,\n    -65040,-65033,-65027,-65021,-65015,-65008,-65002,-64995,\n    -64989,-64982,-64976,-64969,-64963,-64956,-64949,-64943,\n    -64936,-64929,-64922,-64915,-64908,-64902,-64895,-64887,\n    -64880,-64873,-64866,-64859,-64852,-64844,-64837,-64830,\n    -64822,-64815,-64808,-64800,-64793,-64785,-64777,-64770,\n    -64762,-64754,-64747,-64739,-64731,-64723,-64715,-64707,\n    -64699,-64691,-64683,-64675,-64667,-64659,-64651,-64642,\n    -64634,-64626,-64617,-64609,-64601,-64592,-64584,-64575,\n    -64566,-64558,-64549,-64540,-64532,-64523,-64514,-64505,\n    -64496,-64487,-64478,-64469,-64460,-64451,-64442,-64433,\n    -64424,-64414,-64405,-64396,-64387,-64377,-64368,-64358,\n    -64349,-64339,-64330,-64320,-64310,-64301,-64291,-64281,\n    -64271,-64261,-64252,-64242,-64232,-64222,-64212,-64202,\n    -64192,-64181,-64171,-64161,-64151,-64140,-64130,-64120,\n    -64109,-64099,-64088,-64078,-64067,-64057,-64046,-64035,\n    -64025,-64014,-64003,-63992,-63981,-63971,-63960,-63949,\n    -63938,-63927,-63915,-63904,-63893,-63882,-63871,-63859,\n    -63848,-63837,-63825,-63814,-63803,-63791,-63779,-63768,\n    -63756,-63745,-63733,-63721,-63709,-63698,-63686,-63674,\n    -63662,-63650,-63638,-63626,-63614,-63602,-63590,-63578,\n    -63565,-63553,-63541,-63528,-63516,-63504,-63491,-63479,\n    -63466,-63454,-63441,-63429,-63416,-63403,-63390,-63378,\n    -63365,-63352,-63339,-63326,-63313,-63300,-63287,-63274,\n    -63261,-63248,-63235,-63221,-63208,-63195,-63182,-63168,\n    -63155,-63141,-63128,-63114,-63101,-63087,-63074,-63060,\n    -63046,-63032,-63019,-63005,-62991,-62977,-62963,-62949,\n    -62935,-62921,-62907,-62893,-62879,-62865,-62850,-62836,\n    -62822,-62808,-62793,-62779,-62764,-62750,-62735,-62721,\n    -62706,-62692,-62677,-62662,-62648,-62633,-62618,-62603,\n    -62588,-62573,-62558,-62543,-62528,-62513,-62498,-62483,\n    -62468,-62453,-62437,-62422,-62407,-62391,-62376,-62360,\n    -62345,-62329,-62314,-62298,-62283,-62267,-62251,-62236,\n    -62220,-62204,-62188,-62172,-62156,-62141,-62125,-62108,\n    -62092,-62076,-62060,-62044,-62028,-62012,-61995,-61979,\n    -61963,-61946,-61930,-61913,-61897,-61880,-61864,-61847,\n    -61831,-61814,-61797,-61780,-61764,-61747,-61730,-61713,\n    -61696,-61679,-61662,-61645,-61628,-61611,-61594,-61577,\n    -61559,-61542,-61525,-61507,-61490,-61473,-61455,-61438,\n    -61420,-61403,-61385,-61367,-61350,-61332,-61314,-61297,\n    -61279,-61261,-61243,-61225,-61207,-61189,-61171,-61153,\n    -61135,-61117,-61099,-61081,-61062,-61044,-61026,-61007,\n    -60989,-60971,-60952,-60934,-60915,-60897,-60878,-60859,\n    -60841,-60822,-60803,-60785,-60766,-60747,-60728,-60709,\n    -60690,-60671,-60652,-60633,-60614,-60595,-60576,-60556,\n    -60537,-60518,-60499,-60479,-60460,-60441,-60421,-60402,\n    -60382,-60363,-60343,-60323,-60304,-60284,-60264,-60244,\n    -60225,-60205,-60185,-60165,-60145,-60125,-60105,-60085,\n    -60065,-60045,-60025,-60004,-59984,-59964,-59944,-59923,\n    -59903,-59883,-59862,-59842,-59821,-59801,-59780,-59759,\n    -59739,-59718,-59697,-59677,-59656,-59635,-59614,-59593,\n    -59572,-59551,-59530,-59509,-59488,-59467,-59446,-59425,\n    -59404,-59382,-59361,-59340,-59318,-59297,-59276,-59254,\n    -59233,-59211,-59189,-59168,-59146,-59125,-59103,-59081,\n    -59059,-59038,-59016,-58994,-58972,-58950,-58928,-58906,\n    -58884,-58862,-58840,-58818,-58795,-58773,-58751,-58729,\n    -58706,-58684,-58662,-58639,-58617,-58594,-58572,-58549,\n    -58527,-58504,-58481,-58459,-58436,-58413,-58390,-58367,\n    -58345,-58322,-58299,-58276,-58253,-58230,-58207,-58183,\n    -58160,-58137,-58114,-58091,-58067,-58044,-58021,-57997,\n    -57974,-57950,-57927,-57903,-57880,-57856,-57833,-57809,\n    -57785,-57762,-57738,-57714,-57690,-57666,-57642,-57618,\n    -57594,-57570,-57546,-57522,-57498,-57474,-57450,-57426,\n    -57402,-57377,-57353,-57329,-57304,-57280,-57255,-57231,\n    -57206,-57182,-57157,-57133,-57108,-57083,-57059,-57034,\n    -57009,-56984,-56959,-56935,-56910,-56885,-56860,-56835,\n    -56810,-56785,-56760,-56734,-56709,-56684,-56659,-56633,\n    -56608,-56583,-56557,-56532,-56507,-56481,-56456,-56430,\n    -56404,-56379,-56353,-56328,-56302,-56276,-56250,-56225,\n    -56199,-56173,-56147,-56121,-56095,-56069,-56043,-56017,\n    -55991,-55965,-55938,-55912,-55886,-55860,-55833,-55807,\n    -55781,-55754,-55728,-55701,-55675,-55648,-55622,-55595,\n    -55569,-55542,-55515,-55489,-55462,-55435,-55408,-55381,\n    -55354,-55327,-55300,-55274,-55246,-55219,-55192,-55165,\n    -55138,-55111,-55084,-55056,-55029,-55002,-54974,-54947,\n    -54920,-54892,-54865,-54837,-54810,-54782,-54755,-54727,\n    -54699,-54672,-54644,-54616,-54588,-54560,-54533,-54505,\n    -54477,-54449,-54421,-54393,-54365,-54337,-54308,-54280,\n    -54252,-54224,-54196,-54167,-54139,-54111,-54082,-54054,\n    -54026,-53997,-53969,-53940,-53911,-53883,-53854,-53826,\n    -53797,-53768,-53739,-53711,-53682,-53653,-53624,-53595,\n    -53566,-53537,-53508,-53479,-53450,-53421,-53392,-53363,\n    -53334,-53304,-53275,-53246,-53216,-53187,-53158,-53128,\n    -53099,-53069,-53040,-53010,-52981,-52951,-52922,-52892,\n    -52862,-52832,-52803,-52773,-52743,-52713,-52683,-52653,\n    -52624,-52594,-52564,-52534,-52503,-52473,-52443,-52413,\n    -52383,-52353,-52322,-52292,-52262,-52231,-52201,-52171,\n    -52140,-52110,-52079,-52049,-52018,-51988,-51957,-51926,\n    -51896,-51865,-51834,-51803,-51773,-51742,-51711,-51680,\n    -51649,-51618,-51587,-51556,-51525,-51494,-51463,-51432,\n    -51401,-51369,-51338,-51307,-51276,-51244,-51213,-51182,\n    -51150,-51119,-51087,-51056,-51024,-50993,-50961,-50929,\n    -50898,-50866,-50834,-50803,-50771,-50739,-50707,-50675,\n    -50644,-50612,-50580,-50548,-50516,-50484,-50452,-50420,\n    -50387,-50355,-50323,-50291,-50259,-50226,-50194,-50162,\n    -50129,-50097,-50065,-50032,-50000,-49967,-49935,-49902,\n    -49869,-49837,-49804,-49771,-49739,-49706,-49673,-49640,\n    -49608,-49575,-49542,-49509,-49476,-49443,-49410,-49377,\n    -49344,-49311,-49278,-49244,-49211,-49178,-49145,-49112,\n    -49078,-49045,-49012,-48978,-48945,-48911,-48878,-48844,\n    -48811,-48777,-48744,-48710,-48676,-48643,-48609,-48575,\n    -48542,-48508,-48474,-48440,-48406,-48372,-48338,-48305,\n    -48271,-48237,-48202,-48168,-48134,-48100,-48066,-48032,\n    -47998,-47963,-47929,-47895,-47860,-47826,-47792,-47757,\n    -47723,-47688,-47654,-47619,-47585,-47550,-47516,-47481,\n    -47446,-47412,-47377,-47342,-47307,-47273,-47238,-47203,\n    -47168,-47133,-47098,-47063,-47028,-46993,-46958,-46923,\n    -46888,-46853,-46818,-46783,-46747,-46712,-46677,-46642,\n    -46606,-46571,-46536,-46500,-46465,-46429,-46394,-46358,\n    -46323,-46287,-46251,-46216,-46180,-46145,-46109,-46073,\n    -46037,-46002,-45966,-45930,-45894,-45858,-45822,-45786,\n    -45750,-45714,-45678,-45642,-45606,-45570,-45534,-45498,\n    -45462,-45425,-45389,-45353,-45316,-45280,-45244,-45207,\n    -45171,-45135,-45098,-45062,-45025,-44989,-44952,-44915,\n    -44879,-44842,-44806,-44769,-44732,-44695,-44659,-44622,\n    -44585,-44548,-44511,-44474,-44437,-44400,-44363,-44326,\n    -44289,-44252,-44215,-44178,-44141,-44104,-44067,-44029,\n    -43992,-43955,-43918,-43880,-43843,-43806,-43768,-43731,\n    -43693,-43656,-43618,-43581,-43543,-43506,-43468,-43430,\n    -43393,-43355,-43317,-43280,-43242,-43204,-43166,-43128,\n    -43091,-43053,-43015,-42977,-42939,-42901,-42863,-42825,\n    -42787,-42749,-42711,-42672,-42634,-42596,-42558,-42520,\n    -42481,-42443,-42405,-42366,-42328,-42290,-42251,-42213,\n    -42174,-42136,-42097,-42059,-42020,-41982,-41943,-41904,\n    -41866,-41827,-41788,-41750,-41711,-41672,-41633,-41595,\n    -41556,-41517,-41478,-41439,-41400,-41361,-41322,-41283,\n    -41244,-41205,-41166,-41127,-41087,-41048,-41009,-40970,\n    -40931,-40891,-40852,-40813,-40773,-40734,-40695,-40655,\n    -40616,-40576,-40537,-40497,-40458,-40418,-40379,-40339,\n    -40299,-40260,-40220,-40180,-40141,-40101,-40061,-40021,\n    -39982,-39942,-39902,-39862,-39822,-39782,-39742,-39702,\n    -39662,-39622,-39582,-39542,-39502,-39462,-39422,-39382,\n    -39341,-39301,-39261,-39221,-39180,-39140,-39100,-39059,\n    -39019,-38979,-38938,-38898,-38857,-38817,-38776,-38736,\n    -38695,-38655,-38614,-38573,-38533,-38492,-38451,-38411,\n    -38370,-38329,-38288,-38248,-38207,-38166,-38125,-38084,\n    -38043,-38002,-37961,-37920,-37879,-37838,-37797,-37756,\n    -37715,-37674,-37633,-37592,-37550,-37509,-37468,-37427,\n    -37386,-37344,-37303,-37262,-37220,-37179,-37137,-37096,\n    -37055,-37013,-36972,-36930,-36889,-36847,-36805,-36764,\n    -36722,-36681,-36639,-36597,-36556,-36514,-36472,-36430,\n    -36388,-36347,-36305,-36263,-36221,-36179,-36137,-36095,\n    -36053,-36011,-35969,-35927,-35885,-35843,-35801,-35759,\n    -35717,-35675,-35633,-35590,-35548,-35506,-35464,-35421,\n    -35379,-35337,-35294,-35252,-35210,-35167,-35125,-35082,\n    -35040,-34997,-34955,-34912,-34870,-34827,-34785,-34742,\n    -34699,-34657,-34614,-34571,-34529,-34486,-34443,-34400,\n    -34358,-34315,-34272,-34229,-34186,-34143,-34100,-34057,\n    -34015,-33972,-33929,-33886,-33843,-33799,-33756,-33713,\n    -33670,-33627,-33584,-33541,-33498,-33454,-33411,-33368,\n    -33325,-33281,-33238,-33195,-33151,-33108,-33065,-33021,\n    -32978,-32934,-32891,-32847,-32804,-32760,-32717,-32673,\n    -32630,-32586,-32542,-32499,-32455,-32411,-32368,-32324,\n    -32280,-32236,-32193,-32149,-32105,-32061,-32017,-31974,\n    -31930,-31886,-31842,-31798,-31754,-31710,-31666,-31622,\n    -31578,-31534,-31490,-31446,-31402,-31357,-31313,-31269,\n    -31225,-31181,-31136,-31092,-31048,-31004,-30959,-30915,\n    -30871,-30826,-30782,-30738,-30693,-30649,-30604,-30560,\n    -30515,-30471,-30426,-30382,-30337,-30293,-30248,-30204,\n    -30159,-30114,-30070,-30025,-29980,-29936,-29891,-29846,\n    -29801,-29757,-29712,-29667,-29622,-29577,-29533,-29488,\n    -29443,-29398,-29353,-29308,-29263,-29218,-29173,-29128,\n    -29083,-29038,-28993,-28948,-28903,-28858,-28812,-28767,\n    -28722,-28677,-28632,-28586,-28541,-28496,-28451,-28405,\n    -28360,-28315,-28269,-28224,-28179,-28133,-28088,-28042,\n    -27997,-27952,-27906,-27861,-27815,-27770,-27724,-27678,\n    -27633,-27587,-27542,-27496,-27450,-27405,-27359,-27313,\n    -27268,-27222,-27176,-27131,-27085,-27039,-26993,-26947,\n    -26902,-26856,-26810,-26764,-26718,-26672,-26626,-26580,\n    -26534,-26488,-26442,-26396,-26350,-26304,-26258,-26212,\n    -26166,-26120,-26074,-26028,-25982,-25936,-25889,-25843,\n    -25797,-25751,-25705,-25658,-25612,-25566,-25520,-25473,\n    -25427,-25381,-25334,-25288,-25241,-25195,-25149,-25102,\n    -25056,-25009,-24963,-24916,-24870,-24823,-24777,-24730,\n    -24684,-24637,-24591,-24544,-24497,-24451,-24404,-24357,\n    -24311,-24264,-24217,-24171,-24124,-24077,-24030,-23984,\n    -23937,-23890,-23843,-23796,-23750,-23703,-23656,-23609,\n    -23562,-23515,-23468,-23421,-23374,-23327,-23280,-23233,\n    -23186,-23139,-23092,-23045,-22998,-22951,-22904,-22857,\n    -22810,-22763,-22716,-22668,-22621,-22574,-22527,-22480,\n    -22432,-22385,-22338,-22291,-22243,-22196,-22149,-22102,\n    -22054,-22007,-21960,-21912,-21865,-21817,-21770,-21723,\n    -21675,-21628,-21580,-21533,-21485,-21438,-21390,-21343,\n    -21295,-21248,-21200,-21153,-21105,-21057,-21010,-20962,\n    -20915,-20867,-20819,-20772,-20724,-20676,-20629,-20581,\n    -20533,-20485,-20438,-20390,-20342,-20294,-20246,-20199,\n    -20151,-20103,-20055,-20007,-19959,-19912,-19864,-19816,\n    -19768,-19720,-19672,-19624,-19576,-19528,-19480,-19432,\n    -19384,-19336,-19288,-19240,-19192,-19144,-19096,-19048,\n    -19000,-18951,-18903,-18855,-18807,-18759,-18711,-18663,\n    -18614,-18566,-18518,-18470,-18421,-18373,-18325,-18277,\n    -18228,-18180,-18132,-18084,-18035,-17987,-17939,-17890,\n    -17842,-17793,-17745,-17697,-17648,-17600,-17551,-17503,\n    -17455,-17406,-17358,-17309,-17261,-17212,-17164,-17115,\n    -17067,-17018,-16970,-16921,-16872,-16824,-16775,-16727,\n    -16678,-16629,-16581,-16532,-16484,-16435,-16386,-16338,\n    -16289,-16240,-16191,-16143,-16094,-16045,-15997,-15948,\n    -15899,-15850,-15802,-15753,-15704,-15655,-15606,-15557,\n    -15509,-15460,-15411,-15362,-15313,-15264,-15215,-15167,\n    -15118,-15069,-15020,-14971,-14922,-14873,-14824,-14775,\n    -14726,-14677,-14628,-14579,-14530,-14481,-14432,-14383,\n    -14334,-14285,-14236,-14187,-14138,-14089,-14040,-13990,\n    -13941,-13892,-13843,-13794,-13745,-13696,-13647,-13597,\n    -13548,-13499,-13450,-13401,-13351,-13302,-13253,-13204,\n    -13154,-13105,-13056,-13007,-12957,-12908,-12859,-12810,\n    -12760,-12711,-12662,-12612,-12563,-12514,-12464,-12415,\n    -12366,-12316,-12267,-12217,-12168,-12119,-12069,-12020,\n    -11970,-11921,-11872,-11822,-11773,-11723,-11674,-11624,\n    -11575,-11525,-11476,-11426,-11377,-11327,-11278,-11228,\n    -11179,-11129,-11080,-11030,-10981,-10931,-10882,-10832,\n    -10782,-10733,-10683,-10634,-10584,-10534,-10485,-10435,\n    -10386,-10336,-10286,-10237,-10187,-10137,-10088,-10038,\n    -9988,-9939,-9889,-9839,-9790,-9740,-9690,-9640,\n    -9591,-9541,-9491,-9442,-9392,-9342,-9292,-9243,\n    -9193,-9143,-9093,-9043,-8994,-8944,-8894,-8844,\n    -8794,-8745,-8695,-8645,-8595,-8545,-8496,-8446,\n    -8396,-8346,-8296,-8246,-8196,-8147,-8097,-8047,\n    -7997,-7947,-7897,-7847,-7797,-7747,-7697,-7648,\n    -7598,-7548,-7498,-7448,-7398,-7348,-7298,-7248,\n    -7198,-7148,-7098,-7048,-6998,-6948,-6898,-6848,\n    -6798,-6748,-6698,-6648,-6598,-6548,-6498,-6448,\n    -6398,-6348,-6298,-6248,-6198,-6148,-6098,-6048,\n    -5998,-5948,-5898,-5848,-5798,-5747,-5697,-5647,\n    -5597,-5547,-5497,-5447,-5397,-5347,-5297,-5247,\n    -5197,-5146,-5096,-5046,-4996,-4946,-4896,-4846,\n    -4796,-4745,-4695,-4645,-4595,-4545,-4495,-4445,\n    -4394,-4344,-4294,-4244,-4194,-4144,-4093,-4043,\n    -3993,-3943,-3893,-3843,-3792,-3742,-3692,-3642,\n    -3592,-3541,-3491,-3441,-3391,-3341,-3291,-3240,\n    -3190,-3140,-3090,-3039,-2989,-2939,-2889,-2839,\n    -2788,-2738,-2688,-2638,-2588,-2537,-2487,-2437,\n    -2387,-2336,-2286,-2236,-2186,-2135,-2085,-2035,\n    -1985,-1934,-1884,-1834,-1784,-1733,-1683,-1633,\n    -1583,-1532,-1482,-1432,-1382,-1331,-1281,-1231,\n    -1181,-1130,-1080,-1030,-980,-929,-879,-829,\n    -779,-728,-678,-628,-578,-527,-477,-427,\n    -376,-326,-276,-226,-175,-125,-75,-25,\n    25,75,125,175,226,276,326,376,\n    427,477,527,578,628,678,728,779,\n    829,879,929,980,1030,1080,1130,1181,\n    1231,1281,1331,1382,1432,1482,1532,1583,\n    1633,1683,1733,1784,1834,1884,1934,1985,\n    2035,2085,2135,2186,2236,2286,2336,2387,\n    2437,2487,2537,2587,2638,2688,2738,2788,\n    2839,2889,2939,2989,3039,3090,3140,3190,\n    3240,3291,3341,3391,3441,3491,3542,3592,\n    3642,3692,3742,3792,3843,3893,3943,3993,\n    4043,4093,4144,4194,4244,4294,4344,4394,\n    4445,4495,4545,4595,4645,4695,4745,4796,\n    4846,4896,4946,4996,5046,5096,5146,5197,\n    5247,5297,5347,5397,5447,5497,5547,5597,\n    5647,5697,5747,5798,5848,5898,5948,5998,\n    6048,6098,6148,6198,6248,6298,6348,6398,\n    6448,6498,6548,6598,6648,6698,6748,6798,\n    6848,6898,6948,6998,7048,7098,7148,7198,\n    7248,7298,7348,7398,7448,7498,7548,7598,\n    7648,7697,7747,7797,7847,7897,7947,7997,\n    8047,8097,8147,8196,8246,8296,8346,8396,\n    8446,8496,8545,8595,8645,8695,8745,8794,\n    8844,8894,8944,8994,9043,9093,9143,9193,\n    9243,9292,9342,9392,9442,9491,9541,9591,\n    9640,9690,9740,9790,9839,9889,9939,9988,\n    10038,10088,10137,10187,10237,10286,10336,10386,\n    10435,10485,10534,10584,10634,10683,10733,10782,\n    10832,10882,10931,10981,11030,11080,11129,11179,\n    11228,11278,11327,11377,11426,11476,11525,11575,\n    11624,11674,11723,11773,11822,11872,11921,11970,\n    12020,12069,12119,12168,12218,12267,12316,12366,\n    12415,12464,12514,12563,12612,12662,12711,12760,\n    12810,12859,12908,12957,13007,13056,13105,13154,\n    13204,13253,13302,13351,13401,13450,13499,13548,\n    13597,13647,13696,13745,13794,13843,13892,13941,\n    13990,14040,14089,14138,14187,14236,14285,14334,\n    14383,14432,14481,14530,14579,14628,14677,14726,\n    14775,14824,14873,14922,14971,15020,15069,15118,\n    15167,15215,15264,15313,15362,15411,15460,15509,\n    15557,15606,15655,15704,15753,15802,15850,15899,\n    15948,15997,16045,16094,16143,16191,16240,16289,\n    16338,16386,16435,16484,16532,16581,16629,16678,\n    16727,16775,16824,16872,16921,16970,17018,17067,\n    17115,17164,17212,17261,17309,17358,17406,17455,\n    17503,17551,17600,17648,17697,17745,17793,17842,\n    17890,17939,17987,18035,18084,18132,18180,18228,\n    18277,18325,18373,18421,18470,18518,18566,18614,\n    18663,18711,18759,18807,18855,18903,18951,19000,\n    19048,19096,19144,19192,19240,19288,19336,19384,\n    19432,19480,19528,19576,19624,19672,19720,19768,\n    19816,19864,19912,19959,20007,20055,20103,20151,\n    20199,20246,20294,20342,20390,20438,20485,20533,\n    20581,20629,20676,20724,20772,20819,20867,20915,\n    20962,21010,21057,21105,21153,21200,21248,21295,\n    21343,21390,21438,21485,21533,21580,21628,21675,\n    21723,21770,21817,21865,21912,21960,22007,22054,\n    22102,22149,22196,22243,22291,22338,22385,22432,\n    22480,22527,22574,22621,22668,22716,22763,22810,\n    22857,22904,22951,22998,23045,23092,23139,23186,\n    23233,23280,23327,23374,23421,23468,23515,23562,\n    23609,23656,23703,23750,23796,23843,23890,23937,\n    23984,24030,24077,24124,24171,24217,24264,24311,\n    24357,24404,24451,24497,24544,24591,24637,24684,\n    24730,24777,24823,24870,24916,24963,25009,25056,\n    25102,25149,25195,25241,25288,25334,25381,25427,\n    25473,25520,25566,25612,25658,25705,25751,25797,\n    25843,25889,25936,25982,26028,26074,26120,26166,\n    26212,26258,26304,26350,26396,26442,26488,26534,\n    26580,26626,26672,26718,26764,26810,26856,26902,\n    26947,26993,27039,27085,27131,27176,27222,27268,\n    27313,27359,27405,27450,27496,27542,27587,27633,\n    27678,27724,27770,27815,27861,27906,27952,27997,\n    28042,28088,28133,28179,28224,28269,28315,28360,\n    28405,28451,28496,28541,28586,28632,28677,28722,\n    28767,28812,28858,28903,28948,28993,29038,29083,\n    29128,29173,29218,29263,29308,29353,29398,29443,\n    29488,29533,29577,29622,29667,29712,29757,29801,\n    29846,29891,29936,29980,30025,30070,30114,30159,\n    30204,30248,30293,30337,30382,30427,30471,30516,\n    30560,30604,30649,30693,30738,30782,30826,30871,\n    30915,30959,31004,31048,31092,31136,31181,31225,\n    31269,31313,31357,31402,31446,31490,31534,31578,\n    31622,31666,31710,31754,31798,31842,31886,31930,\n    31974,32017,32061,32105,32149,32193,32236,32280,\n    32324,32368,32411,32455,32499,32542,32586,32630,\n    32673,32717,32760,32804,32847,32891,32934,32978,\n    33021,33065,33108,33151,33195,33238,33281,33325,\n    33368,33411,33454,33498,33541,33584,33627,33670,\n    33713,33756,33799,33843,33886,33929,33972,34015,\n    34057,34100,34143,34186,34229,34272,34315,34358,\n    34400,34443,34486,34529,34571,34614,34657,34699,\n    34742,34785,34827,34870,34912,34955,34997,35040,\n    35082,35125,35167,35210,35252,35294,35337,35379,\n    35421,35464,35506,35548,35590,35633,35675,35717,\n    35759,35801,35843,35885,35927,35969,36011,36053,\n    36095,36137,36179,36221,36263,36305,36347,36388,\n    36430,36472,36514,36556,36597,36639,36681,36722,\n    36764,36805,36847,36889,36930,36972,37013,37055,\n    37096,37137,37179,37220,37262,37303,37344,37386,\n    37427,37468,37509,37551,37592,37633,37674,37715,\n    37756,37797,37838,37879,37920,37961,38002,38043,\n    38084,38125,38166,38207,38248,38288,38329,38370,\n    38411,38451,38492,38533,38573,38614,38655,38695,\n    38736,38776,38817,38857,38898,38938,38979,39019,\n    39059,39100,39140,39180,39221,39261,39301,39341,\n    39382,39422,39462,39502,39542,39582,39622,39662,\n    39702,39742,39782,39822,39862,39902,39942,39982,\n    40021,40061,40101,40141,40180,40220,40260,40299,\n    40339,40379,40418,40458,40497,40537,40576,40616,\n    40655,40695,40734,40773,40813,40852,40891,40931,\n    40970,41009,41048,41087,41127,41166,41205,41244,\n    41283,41322,41361,41400,41439,41478,41517,41556,\n    41595,41633,41672,41711,41750,41788,41827,41866,\n    41904,41943,41982,42020,42059,42097,42136,42174,\n    42213,42251,42290,42328,42366,42405,42443,42481,\n    42520,42558,42596,42634,42672,42711,42749,42787,\n    42825,42863,42901,42939,42977,43015,43053,43091,\n    43128,43166,43204,43242,43280,43317,43355,43393,\n    43430,43468,43506,43543,43581,43618,43656,43693,\n    43731,43768,43806,43843,43880,43918,43955,43992,\n    44029,44067,44104,44141,44178,44215,44252,44289,\n    44326,44363,44400,44437,44474,44511,44548,44585,\n    44622,44659,44695,44732,44769,44806,44842,44879,\n    44915,44952,44989,45025,45062,45098,45135,45171,\n    45207,45244,45280,45316,45353,45389,45425,45462,\n    45498,45534,45570,45606,45642,45678,45714,45750,\n    45786,45822,45858,45894,45930,45966,46002,46037,\n    46073,46109,46145,46180,46216,46252,46287,46323,\n    46358,46394,46429,46465,46500,46536,46571,46606,\n    46642,46677,46712,46747,46783,46818,46853,46888,\n    46923,46958,46993,47028,47063,47098,47133,47168,\n    47203,47238,47273,47308,47342,47377,47412,47446,\n    47481,47516,47550,47585,47619,47654,47688,47723,\n    47757,47792,47826,47861,47895,47929,47963,47998,\n    48032,48066,48100,48134,48168,48202,48237,48271,\n    48305,48338,48372,48406,48440,48474,48508,48542,\n    48575,48609,48643,48676,48710,48744,48777,48811,\n    48844,48878,48911,48945,48978,49012,49045,49078,\n    49112,49145,49178,49211,49244,49278,49311,49344,\n    49377,49410,49443,49476,49509,49542,49575,49608,\n    49640,49673,49706,49739,49771,49804,49837,49869,\n    49902,49935,49967,50000,50032,50064,50097,50129,\n    50162,50194,50226,50259,50291,50323,50355,50387,\n    50420,50452,50484,50516,50548,50580,50612,50644,\n    50675,50707,50739,50771,50803,50834,50866,50898,\n    50929,50961,50993,51024,51056,51087,51119,51150,\n    51182,51213,51244,51276,51307,51338,51369,51401,\n    51432,51463,51494,51525,51556,51587,51618,51649,\n    51680,51711,51742,51773,51803,51834,51865,51896,\n    51926,51957,51988,52018,52049,52079,52110,52140,\n    52171,52201,52231,52262,52292,52322,52353,52383,\n    52413,52443,52473,52503,52534,52564,52594,52624,\n    52653,52683,52713,52743,52773,52803,52832,52862,\n    52892,52922,52951,52981,53010,53040,53069,53099,\n    53128,53158,53187,53216,53246,53275,53304,53334,\n    53363,53392,53421,53450,53479,53508,53537,53566,\n    53595,53624,53653,53682,53711,53739,53768,53797,\n    53826,53854,53883,53912,53940,53969,53997,54026,\n    54054,54082,54111,54139,54167,54196,54224,54252,\n    54280,54309,54337,54365,54393,54421,54449,54477,\n    54505,54533,54560,54588,54616,54644,54672,54699,\n    54727,54755,54782,54810,54837,54865,54892,54920,\n    54947,54974,55002,55029,55056,55084,55111,55138,\n    55165,55192,55219,55246,55274,55300,55327,55354,\n    55381,55408,55435,55462,55489,55515,55542,55569,\n    55595,55622,55648,55675,55701,55728,55754,55781,\n    55807,55833,55860,55886,55912,55938,55965,55991,\n    56017,56043,56069,56095,56121,56147,56173,56199,\n    56225,56250,56276,56302,56328,56353,56379,56404,\n    56430,56456,56481,56507,56532,56557,56583,56608,\n    56633,56659,56684,56709,56734,56760,56785,56810,\n    56835,56860,56885,56910,56935,56959,56984,57009,\n    57034,57059,57083,57108,57133,57157,57182,57206,\n    57231,57255,57280,57304,57329,57353,57377,57402,\n    57426,57450,57474,57498,57522,57546,57570,57594,\n    57618,57642,57666,57690,57714,57738,57762,57785,\n    57809,57833,57856,57880,57903,57927,57950,57974,\n    57997,58021,58044,58067,58091,58114,58137,58160,\n    58183,58207,58230,58253,58276,58299,58322,58345,\n    58367,58390,58413,58436,58459,58481,58504,58527,\n    58549,58572,58594,58617,58639,58662,58684,58706,\n    58729,58751,58773,58795,58818,58840,58862,58884,\n    58906,58928,58950,58972,58994,59016,59038,59059,\n    59081,59103,59125,59146,59168,59190,59211,59233,\n    59254,59276,59297,59318,59340,59361,59382,59404,\n    59425,59446,59467,59488,59509,59530,59551,59572,\n    59593,59614,59635,59656,59677,59697,59718,59739,\n    59759,59780,59801,59821,59842,59862,59883,59903,\n    59923,59944,59964,59984,60004,60025,60045,60065,\n    60085,60105,60125,60145,60165,60185,60205,60225,\n    60244,60264,60284,60304,60323,60343,60363,60382,\n    60402,60421,60441,60460,60479,60499,60518,60537,\n    60556,60576,60595,60614,60633,60652,60671,60690,\n    60709,60728,60747,60766,60785,60803,60822,60841,\n    60859,60878,60897,60915,60934,60952,60971,60989,\n    61007,61026,61044,61062,61081,61099,61117,61135,\n    61153,61171,61189,61207,61225,61243,61261,61279,\n    61297,61314,61332,61350,61367,61385,61403,61420,\n    61438,61455,61473,61490,61507,61525,61542,61559,\n    61577,61594,61611,61628,61645,61662,61679,61696,\n    61713,61730,61747,61764,61780,61797,61814,61831,\n    61847,61864,61880,61897,61913,61930,61946,61963,\n    61979,61995,62012,62028,62044,62060,62076,62092,\n    62108,62125,62141,62156,62172,62188,62204,62220,\n    62236,62251,62267,62283,62298,62314,62329,62345,\n    62360,62376,62391,62407,62422,62437,62453,62468,\n    62483,62498,62513,62528,62543,62558,62573,62588,\n    62603,62618,62633,62648,62662,62677,62692,62706,\n    62721,62735,62750,62764,62779,62793,62808,62822,\n    62836,62850,62865,62879,62893,62907,62921,62935,\n    62949,62963,62977,62991,63005,63019,63032,63046,\n    63060,63074,63087,63101,63114,63128,63141,63155,\n    63168,63182,63195,63208,63221,63235,63248,63261,\n    63274,63287,63300,63313,63326,63339,63352,63365,\n    63378,63390,63403,63416,63429,63441,63454,63466,\n    63479,63491,63504,63516,63528,63541,63553,63565,\n    63578,63590,63602,63614,63626,63638,63650,63662,\n    63674,63686,63698,63709,63721,63733,63745,63756,\n    63768,63779,63791,63803,63814,63825,63837,63848,\n    63859,63871,63882,63893,63904,63915,63927,63938,\n    63949,63960,63971,63981,63992,64003,64014,64025,\n    64035,64046,64057,64067,64078,64088,64099,64109,\n    64120,64130,64140,64151,64161,64171,64181,64192,\n    64202,64212,64222,64232,64242,64252,64261,64271,\n    64281,64291,64301,64310,64320,64330,64339,64349,\n    64358,64368,64377,64387,64396,64405,64414,64424,\n    64433,64442,64451,64460,64469,64478,64487,64496,\n    64505,64514,64523,64532,64540,64549,64558,64566,\n    64575,64584,64592,64600,64609,64617,64626,64634,\n    64642,64651,64659,64667,64675,64683,64691,64699,\n    64707,64715,64723,64731,64739,64747,64754,64762,\n    64770,64777,64785,64793,64800,64808,64815,64822,\n    64830,64837,64844,64852,64859,64866,64873,64880,\n    64887,64895,64902,64908,64915,64922,64929,64936,\n    64943,64949,64956,64963,64969,64976,64982,64989,\n    64995,65002,65008,65015,65021,65027,65033,65040,\n    65046,65052,65058,65064,65070,65076,65082,65088,\n    65094,65099,65105,65111,65117,65122,65128,65133,\n    65139,65144,65150,65155,65161,65166,65171,65177,\n    65182,65187,65192,65197,65202,65207,65212,65217,\n    65222,65227,65232,65237,65242,65246,65251,65256,\n    65260,65265,65270,65274,65279,65283,65287,65292,\n    65296,65300,65305,65309,65313,65317,65321,65325,\n    65329,65333,65337,65341,65345,65349,65352,65356,\n    65360,65363,65367,65371,65374,65378,65381,65385,\n    65388,65391,65395,65398,65401,65404,65408,65411,\n    65414,65417,65420,65423,65426,65429,65431,65434,\n    65437,65440,65442,65445,65448,65450,65453,65455,\n    65458,65460,65463,65465,65467,65470,65472,65474,\n    65476,65478,65480,65482,65484,65486,65488,65490,\n    65492,65494,65496,65497,65499,65501,65502,65504,\n    65505,65507,65508,65510,65511,65513,65514,65515,\n    65516,65518,65519,65520,65521,65522,65523,65524,\n    65525,65526,65527,65527,65528,65529,65530,65530,\n    65531,65531,65532,65532,65533,65533,65534,65534,\n    65534,65535,65535,65535,65535,65535,65535,65535\n};\n\nconst fixed_t *finecosine = &finesine[FINEANGLES/4];\n\nconst angle_t tantoangle[2049] =\n{\n    0,333772,667544,1001315,1335086,1668857,2002626,2336395,\n    2670163,3003929,3337694,3671457,4005219,4338979,4672736,5006492,\n    5340245,5673995,6007743,6341488,6675230,7008968,7342704,7676435,\n    8010164,8343888,8677609,9011325,9345037,9678744,10012447,10346145,\n    10679838,11013526,11347209,11680887,12014558,12348225,12681885,13015539,\n    13349187,13682829,14016464,14350092,14683714,15017328,15350936,15684536,\n    16018129,16351714,16685291,17018860,17352422,17685974,18019518,18353054,\n    18686582,19020100,19353610,19687110,20020600,20354080,20687552,21021014,\n    21354466,21687906,22021338,22354758,22688168,23021568,23354956,23688332,\n    24021698,24355052,24688396,25021726,25355046,25688352,26021648,26354930,\n    26688200,27021456,27354702,27687932,28021150,28354356,28687548,29020724,\n    29353888,29687038,30020174,30353296,30686404,31019496,31352574,31685636,\n    32018684,32351718,32684734,33017736,33350722,33683692,34016648,34349584,\n    34682508,35015412,35348300,35681172,36014028,36346868,36679688,37012492,\n    37345276,37678044,38010792,38343524,38676240,39008936,39341612,39674272,\n    40006912,40339532,40672132,41004716,41337276,41669820,42002344,42334848,\n    42667332,42999796,43332236,43664660,43997060,44329444,44661800,44994140,\n    45326456,45658752,45991028,46323280,46655512,46987720,47319908,47652072,\n    47984212,48316332,48648428,48980500,49312548,49644576,49976580,50308556,\n    50640512,50972444,51304352,51636236,51968096,52299928,52631740,52963524,\n    53295284,53627020,53958728,54290412,54622068,54953704,55285308,55616888,\n    55948444,56279972,56611472,56942948,57274396,57605816,57937212,58268576,\n    58599916,58931228,59262512,59593768,59924992,60256192,60587364,60918508,\n    61249620,61580704,61911760,62242788,62573788,62904756,63235692,63566604,\n    63897480,64228332,64559148,64889940,65220696,65551424,65882120,66212788,\n    66543420,66874024,67204600,67535136,67865648,68196120,68526568,68856984,\n    69187360,69517712,69848024,70178304,70508560,70838776,71168960,71499112,\n    71829224,72159312,72489360,72819376,73149360,73479304,73809216,74139096,\n    74468936,74798744,75128520,75458264,75787968,76117632,76447264,76776864,\n    77106424,77435952,77765440,78094888,78424304,78753688,79083032,79412336,\n    79741608,80070840,80400032,80729192,81058312,81387392,81716432,82045440,\n    82374408,82703336,83032224,83361080,83689896,84018664,84347400,84676096,\n    85004760,85333376,85661952,85990488,86318984,86647448,86975864,87304240,\n    87632576,87960872,88289128,88617344,88945520,89273648,89601736,89929792,\n    90257792,90585760,90913688,91241568,91569408,91897200,92224960,92552672,\n    92880336,93207968,93535552,93863088,94190584,94518040,94845448,95172816,\n    95500136,95827416,96154648,96481832,96808976,97136080,97463136,97790144,\n    98117112,98444032,98770904,99097736,99424520,99751256,100077944,100404592,\n    100731192,101057744,101384248,101710712,102037128,102363488,102689808,103016080,\n    103342312,103668488,103994616,104320696,104646736,104972720,105298656,105624552,\n    105950392,106276184,106601928,106927624,107253272,107578872,107904416,108229920,\n    108555368,108880768,109206120,109531416,109856664,110181872,110507016,110832120,\n    111157168,111482168,111807112,112132008,112456856,112781648,113106392,113431080,\n    113755720,114080312,114404848,114729328,115053760,115378136,115702464,116026744,\n    116350960,116675128,116999248,117323312,117647320,117971272,118295176,118619024,\n    118942816,119266560,119590248,119913880,120237456,120560984,120884456,121207864,\n    121531224,121854528,122177784,122500976,122824112,123147200,123470224,123793200,\n    124116120,124438976,124761784,125084528,125407224,125729856,126052432,126374960,\n    126697424,127019832,127342184,127664472,127986712,128308888,128631008,128953072,\n    129275080,129597024,129918912,130240744,130562520,130884232,131205888,131527480,\n    131849016,132170496,132491912,132813272,133134576,133455816,133776992,134098120,\n    134419184,134740176,135061120,135382000,135702816,136023584,136344272,136664912,\n    136985488,137306016,137626464,137946864,138267184,138587456,138907664,139227808,\n    139547904,139867920,140187888,140507776,140827616,141147392,141467104,141786752,\n    142106336,142425856,142745312,143064720,143384048,143703312,144022512,144341664,\n    144660736,144979744,145298704,145617584,145936400,146255168,146573856,146892480,\n    147211040,147529536,147847968,148166336,148484640,148802880,149121056,149439152,\n    149757200,150075168,150393072,150710912,151028688,151346400,151664048,151981616,\n    152299136,152616576,152933952,153251264,153568496,153885680,154202784,154519824,\n    154836784,155153696,155470528,155787296,156104000,156420624,156737200,157053696,\n    157370112,157686480,158002768,158318976,158635136,158951216,159267232,159583168,\n    159899040,160214848,160530592,160846256,161161840,161477376,161792832,162108208,\n    162423520,162738768,163053952,163369040,163684080,163999040,164313936,164628752,\n    164943504,165258176,165572784,165887312,166201776,166516160,166830480,167144736,\n    167458912,167773008,168087040,168400992,168714880,169028688,169342432,169656096,\n    169969696,170283216,170596672,170910032,171223344,171536576,171849728,172162800,\n    172475808,172788736,173101600,173414384,173727104,174039728,174352288,174664784,\n    174977200,175289536,175601792,175913984,176226096,176538144,176850096,177161984,\n    177473792,177785536,178097200,178408784,178720288,179031728,179343088,179654368,\n    179965568,180276704,180587744,180898720,181209616,181520448,181831184,182141856,\n    182452448,182762960,183073408,183383760,183694048,184004240,184314368,184624416,\n    184934400,185244288,185554096,185863840,186173504,186483072,186792576,187102000,\n    187411344,187720608,188029808,188338912,188647936,188956896,189265760,189574560,\n    189883264,190191904,190500448,190808928,191117312,191425632,191733872,192042016,\n    192350096,192658096,192966000,193273840,193581584,193889264,194196848,194504352,\n    194811792,195119136,195426400,195733584,196040688,196347712,196654656,196961520,\n    197268304,197574992,197881616,198188144,198494592,198800960,199107248,199413456,\n    199719584,200025616,200331584,200637456,200943248,201248960,201554576,201860128,\n    202165584,202470960,202776256,203081456,203386592,203691632,203996592,204301472,\n    204606256,204910976,205215600,205520144,205824592,206128960,206433248,206737456,\n    207041584,207345616,207649568,207953424,208257216,208560912,208864512,209168048,\n    209471488,209774832,210078112,210381296,210684384,210987408,211290336,211593184,\n    211895936,212198608,212501184,212803680,213106096,213408432,213710672,214012816,\n    214314880,214616864,214918768,215220576,215522288,215823920,216125472,216426928,\n    216728304,217029584,217330784,217631904,217932928,218233856,218534704,218835472,\n    219136144,219436720,219737216,220037632,220337952,220638192,220938336,221238384,\n    221538352,221838240,222138032,222437728,222737344,223036880,223336304,223635664,\n    223934912,224234096,224533168,224832160,225131072,225429872,225728608,226027232,\n    226325776,226624240,226922608,227220880,227519056,227817152,228115168,228413088,\n    228710912,229008640,229306288,229603840,229901312,230198688,230495968,230793152,\n    231090256,231387280,231684192,231981024,232277760,232574416,232870960,233167440,\n    233463808,233760096,234056288,234352384,234648384,234944304,235240128,235535872,\n    235831504,236127056,236422512,236717888,237013152,237308336,237603424,237898416,\n    238193328,238488144,238782864,239077488,239372016,239666464,239960816,240255072,\n    240549232,240843312,241137280,241431168,241724960,242018656,242312256,242605776,\n    242899200,243192512,243485744,243778896,244071936,244364880,244657744,244950496,\n    245243168,245535744,245828224,246120608,246412912,246705104,246997216,247289216,\n    247581136,247872960,248164688,248456320,248747856,249039296,249330640,249621904,\n    249913056,250204128,250495088,250785968,251076736,251367424,251658016,251948512,\n    252238912,252529200,252819408,253109520,253399536,253689456,253979280,254269008,\n    254558640,254848176,255137632,255426976,255716224,256005376,256294432,256583392,\n    256872256,257161024,257449696,257738272,258026752,258315136,258603424,258891600,\n    259179696,259467696,259755600,260043392,260331104,260618704,260906224,261193632,\n    261480960,261768176,262055296,262342320,262629248,262916080,263202816,263489456,\n    263776000,264062432,264348784,264635024,264921168,265207216,265493168,265779024,\n    266064784,266350448,266636000,266921472,267206832,267492096,267777264,268062336,\n    268347312,268632192,268916960,269201632,269486208,269770688,270055072,270339360,\n    270623552,270907616,271191616,271475488,271759296,272042976,272326560,272610048,\n    272893440,273176736,273459936,273743040,274026048,274308928,274591744,274874432,\n    275157024,275439520,275721920,276004224,276286432,276568512,276850528,277132416,\n    277414240,277695936,277977536,278259040,278540448,278821728,279102944,279384032,\n    279665056,279945952,280226752,280507456,280788064,281068544,281348960,281629248,\n    281909472,282189568,282469568,282749440,283029248,283308960,283588544,283868032,\n    284147424,284426720,284705920,284985024,285264000,285542912,285821696,286100384,\n    286378976,286657440,286935840,287214112,287492320,287770400,288048384,288326240,\n    288604032,288881696,289159264,289436768,289714112,289991392,290268576,290545632,\n    290822592,291099456,291376224,291652896,291929440,292205888,292482272,292758528,\n    293034656,293310720,293586656,293862496,294138240,294413888,294689440,294964864,\n    295240192,295515424,295790560,296065600,296340512,296615360,296890080,297164704,\n    297439200,297713632,297987936,298262144,298536256,298810240,299084160,299357952,\n    299631648,299905248,300178720,300452128,300725408,300998592,301271680,301544640,\n    301817536,302090304,302362976,302635520,302908000,303180352,303452608,303724768,\n    303996800,304268768,304540608,304812320,305083968,305355520,305626944,305898272,\n    306169472,306440608,306711616,306982528,307253344,307524064,307794656,308065152,\n    308335552,308605856,308876032,309146112,309416096,309685984,309955744,310225408,\n    310494976,310764448,311033824,311303072,311572224,311841280,312110208,312379040,\n    312647776,312916416,313184960,313453376,313721696,313989920,314258016,314526016,\n    314793920,315061728,315329408,315597024,315864512,316131872,316399168,316666336,\n    316933408,317200384,317467232,317733984,318000640,318267200,318533632,318799968,\n    319066208,319332352,319598368,319864288,320130112,320395808,320661408,320926912,\n    321192320,321457632,321722816,321987904,322252864,322517760,322782528,323047200,\n    323311744,323576192,323840544,324104800,324368928,324632992,324896928,325160736,\n    325424448,325688096,325951584,326215008,326478304,326741504,327004608,327267584,\n    327530464,327793248,328055904,328318496,328580960,328843296,329105568,329367712,\n    329629760,329891680,330153536,330415264,330676864,330938400,331199808,331461120,\n    331722304,331983392,332244384,332505280,332766048,333026752,333287296,333547776,\n    333808128,334068384,334328544,334588576,334848512,335108352,335368064,335627712,\n    335887200,336146624,336405920,336665120,336924224,337183200,337442112,337700864,\n    337959552,338218112,338476576,338734944,338993184,339251328,339509376,339767296,\n    340025120,340282848,340540480,340797984,341055392,341312704,341569888,341826976,\n    342083968,342340832,342597600,342854272,343110848,343367296,343623648,343879904,\n    344136032,344392064,344648000,344903808,345159520,345415136,345670656,345926048,\n    346181344,346436512,346691616,346946592,347201440,347456224,347710880,347965440,\n    348219872,348474208,348728448,348982592,349236608,349490528,349744320,349998048,\n    350251648,350505152,350758528,351011808,351264992,351518048,351771040,352023872,\n    352276640,352529280,352781824,353034272,353286592,353538816,353790944,354042944,\n    354294880,354546656,354798368,355049952,355301440,355552800,355804096,356055264,\n    356306304,356557280,356808128,357058848,357309504,357560032,357810464,358060768,\n    358311008,358561088,358811104,359060992,359310784,359560480,359810048,360059520,\n    360308896,360558144,360807296,361056352,361305312,361554144,361802880,362051488,\n    362300032,362548448,362796736,363044960,363293056,363541024,363788928,364036704,\n    364284384,364531936,364779392,365026752,365274016,365521152,365768192,366015136,\n    366261952,366508672,366755296,367001792,367248192,367494496,367740704,367986784,\n    368232768,368478656,368724416,368970080,369215648,369461088,369706432,369951680,\n    370196800,370441824,370686752,370931584,371176288,371420896,371665408,371909792,\n    372154080,372398272,372642336,372886304,373130176,373373952,373617600,373861152,\n    374104608,374347936,374591168,374834304,375077312,375320224,375563040,375805760,\n    376048352,376290848,376533248,376775520,377017696,377259776,377501728,377743584,\n    377985344,378227008,378468544,378709984,378951328,379192544,379433664,379674688,\n    379915584,380156416,380397088,380637696,380878176,381118560,381358848,381599040,\n    381839104,382079072,382318912,382558656,382798304,383037856,383277280,383516640,\n    383755840,383994976,384233984,384472896,384711712,384950400,385188992,385427488,\n    385665888,385904160,386142336,386380384,386618368,386856224,387093984,387331616,\n    387569152,387806592,388043936,388281152,388518272,388755296,388992224,389229024,\n    389465728,389702336,389938816,390175200,390411488,390647680,390883744,391119712,\n    391355584,391591328,391826976,392062528,392297984,392533312,392768544,393003680,\n    393238720,393473632,393708448,393943168,394177760,394412256,394646656,394880960,\n    395115136,395349216,395583200,395817088,396050848,396284512,396518080,396751520,\n    396984864,397218112,397451264,397684288,397917248,398150080,398382784,398615424,\n    398847936,399080320,399312640,399544832,399776928,400008928,400240832,400472608,\n    400704288,400935872,401167328,401398720,401629984,401861120,402092192,402323136,\n    402553984,402784736,403015360,403245888,403476320,403706656,403936896,404167008,\n    404397024,404626944,404856736,405086432,405316032,405545536,405774912,406004224,\n    406233408,406462464,406691456,406920320,407149088,407377760,407606336,407834784,\n    408063136,408291392,408519520,408747584,408975520,409203360,409431072,409658720,\n    409886240,410113664,410340992,410568192,410795296,411022304,411249216,411476032,\n    411702720,411929312,412155808,412382176,412608480,412834656,413060736,413286720,\n    413512576,413738336,413964000,414189568,414415040,414640384,414865632,415090784,\n    415315840,415540800,415765632,415990368,416215008,416439552,416663968,416888288,\n    417112512,417336640,417560672,417784576,418008384,418232096,418455712,418679200,\n    418902624,419125920,419349120,419572192,419795200,420018080,420240864,420463552,\n    420686144,420908608,421130976,421353280,421575424,421797504,422019488,422241344,\n    422463104,422684768,422906336,423127776,423349120,423570400,423791520,424012576,\n    424233536,424454368,424675104,424895744,425116288,425336736,425557056,425777280,\n    425997408,426217440,426437376,426657184,426876928,427096544,427316064,427535488,\n    427754784,427974016,428193120,428412128,428631040,428849856,429068544,429287168,\n    429505664,429724064,429942368,430160576,430378656,430596672,430814560,431032352,\n    431250048,431467616,431685120,431902496,432119808,432336992,432554080,432771040,\n    432987936,433204736,433421408,433637984,433854464,434070848,434287104,434503296,\n    434719360,434935360,435151232,435367008,435582656,435798240,436013696,436229088,\n    436444352,436659520,436874592,437089568,437304416,437519200,437733856,437948416,\n    438162880,438377248,438591520,438805696,439019744,439233728,439447584,439661344,\n    439875008,440088576,440302048,440515392,440728672,440941824,441154880,441367872,\n    441580736,441793472,442006144,442218720,442431168,442643552,442855808,443067968,\n    443280032,443492000,443703872,443915648,444127296,444338880,444550336,444761696,\n    444972992,445184160,445395232,445606176,445817056,446027840,446238496,446449088,\n    446659552,446869920,447080192,447290400,447500448,447710432,447920320,448130112,\n    448339776,448549376,448758848,448968224,449177536,449386720,449595808,449804800,\n    450013664,450222464,450431168,450639776,450848256,451056640,451264960,451473152,\n    451681248,451889248,452097152,452304960,452512672,452720288,452927808,453135232,\n    453342528,453549760,453756864,453963904,454170816,454377632,454584384,454791008,\n    454997536,455203968,455410304,455616544,455822688,456028704,456234656,456440512,\n    456646240,456851904,457057472,457262912,457468256,457673536,457878688,458083744,\n    458288736,458493600,458698368,458903040,459107616,459312096,459516480,459720768,\n    459924960,460129056,460333056,460536960,460740736,460944448,461148064,461351584,\n    461554976,461758304,461961536,462164640,462367680,462570592,462773440,462976160,\n    463178816,463381344,463583776,463786144,463988384,464190560,464392608,464594560,\n    464796448,464998208,465199872,465401472,465602944,465804320,466005600,466206816,\n    466407904,466608896,466809824,467010624,467211328,467411936,467612480,467812896,\n    468013216,468213440,468413600,468613632,468813568,469013440,469213184,469412832,\n    469612416,469811872,470011232,470210528,470409696,470608800,470807776,471006688,\n    471205472,471404192,471602784,471801312,471999712,472198048,472396288,472594400,\n    472792448,472990400,473188256,473385984,473583648,473781216,473978688,474176064,\n    474373344,474570528,474767616,474964608,475161504,475358336,475555040,475751648,\n    475948192,476144608,476340928,476537184,476733312,476929376,477125344,477321184,\n    477516960,477712640,477908224,478103712,478299104,478494400,478689600,478884704,\n    479079744,479274656,479469504,479664224,479858880,480053408,480247872,480442240,\n    480636512,480830656,481024736,481218752,481412640,481606432,481800128,481993760,\n    482187264,482380704,482574016,482767264,482960416,483153472,483346432,483539296,\n    483732064,483924768,484117344,484309856,484502240,484694560,484886784,485078912,\n    485270944,485462880,485654720,485846464,486038144,486229696,486421184,486612576,\n    486803840,486995040,487186176,487377184,487568096,487758912,487949664,488140320,\n    488330880,488521312,488711712,488901984,489092160,489282240,489472256,489662176,\n    489851968,490041696,490231328,490420896,490610336,490799712,490988960,491178144,\n    491367232,491556224,491745120,491933920,492122656,492311264,492499808,492688256,\n    492876608,493064864,493253056,493441120,493629120,493817024,494004832,494192544,\n    494380160,494567712,494755136,494942496,495129760,495316928,495504000,495691008,\n    495877888,496064704,496251424,496438048,496624608,496811040,496997408,497183680,\n    497369856,497555936,497741920,497927840,498113632,498299360,498484992,498670560,\n    498856000,499041376,499226656,499411840,499596928,499781920,499966848,500151680,\n    500336416,500521056,500705600,500890080,501074464,501258752,501442944,501627040,\n    501811072,501995008,502178848,502362592,502546240,502729824,502913312,503096704,\n    503280000,503463232,503646368,503829408,504012352,504195200,504377984,504560672,\n    504743264,504925760,505108192,505290496,505472736,505654912,505836960,506018944,\n    506200832,506382624,506564320,506745952,506927488,507108928,507290272,507471552,\n    507652736,507833824,508014816,508195744,508376576,508557312,508737952,508918528,\n    509099008,509279392,509459680,509639904,509820032,510000064,510180000,510359872,\n    510539648,510719328,510898944,511078432,511257856,511437216,511616448,511795616,\n    511974688,512153664,512332576,512511392,512690112,512868768,513047296,513225792,\n    513404160,513582432,513760640,513938784,514116800,514294752,514472608,514650368,\n    514828064,515005664,515183168,515360608,515537952,515715200,515892352,516069440,\n    516246432,516423328,516600160,516776896,516953536,517130112,517306592,517482976,\n    517659264,517835488,518011616,518187680,518363648,518539520,518715296,518891008,\n    519066624,519242144,519417600,519592960,519768256,519943424,520118528,520293568,\n    520468480,520643328,520818112,520992800,521167392,521341888,521516320,521690656,\n    521864896,522039072,522213152,522387168,522561056,522734912,522908640,523082304,\n    523255872,523429376,523602784,523776096,523949312,524122464,524295552,524468512,\n    524641440,524814240,524986976,525159616,525332192,525504640,525677056,525849344,\n    526021568,526193728,526365792,526537760,526709632,526881440,527053152,527224800,\n    527396352,527567840,527739200,527910528,528081728,528252864,528423936,528594880,\n    528765760,528936576,529107296,529277920,529448480,529618944,529789344,529959648,\n    530129856,530300000,530470048,530640000,530809888,530979712,531149440,531319072,\n    531488608,531658080,531827488,531996800,532166016,532335168,532504224,532673184,\n    532842080,533010912,533179616,533348288,533516832,533685312,533853728,534022048,\n    534190272,534358432,534526496,534694496,534862400,535030240,535197984,535365632,\n    535533216,535700704,535868128,536035456,536202720,536369888,536536992,536704000,\n    536870912\n};\n\n// Now where did these came from?\nconst byte gammatable[5][256] =\n{\n    {\n        1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,\n        17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,\n        33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,\n        49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,\n        65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,\n        81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,\n        97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,\n        113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,\n        128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,\n        144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,\n        160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,\n        176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,\n        192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,\n        208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,\n        224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,\n        240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255\n    },\n\n    {\n        2,4,5,7,8,10,11,12,14,15,16,18,19,20,21,23,\n        24,25,26,27,29,30,31,32,33,34,36,37,38,39,40,41,\n        42,44,45,46,47,48,49,50,51,52,54,55,56,57,58,59,\n        60,61,62,63,64,65,66,67,69,70,71,72,73,74,75,76,\n        77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,\n        93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,\n        109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,\n        125,126,127,128,129,129,130,131,132,133,134,135,136,137,138,139,\n        140,141,142,143,144,145,146,147,148,148,149,150,151,152,153,154,\n        155,156,157,158,159,160,161,162,163,163,164,165,166,167,168,169,\n        170,171,172,173,174,175,175,176,177,178,179,180,181,182,183,184,\n        185,186,186,187,188,189,190,191,192,193,194,195,196,196,197,198,\n        199,200,201,202,203,204,205,205,206,207,208,209,210,211,212,213,\n        214,214,215,216,217,218,219,220,221,222,222,223,224,225,226,227,\n        228,229,230,230,231,232,233,234,235,236,237,237,238,239,240,241,\n        242,243,244,245,245,246,247,248,249,250,251,252,252,253,254,255\n    },\n\n    {\n        4,7,9,11,13,15,17,19,21,22,24,26,27,29,30,32,\n        33,35,36,38,39,40,42,43,45,46,47,48,50,51,52,54,\n        55,56,57,59,60,61,62,63,65,66,67,68,69,70,72,73,\n        74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90,\n        91,92,93,94,95,96,97,98,100,101,102,103,104,105,106,107,\n        108,109,110,111,112,113,114,114,115,116,117,118,119,120,121,122,\n        123,124,125,126,127,128,129,130,131,132,133,133,134,135,136,137,\n        138,139,140,141,142,143,144,144,145,146,147,148,149,150,151,152,\n        153,153,154,155,156,157,158,159,160,160,161,162,163,164,165,166,\n        166,167,168,169,170,171,172,172,173,174,175,176,177,178,178,179,\n        180,181,182,183,183,184,185,186,187,188,188,189,190,191,192,193,\n        193,194,195,196,197,197,198,199,200,201,201,202,203,204,205,206,\n        206,207,208,209,210,210,211,212,213,213,214,215,216,217,217,218,\n        219,220,221,221,222,223,224,224,225,226,227,228,228,229,230,231,\n        231,232,233,234,235,235,236,237,238,238,239,240,241,241,242,243,\n        244,244,245,246,247,247,248,249,250,251,251,252,253,254,254,255\n    },\n\n    {\n        8,12,16,19,22,24,27,29,31,34,36,38,40,41,43,45,\n        47,49,50,52,53,55,57,58,60,61,63,64,65,67,68,70,\n        71,72,74,75,76,77,79,80,81,82,84,85,86,87,88,90,\n        91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107,\n        108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,\n        124,125,126,127,128,129,130,131,132,133,134,135,135,136,137,138,\n        139,140,141,142,143,143,144,145,146,147,148,149,150,150,151,152,\n        153,154,155,155,156,157,158,159,160,160,161,162,163,164,165,165,\n        166,167,168,169,169,170,171,172,173,173,174,175,176,176,177,178,\n        179,180,180,181,182,183,183,184,185,186,186,187,188,189,189,190,\n        191,192,192,193,194,195,195,196,197,197,198,199,200,200,201,202,\n        202,203,204,205,205,206,207,207,208,209,210,210,211,212,212,213,\n        214,214,215,216,216,217,218,219,219,220,221,221,222,223,223,224,\n        225,225,226,227,227,228,229,229,230,231,231,232,233,233,234,235,\n        235,236,237,237,238,238,239,240,240,241,242,242,243,244,244,245,\n        246,246,247,247,248,249,249,250,251,251,252,253,253,254,254,255\n    },\n\n\n    {\n        16,23,28,32,36,39,42,45,48,50,53,55,57,60,62,64,\n        66,68,69,71,73,75,76,78,80,81,83,84,86,87,89,90,\n        92,93,94,96,97,98,100,101,102,103,105,106,107,108,109,110,\n        112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,128,\n        128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,\n        143,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,\n        157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169,\n        169,170,171,172,172,173,174,175,175,176,177,177,178,179,180,180,\n        181,182,182,183,184,184,185,186,187,187,188,189,189,190,191,191,\n        192,193,193,194,195,195,196,196,197,198,198,199,200,200,201,202,\n        202,203,203,204,205,205,206,207,207,208,208,209,210,210,211,211,\n        212,213,213,214,214,215,216,216,217,217,218,219,219,220,220,221,\n        221,222,223,223,224,224,225,225,226,227,227,228,228,229,229,230,\n        230,231,232,232,233,233,234,234,235,235,236,236,237,237,238,239,\n        239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,\n        247,248,248,249,249,250,250,251,251,252,252,253,254,254,255,255\n    }\n};\n\n"
  },
  {
    "path": "fbdoom/tables.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 1993-2008 Raven Software\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tLookup tables.\n//\tDo not try to look them up :-).\n//\tIn the order of appearance: \n//\n//\tint finetangent[4096]\t- Tangens LUT.\n//\t Should work with BAM fairly well (12 of 16bit,\n//      effectively, by shifting).\n//\n//\tint finesine[10240]\t\t- Sine lookup.\n//\t Guess what, serves as cosine, too.\n//\t Remarkable thing is, how to use BAMs with this? \n//\n//\tint tantoangle[2049]\t- ArcTan LUT,\n//\t  maps tan(angle) to angle fast. Gotta search.\t\n//    \n\n\n#ifndef __TABLES__\n#define __TABLES__\n\n#include \"doomtype.h\"\n\n#include \"m_fixed.h\"\n\t\n#define FINEANGLES\t\t8192\n#define FINEMASK\t\t(FINEANGLES-1)\n\n\n// 0x100000000 to 0x2000\n#define ANGLETOFINESHIFT\t19\t\t\n\n// Effective size is 10240.\nextern const fixed_t finesine[5*FINEANGLES/4];\n\n// Re-use data, is just PI/2 pahse shift.\nextern const fixed_t *finecosine;\n\n\n// Effective size is 4096.\nextern const fixed_t finetangent[FINEANGLES/2];\n\n// Gamma correction tables.\nextern const byte gammatable[5][256];\n\n// Binary Angle Measument, BAM.\n\n#define ANG45           0x20000000\n#define ANG90           0x40000000\n#define ANG180          0x80000000\n#define ANG270          0xc0000000\n#define ANG_MAX         0xffffffff\n\n#define ANG1            (ANG45 / 45)\n#define ANG60           (ANG180 / 3)\n\n// Heretic code uses this definition as though it represents one \n// degree, but it is not!  This is actually ~1.40 degrees.\n\n#define ANG1_X          0x01000000\n\n#define SLOPERANGE\t\t2048\n#define SLOPEBITS\t\t11\n#define DBITS\t\t\t(FRACBITS-SLOPEBITS)\n\ntypedef unsigned angle_t;\n\n\n// Effective size is 2049;\n// The +1 size is to handle the case when x==y\n//  without additional checking.\nextern const angle_t tantoangle[SLOPERANGE+1];\n\n\n// Utility function,\n//  called by R_PointToAngle.\nint SlopeDiv(unsigned int num, unsigned int den);\n\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/v_patch.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//      Refresh/rendering module, shared data struct definitions.\n//\n\n\n#ifndef V_PATCH_H\n#define V_PATCH_H\n\n// Patches.\n// A patch holds one or more columns.\n// Patches are used for sprites and all masked pictures,\n// and we compose textures from the TEXTURE1/2 lists\n// of patches.\n\ntypedef struct \n{ \n    short\t\twidth;\t\t// bounding box size \n    short\t\theight; \n    short\t\tleftoffset;\t// pixels to the left of origin \n    short\t\ttopoffset;\t// pixels below the origin \n    int\t\t\tcolumnofs[8];\t// only [width] used\n    // the [0] is &columnofs[width] \n} PACKEDATTR patch_t;\n\n// posts are runs of non masked source pixels\ntypedef struct\n{\n    byte\t\ttopdelta;\t// -1 is the last post in a column\n    byte\t\tlength; \t// length data bytes follows\n} PACKEDATTR post_t;\n\n// column_t is a list of 0 or more post_t, (byte)-1 terminated\ntypedef post_t\tcolumn_t;\n\n#endif \n\n"
  },
  {
    "path": "fbdoom/v_video.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 1993-2008 Raven Software\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tGamma correction LUT stuff.\n//\tFunctions to draw patches (by post) directly to screen.\n//\tFunctions to blit a block to the screen.\n//\n\n#include <stdio.h>\n#include <string.h>\n#include <math.h>\n\n#include \"i_system.h\"\n\n#include \"doomtype.h\"\n\n#include \"deh_str.h\"\n#include \"i_swap.h\"\n#include \"i_video.h\"\n#include \"m_bbox.h\"\n#include \"m_misc.h\"\n#include \"v_video.h\"\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\n#include \"config.h\"\n#ifdef HAVE_LIBPNG\n#include <png.h>\n#endif\n\n// TODO: There are separate RANGECHECK defines for different games, but this\n// is common code. Fix this.\n#define RANGECHECK\n\n// Blending table used for fuzzpatch, etc.\n// Only used in Heretic/Hexen\n\nbyte *tinttable = NULL;\n\n// villsa [STRIFE] Blending table used for Strife\nbyte *xlatab = NULL;\n\n// The screen buffer that the v_video.c code draws to.\n\nstatic byte *dest_screen = NULL;\n\nint dirtybox[4]; \n\n// haleyjd 08/28/10: clipping callback function for patches.\n// This is needed for Chocolate Strife, which clips patches to the screen.\nstatic vpatchclipfunc_t patchclip_callback = NULL;\n\n//\n// V_MarkRect \n// \nvoid V_MarkRect(int x, int y, int width, int height) \n{ \n    // If we are temporarily using an alternate screen, do not \n    // affect the update box.\n\n    if (dest_screen == I_VideoBuffer)\n    {\n        M_AddToBox (dirtybox, x, y); \n        M_AddToBox (dirtybox, x + width-1, y + height-1); \n    }\n} \n \n\n//\n// V_CopyRect \n// \nvoid V_CopyRect(int srcx, int srcy, byte *source,\n                int width, int height,\n                int destx, int desty)\n{ \n    byte *src;\n    byte *dest; \n \n#ifdef RANGECHECK \n    if (srcx < 0\n     || srcx + width > SCREENWIDTH\n     || srcy < 0\n     || srcy + height > SCREENHEIGHT \n     || destx < 0\n     || destx + width > SCREENWIDTH\n     || desty < 0\n     || desty + height > SCREENHEIGHT)\n    {\n        I_Error (\"Bad V_CopyRect\");\n    }\n#endif \n\n    V_MarkRect(destx, desty, width, height); \n \n    src = source + SCREENWIDTH * srcy + srcx; \n    dest = dest_screen + SCREENWIDTH * desty + destx; \n\n    for ( ; height>0 ; height--) \n    { \n        memcpy(dest, src, width); \n        src += SCREENWIDTH; \n        dest += SCREENWIDTH; \n    } \n} \n \n//\n// V_SetPatchClipCallback\n//\n// haleyjd 08/28/10: Added for Strife support.\n// By calling this function, you can setup runtime error checking for patch \n// clipping. Strife never caused errors by drawing patches partway off-screen.\n// Some versions of vanilla DOOM also behaved differently than the default\n// implementation, so this could possibly be extended to those as well for\n// accurate emulation.\n//\nvoid V_SetPatchClipCallback(vpatchclipfunc_t func)\n{\n    patchclip_callback = func;\n}\n\n//\n// V_DrawPatch\n// Masks a column based masked pic to the screen. \n//\n\nvoid V_DrawPatch(int x, int y, patch_t *patch)\n{ \n    int count;\n    int col;\n    column_t *column;\n    byte *desttop;\n    byte *dest;\n    byte *source;\n    int w;\n\n    y -= SHORT(patch->topoffset);\n    x -= SHORT(patch->leftoffset);\n\n    // haleyjd 08/28/10: Strife needs silent error checking here.\n    if(patchclip_callback)\n    {\n        if(!patchclip_callback(patch, x, y))\n            return;\n    }\n\n#ifdef RANGECHECK\n    if (x < 0\n     || x + SHORT(patch->width) > SCREENWIDTH\n     || y < 0\n     || y + SHORT(patch->height) > SCREENHEIGHT)\n    {\n        I_Error(\"Bad V_DrawPatch x=%i y=%i patch.width=%i patch.height=%i topoffset=%i leftoffset=%i\", x, y, patch->width, patch->height, patch->topoffset, patch->leftoffset);\n    }\n#endif\n\n    V_MarkRect(x, y, SHORT(patch->width), SHORT(patch->height));\n\n    col = 0;\n    desttop = dest_screen + y * SCREENWIDTH + x;\n\n    w = SHORT(patch->width);\n\n    for ( ; col<w ; x++, col++, desttop++)\n    {\n        column = (column_t *)((byte *)patch + LONG(patch->columnofs[col]));\n\n        // step through the posts in a column\n        while (column->topdelta != 0xff)\n        {\n            source = (byte *)column + 3;\n            dest = desttop + column->topdelta*SCREENWIDTH;\n            count = column->length;\n\n            while (count--)\n            {\n                *dest = *source++;\n                dest += SCREENWIDTH;\n            }\n            column = (column_t *)((byte *)column + column->length + 4);\n        }\n    }\n}\n\n//\n// V_DrawPatchFlipped\n// Masks a column based masked pic to the screen.\n// Flips horizontally, e.g. to mirror face.\n//\n\nvoid V_DrawPatchFlipped(int x, int y, patch_t *patch)\n{\n    int count;\n    int col; \n    column_t *column; \n    byte *desttop;\n    byte *dest;\n    byte *source; \n    int w; \n \n    y -= SHORT(patch->topoffset); \n    x -= SHORT(patch->leftoffset); \n\n    // haleyjd 08/28/10: Strife needs silent error checking here.\n    if(patchclip_callback)\n    {\n        if(!patchclip_callback(patch, x, y))\n            return;\n    }\n\n#ifdef RANGECHECK \n    if (x < 0\n     || x + SHORT(patch->width) > SCREENWIDTH\n     || y < 0\n     || y + SHORT(patch->height) > SCREENHEIGHT)\n    {\n        I_Error(\"Bad V_DrawPatchFlipped\");\n    }\n#endif\n\n    V_MarkRect (x, y, SHORT(patch->width), SHORT(patch->height));\n\n    col = 0;\n    desttop = dest_screen + y * SCREENWIDTH + x;\n\n    w = SHORT(patch->width);\n\n    for ( ; col<w ; x++, col++, desttop++)\n    {\n        column = (column_t *)((byte *)patch + LONG(patch->columnofs[w-1-col]));\n\n        // step through the posts in a column\n        while (column->topdelta != 0xff )\n        {\n            source = (byte *)column + 3;\n            dest = desttop + column->topdelta*SCREENWIDTH;\n            count = column->length;\n\n            while (count--)\n            {\n                *dest = *source++;\n                dest += SCREENWIDTH;\n            }\n            column = (column_t *)((byte *)column + column->length + 4);\n        }\n    }\n}\n\n\n\n//\n// V_DrawPatchDirect\n// Draws directly to the screen on the pc. \n//\n\nvoid V_DrawPatchDirect(int x, int y, patch_t *patch)\n{\n    V_DrawPatch(x, y, patch); \n} \n\n//\n// V_DrawTLPatch\n//\n// Masks a column based translucent masked pic to the screen.\n//\n\nvoid V_DrawTLPatch(int x, int y, patch_t * patch)\n{\n    int count, col;\n    column_t *column;\n    byte *desttop, *dest, *source;\n    int w;\n\n    y -= SHORT(patch->topoffset);\n    x -= SHORT(patch->leftoffset);\n\n    if (x < 0\n     || x + SHORT(patch->width) > SCREENWIDTH \n     || y < 0\n     || y + SHORT(patch->height) > SCREENHEIGHT)\n    {\n        I_Error(\"Bad V_DrawTLPatch\");\n    }\n\n    col = 0;\n    desttop = dest_screen + y * SCREENWIDTH + x;\n\n    w = SHORT(patch->width);\n    for (; col < w; x++, col++, desttop++)\n    {\n        column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col]));\n\n        // step through the posts in a column\n\n        while (column->topdelta != 0xff)\n        {\n            source = (byte *) column + 3;\n            dest = desttop + column->topdelta * SCREENWIDTH;\n            count = column->length;\n\n            while (count--)\n            {\n                *dest = tinttable[((*dest) << 8) + *source++];\n                dest += SCREENWIDTH;\n            }\n            column = (column_t *) ((byte *) column + column->length + 4);\n        }\n    }\n}\n\n//\n// V_DrawXlaPatch\n//\n// villsa [STRIFE] Masks a column based translucent masked pic to the screen.\n//\n\nvoid V_DrawXlaPatch(int x, int y, patch_t * patch)\n{\n    int count, col;\n    column_t *column;\n    byte *desttop, *dest, *source;\n    int w;\n\n    y -= SHORT(patch->topoffset);\n    x -= SHORT(patch->leftoffset);\n\n    if(patchclip_callback)\n    {\n        if(!patchclip_callback(patch, x, y))\n            return;\n    }\n\n    col = 0;\n    desttop = dest_screen + y * SCREENWIDTH + x;\n\n    w = SHORT(patch->width);\n    for(; col < w; x++, col++, desttop++)\n    {\n        column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col]));\n\n        // step through the posts in a column\n\n        while(column->topdelta != 0xff)\n        {\n            source = (byte *) column + 3;\n            dest = desttop + column->topdelta * SCREENWIDTH;\n            count = column->length;\n\n            while(count--)\n            {\n                *dest = xlatab[*dest + ((*source) << 8)];\n                source++;\n                dest += SCREENWIDTH;\n            }\n            column = (column_t *) ((byte *) column + column->length + 4);\n        }\n    }\n}\n\n//\n// V_DrawAltTLPatch\n//\n// Masks a column based translucent masked pic to the screen.\n//\n\nvoid V_DrawAltTLPatch(int x, int y, patch_t * patch)\n{\n    int count, col;\n    column_t *column;\n    byte *desttop, *dest, *source;\n    int w;\n\n    y -= SHORT(patch->topoffset);\n    x -= SHORT(patch->leftoffset);\n\n    if (x < 0\n     || x + SHORT(patch->width) > SCREENWIDTH\n     || y < 0\n     || y + SHORT(patch->height) > SCREENHEIGHT)\n    {\n        I_Error(\"Bad V_DrawAltTLPatch\");\n    }\n\n    col = 0;\n    desttop = dest_screen + y * SCREENWIDTH + x;\n\n    w = SHORT(patch->width);\n    for (; col < w; x++, col++, desttop++)\n    {\n        column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col]));\n\n        // step through the posts in a column\n\n        while (column->topdelta != 0xff)\n        {\n            source = (byte *) column + 3;\n            dest = desttop + column->topdelta * SCREENWIDTH;\n            count = column->length;\n\n            while (count--)\n            {\n                *dest = tinttable[((*dest) << 8) + *source++];\n                dest += SCREENWIDTH;\n            }\n            column = (column_t *) ((byte *) column + column->length + 4);\n        }\n    }\n}\n\n//\n// V_DrawShadowedPatch\n//\n// Masks a column based masked pic to the screen.\n//\n\nvoid V_DrawShadowedPatch(int x, int y, patch_t *patch)\n{\n    int count, col;\n    column_t *column;\n    byte *desttop, *dest, *source;\n    byte *desttop2, *dest2;\n    int w;\n\n    y -= SHORT(patch->topoffset);\n    x -= SHORT(patch->leftoffset);\n\n    if (x < 0\n     || x + SHORT(patch->width) > SCREENWIDTH\n     || y < 0\n     || y + SHORT(patch->height) > SCREENHEIGHT)\n    {\n        I_Error(\"Bad V_DrawShadowedPatch\");\n    }\n\n    col = 0;\n    desttop = dest_screen + y * SCREENWIDTH + x;\n    desttop2 = dest_screen + (y + 2) * SCREENWIDTH + x + 2;\n\n    w = SHORT(patch->width);\n    for (; col < w; x++, col++, desttop++, desttop2++)\n    {\n        column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col]));\n\n        // step through the posts in a column\n\n        while (column->topdelta != 0xff)\n        {\n            source = (byte *) column + 3;\n            dest = desttop + column->topdelta * SCREENWIDTH;\n            dest2 = desttop2 + column->topdelta * SCREENWIDTH;\n            count = column->length;\n\n            while (count--)\n            {\n                *dest2 = tinttable[((*dest2) << 8)];\n                dest2 += SCREENWIDTH;\n                *dest = *source++;\n                dest += SCREENWIDTH;\n\n            }\n            column = (column_t *) ((byte *) column + column->length + 4);\n        }\n    }\n}\n\n//\n// Load tint table from TINTTAB lump.\n//\n\nvoid V_LoadTintTable(void)\n{\n    tinttable = W_CacheLumpName(\"TINTTAB\", PU_STATIC);\n}\n\n//\n// V_LoadXlaTable\n//\n// villsa [STRIFE] Load xla table from XLATAB lump.\n//\n\nvoid V_LoadXlaTable(void)\n{\n    xlatab = W_CacheLumpName(\"XLATAB\", PU_STATIC);\n}\n\n//\n// V_DrawBlock\n// Draw a linear block of pixels into the view buffer.\n//\n\nvoid V_DrawBlock(int x, int y, int width, int height, byte *src) \n{ \n    byte *dest; \n \n#ifdef RANGECHECK \n    if (x < 0\n     || x + width >SCREENWIDTH\n     || y < 0\n     || y + height > SCREENHEIGHT)\n    {\n\tI_Error (\"Bad V_DrawBlock\");\n    }\n#endif \n \n    V_MarkRect (x, y, width, height); \n \n    dest = dest_screen + y * SCREENWIDTH + x; \n\n    while (height--) \n    { \n\tmemcpy (dest, src, width); \n\tsrc += width; \n\tdest += SCREENWIDTH; \n    } \n} \n\nvoid V_DrawFilledBox(int x, int y, int w, int h, int c)\n{\n    uint8_t *buf, *buf1;\n    int x1, y1;\n\n    buf = I_VideoBuffer + SCREENWIDTH * y + x;\n\n    for (y1 = 0; y1 < h; ++y1)\n    {\n        buf1 = buf;\n\n        for (x1 = 0; x1 < w; ++x1)\n        {\n            *buf1++ = c;\n        }\n\n        buf += SCREENWIDTH;\n    }\n}\n\nvoid V_DrawHorizLine(int x, int y, int w, int c)\n{\n    uint8_t *buf;\n    int x1;\n\n    buf = I_VideoBuffer + SCREENWIDTH * y + x;\n\n    for (x1 = 0; x1 < w; ++x1)\n    {\n        *buf++ = c;\n    }\n}\n\nvoid V_DrawVertLine(int x, int y, int h, int c)\n{\n    uint8_t *buf;\n    int y1;\n\n    buf = I_VideoBuffer + SCREENWIDTH * y + x;\n\n    for (y1 = 0; y1 < h; ++y1)\n    {\n        *buf = c;\n        buf += SCREENWIDTH;\n    }\n}\n\nvoid V_DrawBox(int x, int y, int w, int h, int c)\n{\n    V_DrawHorizLine(x, y, w, c);\n    V_DrawHorizLine(x, y+h-1, w, c);\n    V_DrawVertLine(x, y, h, c);\n    V_DrawVertLine(x+w-1, y, h, c);\n}\n\n//\n// Draw a \"raw\" screen (lump containing raw data to blit directly\n// to the screen)\n//\n \nvoid V_DrawRawScreen(byte *raw)\n{\n    memcpy(dest_screen, raw, SCREENWIDTH * SCREENHEIGHT);\n}\n\n//\n// V_Init\n// \nvoid V_Init (void) \n{ \n    // no-op!\n    // There used to be separate screens that could be drawn to; these are\n    // now handled in the upper layers.\n}\n\n// Set the buffer that the code draws to.\n\nvoid V_UseBuffer(byte *buffer)\n{\n    dest_screen = buffer;\n}\n\n// Restore screen buffer to the i_video screen buffer.\n\nvoid V_RestoreBuffer(void)\n{\n    dest_screen = I_VideoBuffer;\n}\n\n//\n// SCREEN SHOTS\n//\n\ntypedef struct\n{\n    char\t\tmanufacturer;\n    char\t\tversion;\n    char\t\tencoding;\n    char\t\tbits_per_pixel;\n\n    unsigned short\txmin;\n    unsigned short\tymin;\n    unsigned short\txmax;\n    unsigned short\tymax;\n    \n    unsigned short\thres;\n    unsigned short\tvres;\n\n    unsigned char\tpalette[48];\n    \n    char\t\treserved;\n    char\t\tcolor_planes;\n    unsigned short\tbytes_per_line;\n    unsigned short\tpalette_type;\n    \n    char\t\tfiller[58];\n    unsigned char\tdata;\t\t// unbounded\n} PACKEDATTR pcx_t;\n\n\n//\n// WritePCXfile\n//\n\nvoid WritePCXfile(char *filename, byte *data,\n                  int width, int height,\n                  byte *palette)\n{\n    int\t\ti;\n    int\t\tlength;\n    pcx_t*\tpcx;\n    byte*\tpack;\n\t\n    pcx = Z_Malloc (width*height*2+1000, PU_STATIC, NULL);\n\n    pcx->manufacturer = 0x0a;\t\t// PCX id\n    pcx->version = 5;\t\t\t// 256 color\n    pcx->encoding = 1;\t\t\t// uncompressed\n    pcx->bits_per_pixel = 8;\t\t// 256 color\n    pcx->xmin = 0;\n    pcx->ymin = 0;\n    pcx->xmax = SHORT(width-1);\n    pcx->ymax = SHORT(height-1);\n    pcx->hres = SHORT(width);\n    pcx->vres = SHORT(height);\n    memset (pcx->palette,0,sizeof(pcx->palette));\n    pcx->color_planes = 1;\t\t// chunky image\n    pcx->bytes_per_line = SHORT(width);\n    pcx->palette_type = SHORT(2);\t// not a grey scale\n    memset (pcx->filler,0,sizeof(pcx->filler));\n\n    // pack the image\n    pack = &pcx->data;\n\t\n    for (i=0 ; i<width*height ; i++)\n    {\n\tif ( (*data & 0xc0) != 0xc0)\n\t    *pack++ = *data++;\n\telse\n\t{\n\t    *pack++ = 0xc1;\n\t    *pack++ = *data++;\n\t}\n    }\n    \n    // write the palette\n    *pack++ = 0x0c;\t// palette ID byte\n    for (i=0 ; i<768 ; i++)\n\t*pack++ = *palette++;\n    \n    // write output file\n    length = pack - (byte *)pcx;\n    M_WriteFile (filename, pcx, length);\n\n    Z_Free (pcx);\n}\n\n#ifdef HAVE_LIBPNG\n//\n// WritePNGfile\n//\n\nstatic void error_fn(png_structp p, png_const_charp s)\n{\n    printf(\"libpng error: %s\\n\", s);\n}\n\nstatic void warning_fn(png_structp p, png_const_charp s)\n{\n    printf(\"libpng warning: %s\\n\", s);\n}\n\nvoid WritePNGfile(char *filename, byte *data,\n                  int width, int height,\n                  byte *palette)\n{\n    png_structp ppng;\n    png_infop pinfo;\n    png_colorp pcolor;\n    FILE *handle;\n    int i;\n\n    handle = fopen(filename, \"wb\");\n    if (!handle)\n    {\n        return;\n    }\n\n    ppng = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,\n                                   error_fn, warning_fn);\n    if (!ppng)\n    {\n        return;\n    }\n\n    pinfo = png_create_info_struct(ppng);\n    if (!pinfo)\n    {\n        png_destroy_write_struct(&ppng, NULL);\n        return;\n    }\n\n    png_init_io(ppng, handle);\n\n    png_set_IHDR(ppng, pinfo, width, height,\n                 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,\n                 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);\n\n    pcolor = malloc(sizeof(*pcolor) * 256);\n    if (!pcolor)\n    {\n        png_destroy_write_struct(&ppng, &pinfo);\n        return;\n    }\n\n    for (i = 0; i < 256; i++)\n    {\n        pcolor[i].red   = *(palette + 3 * i);\n        pcolor[i].green = *(palette + 3 * i + 1);\n        pcolor[i].blue  = *(palette + 3 * i + 2);\n    }\n\n    png_set_PLTE(ppng, pinfo, pcolor, 256);\n    free(pcolor);\n\n    png_write_info(ppng, pinfo);\n\n    for (i = 0; i < SCREENHEIGHT; i++)\n    {\n        png_write_row(ppng, data + i*SCREENWIDTH);\n    }\n\n    png_write_end(ppng, pinfo);\n    png_destroy_write_struct(&ppng, &pinfo);\n    fclose(handle);\n}\n#endif\n\n//\n// V_ScreenShot\n//\n\nvoid V_ScreenShot(char *format)\n{\n    int i;\n    char lbmname[16]; // haleyjd 20110213: BUG FIX - 12 is too small!\n    char *ext;\n    \n    // find a file name to save it to\n\n#ifdef HAVE_LIBPNG\n    extern int png_screenshots;\n    if (png_screenshots)\n    {\n        ext = \"png\";\n    }\n    else\n#endif\n    {\n        ext = \"pcx\";\n    }\n\n    for (i=0; i<=99; i++)\n    {\n        M_snprintf(lbmname, sizeof(lbmname), format, i, ext);\n\n        if (!M_FileExists(lbmname))\n        {\n            break;      // file doesn't exist\n        }\n    }\n\n    if (i == 100)\n    {\n        I_Error (\"V_ScreenShot: Couldn't create a PCX\");\n    }\n\n#ifdef HAVE_LIBPNG\n    if (png_screenshots)\n    {\n    WritePNGfile(lbmname, I_VideoBuffer,\n                 SCREENWIDTH, SCREENHEIGHT,\n                 W_CacheLumpName (DEH_String(\"PLAYPAL\"), PU_CACHE));\n    }\n    else\n#endif\n    {\n    // save the pcx file\n    WritePCXfile(lbmname, I_VideoBuffer,\n                 SCREENWIDTH, SCREENHEIGHT,\n                 W_CacheLumpName (DEH_String(\"PLAYPAL\"), PU_CACHE));\n    }\n}\n\n#define MOUSE_SPEED_BOX_WIDTH  120\n#define MOUSE_SPEED_BOX_HEIGHT 9\n\nvoid V_DrawMouseSpeedBox(int speed)\n{\n    extern int usemouse;\n    int bgcolor, bordercolor, red, black, white, yellow;\n    int box_x, box_y;\n    int original_speed;\n    int redline_x;\n    int linelen;\n\n    // Get palette indices for colors for widget. These depend on the\n    // palette of the game being played.\n\n    bgcolor = I_GetPaletteIndex(0x77, 0x77, 0x77);\n    bordercolor = I_GetPaletteIndex(0x55, 0x55, 0x55);\n    red = I_GetPaletteIndex(0xff, 0x00, 0x00);\n    black = I_GetPaletteIndex(0x00, 0x00, 0x00);\n    yellow = I_GetPaletteIndex(0xff, 0xff, 0x00);\n    white = I_GetPaletteIndex(0xff, 0xff, 0xff);\n\n    // If the mouse is turned off or acceleration is turned off, don't\n    // draw the box at all.\n\n    if (!usemouse || fabs(mouse_acceleration - 1) < 0.01)\n    {\n        return;\n    }\n\n    // Calculate box position\n\n    box_x = SCREENWIDTH - MOUSE_SPEED_BOX_WIDTH - 10;\n    box_y = 15;\n\n    V_DrawFilledBox(box_x, box_y,\n                    MOUSE_SPEED_BOX_WIDTH, MOUSE_SPEED_BOX_HEIGHT, bgcolor);\n    V_DrawBox(box_x, box_y,\n              MOUSE_SPEED_BOX_WIDTH, MOUSE_SPEED_BOX_HEIGHT, bordercolor);\n\n    // Calculate the position of the red line.  This is 1/3 of the way\n    // along the box.\n\n    redline_x = MOUSE_SPEED_BOX_WIDTH / 3;\n\n    // Undo acceleration and get back the original mouse speed\n\n    if (speed < mouse_threshold)\n    {\n        original_speed = speed;\n    }\n    else\n    {\n        original_speed = speed - mouse_threshold;\n        original_speed = (int) (original_speed / mouse_acceleration);\n        original_speed += mouse_threshold;\n    }\n\n    // Calculate line length\n\n    linelen = (original_speed * redline_x) / mouse_threshold;\n\n    // Draw horizontal \"thermometer\" \n\n    if (linelen > MOUSE_SPEED_BOX_WIDTH - 1)\n    {\n        linelen = MOUSE_SPEED_BOX_WIDTH - 1;\n    }\n\n    V_DrawHorizLine(box_x + 1, box_y + 4, MOUSE_SPEED_BOX_WIDTH - 2, black);\n\n    if (linelen < redline_x)\n    {\n        V_DrawHorizLine(box_x + 1, box_y + MOUSE_SPEED_BOX_HEIGHT / 2,\n                      linelen, white);\n    }\n    else\n    {\n        V_DrawHorizLine(box_x + 1, box_y + MOUSE_SPEED_BOX_HEIGHT / 2,\n                        redline_x, white);\n        V_DrawHorizLine(box_x + redline_x, box_y + MOUSE_SPEED_BOX_HEIGHT / 2,\n                        linelen - redline_x, yellow);\n    }\n\n    // Draw red line\n\n    V_DrawVertLine(box_x + redline_x, box_y + 1,\n                 MOUSE_SPEED_BOX_HEIGHT - 2, red);\n}\n\n"
  },
  {
    "path": "fbdoom/v_video.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tGamma correction LUT.\n//\tFunctions to draw patches (by post) directly to screen.\n//\tFunctions to blit a block to the screen.\n//\n\n\n#ifndef __V_VIDEO__\n#define __V_VIDEO__\n\n#include \"doomtype.h\"\n\n// Needed because we are refering to patches.\n#include \"v_patch.h\"\n\n//\n// VIDEO\n//\n\n#define CENTERY\t\t\t(SCREENHEIGHT/2)\n\n\nextern int dirtybox[4];\n\nextern byte *tinttable;\n\n// haleyjd 08/28/10: implemented for Strife support\n// haleyjd 08/28/10: Patch clipping callback, implemented to support Choco\n// Strife.\ntypedef boolean (*vpatchclipfunc_t)(patch_t *, int, int);\nvoid V_SetPatchClipCallback(vpatchclipfunc_t func);\n\n\n// Allocates buffer screens, call before R_Init.\nvoid V_Init (void);\n\n// Draw a block from the specified source screen to the screen.\n\nvoid V_CopyRect(int srcx, int srcy, byte *source,\n                int width, int height,\n                int destx, int desty);\n\nvoid V_DrawPatch(int x, int y, patch_t *patch);\nvoid V_DrawPatchFlipped(int x, int y, patch_t *patch);\nvoid V_DrawTLPatch(int x, int y, patch_t *patch);\nvoid V_DrawAltTLPatch(int x, int y, patch_t * patch);\nvoid V_DrawShadowedPatch(int x, int y, patch_t *patch);\nvoid V_DrawXlaPatch(int x, int y, patch_t * patch);     // villsa [STRIFE]\nvoid V_DrawPatchDirect(int x, int y, patch_t *patch);\n\n// Draw a linear block of pixels into the view buffer.\n\nvoid V_DrawBlock(int x, int y, int width, int height, byte *src);\n\nvoid V_MarkRect(int x, int y, int width, int height);\n\nvoid V_DrawFilledBox(int x, int y, int w, int h, int c);\nvoid V_DrawHorizLine(int x, int y, int w, int c);\nvoid V_DrawVertLine(int x, int y, int h, int c);\nvoid V_DrawBox(int x, int y, int w, int h, int c);\n\n// Draw a raw screen lump\n\nvoid V_DrawRawScreen(byte *raw);\n\n// Temporarily switch to using a different buffer to draw graphics, etc.\n\nvoid V_UseBuffer(byte *buffer);\n\n// Return to using the normal screen buffer to draw graphics.\n\nvoid V_RestoreBuffer(void);\n\n// Save a screenshot of the current screen to a file, named in the \n// format described in the string passed to the function, eg.\n// \"DOOM%02i.pcx\"\n\nvoid V_ScreenShot(char *format);\n\n// Load the lookup table for translucency calculations from the TINTTAB\n// lump.\n\nvoid V_LoadTintTable(void);\n\n// villsa [STRIFE]\n// Load the lookup table for translucency calculations from the XLATAB\n// lump.\n\nvoid V_LoadXlaTable(void);\n\nvoid V_DrawMouseSpeedBox(int speed);\n\n#endif\n\n"
  },
  {
    "path": "fbdoom/w_checksum.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//       Generate a checksum of the WAD directory.\n//\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"m_misc.h\"\n#include \"sha1.h\"\n#include \"w_checksum.h\"\n#include \"w_wad.h\"\n\nstatic wad_file_t **open_wadfiles = NULL;\nstatic int num_open_wadfiles = 0;\n\nstatic int GetFileNumber(wad_file_t *handle)\n{\n    int i;\n    int result;\n\n    for (i=0; i<num_open_wadfiles; ++i)\n    {\n        if (open_wadfiles[i] == handle)\n        {\n            return i;\n        }\n    }\n\n    // Not found in list.  This is a new file we haven't seen yet.\n    // Allocate another slot for this file.\n\n    open_wadfiles = realloc(open_wadfiles,\n                            sizeof(wad_file_t *) * (num_open_wadfiles + 1));\n    open_wadfiles[num_open_wadfiles] = handle;\n\n    result = num_open_wadfiles;\n    ++num_open_wadfiles;\n\n    return result;\n}\n\nstatic void ChecksumAddLump(sha1_context_t *sha1_context, lumpinfo_t *lump)\n{\n    char buf[9];\n\n    M_StringCopy(buf, lump->name, sizeof(buf));\n    SHA1_UpdateString(sha1_context, buf);\n    SHA1_UpdateInt32(sha1_context, GetFileNumber(lump->wad_file));\n    SHA1_UpdateInt32(sha1_context, lump->position);\n    SHA1_UpdateInt32(sha1_context, lump->size);\n}\n\nvoid W_Checksum(sha1_digest_t digest)\n{\n    sha1_context_t sha1_context;\n    unsigned int i;\n\n    SHA1_Init(&sha1_context);\n\n    num_open_wadfiles = 0;\n\n    // Go through each entry in the WAD directory, adding information\n    // about each entry to the SHA1 hash.\n\n    for (i=0; i<numlumps; ++i)\n    {\n        ChecksumAddLump(&sha1_context, &lumpinfo[i]);\n    }\n    \n    SHA1_Final(digest, &sha1_context);\n}\n\n"
  },
  {
    "path": "fbdoom/w_checksum.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//       Generate a checksum of the WAD directory.\n//\n\n#ifndef W_CHECKSUM_H\n#define W_CHECKSUM_H\n\n#include \"doomtype.h\"\n\nextern void W_Checksum(sha1_digest_t digest);\n\n#endif /* #ifndef W_CHECKSUM_H */\n\n"
  },
  {
    "path": "fbdoom/w_file.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tWAD I/O functions.\n//\n\n#include <stdio.h>\n\n#include \"config.h\"\n\n#include \"doomtype.h\"\n#include \"m_argv.h\"\n\n#include \"w_file.h\"\n\nextern wad_file_class_t stdc_wad_file;\n\n#ifdef _WIN32\nextern wad_file_class_t win32_wad_file;\n#endif\n\n#ifdef HAVE_MMAP\nextern wad_file_class_t posix_wad_file;\n#endif \n\nstatic wad_file_class_t *wad_file_classes[] = \n{\n#ifdef _WIN32\n    &win32_wad_file,\n#endif\n#ifdef HAVE_MMAP\n    &posix_wad_file,\n#endif\n    &stdc_wad_file,\n};\n\nwad_file_t *W_OpenFile(char *path)\n{\n    wad_file_t *result;\n    int i;\n\n    //!\n    // Use the OS's virtual memory subsystem to map WAD files\n    // directly into memory.\n    //\n\n    if (!M_CheckParm(\"-mmap\"))\n    {\n        return stdc_wad_file.OpenFile(path);\n    }\n\n    // Try all classes in order until we find one that works\n\n    result = NULL;\n\n    for (i = 0; i < arrlen(wad_file_classes); ++i)\n    {\n        result = wad_file_classes[i]->OpenFile(path);\n\n        if (result != NULL)\n        {\n            break;\n        }\n    }\n\n    return result;\n}\n\nvoid W_CloseFile(wad_file_t *wad)\n{\n    wad->file_class->CloseFile(wad);\n}\n\nsize_t W_Read(wad_file_t *wad, unsigned int offset,\n              void *buffer, size_t buffer_len)\n{\n    return wad->file_class->Read(wad, offset, buffer, buffer_len);\n}\n\n"
  },
  {
    "path": "fbdoom/w_file.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tWAD I/O functions.\n//\n\n\n#ifndef __W_FILE__\n#define __W_FILE__\n\n#include <stdio.h>\n#include \"doomtype.h\"\n\ntypedef struct _wad_file_s wad_file_t;\n\ntypedef struct\n{\n    // Open a file for reading.\n\n    wad_file_t *(*OpenFile)(char *path);\n\n    // Close the specified file.\n\n    void (*CloseFile)(wad_file_t *file);\n\n    // Read data from the specified position in the file into the \n    // provided buffer.  Returns the number of bytes read.\n\n    size_t (*Read)(wad_file_t *file, unsigned int offset,\n                   void *buffer, size_t buffer_len);\n\n} wad_file_class_t;\n\nstruct _wad_file_s\n{\n    // Class of this file.\n\n    wad_file_class_t *file_class;\n\n    // If this is NULL, the file cannot be mapped into memory.  If this\n    // is non-NULL, it is a pointer to the mapped file.\n\n    byte *mapped;\n\n    // Length of the file, in bytes.\n\n    unsigned int length;\n};\n\n// Open the specified file. Returns a pointer to a new wad_file_t \n// handle for the WAD file, or NULL if it could not be opened.\n\nwad_file_t *W_OpenFile(char *path);\n\n// Close the specified WAD file.\n\nvoid W_CloseFile(wad_file_t *wad);\n\n// Read data from the specified file into the provided buffer.  The\n// data is read from the specified offset from the start of the file.\n// Returns the number of bytes read.\n\nsize_t W_Read(wad_file_t *wad, unsigned int offset,\n              void *buffer, size_t buffer_len);\n\n#endif /* #ifndef __W_FILE__ */\n"
  },
  {
    "path": "fbdoom/w_file_stdc.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tWAD I/O functions.\n//\n\n#include <stdio.h>\n\n#include \"m_misc.h\"\n#include \"w_file.h\"\n#include \"z_zone.h\"\n\ntypedef struct\n{\n    wad_file_t wad;\n    FILE *fstream;\n} stdc_wad_file_t;\n\nextern wad_file_class_t stdc_wad_file;\n\nstatic wad_file_t *W_StdC_OpenFile(char *path)\n{\n    stdc_wad_file_t *result;\n    FILE *fstream;\n\n    fstream = fopen(path, \"rb\");\n\n    if (fstream == NULL)\n    {\n        return NULL;\n    }\n\n    // Create a new stdc_wad_file_t to hold the file handle.\n\n    result = Z_Malloc(sizeof(stdc_wad_file_t), PU_STATIC, 0);\n    result->wad.file_class = &stdc_wad_file;\n    result->wad.mapped = NULL;\n    result->wad.length = M_FileLength(fstream);\n    result->fstream = fstream;\n\n    return &result->wad;\n}\n\nstatic void W_StdC_CloseFile(wad_file_t *wad)\n{\n    stdc_wad_file_t *stdc_wad;\n\n    stdc_wad = (stdc_wad_file_t *) wad;\n\n    fclose(stdc_wad->fstream);\n    Z_Free(stdc_wad);\n}\n\n// Read data from the specified position in the file into the \n// provided buffer.  Returns the number of bytes read.\n\nsize_t W_StdC_Read(wad_file_t *wad, unsigned int offset,\n                   void *buffer, size_t buffer_len)\n{\n    stdc_wad_file_t *stdc_wad;\n    size_t result;\n\n    stdc_wad = (stdc_wad_file_t *) wad;\n\n    // Jump to the specified position in the file.\n\n    fseek(stdc_wad->fstream, offset, SEEK_SET);\n\n    // Read into the buffer.\n\n    result = fread(buffer, 1, buffer_len, stdc_wad->fstream);\n\n    return result;\n}\n\n\nwad_file_class_t stdc_wad_file = \n{\n    W_StdC_OpenFile,\n    W_StdC_CloseFile,\n    W_StdC_Read,\n};\n\n\n"
  },
  {
    "path": "fbdoom/w_file_stdc_unbuffered.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tWAD I/O functions.\n//\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <unistd.h>\n\n#include \"m_misc.h\"\n#include \"w_file.h\"\n#include \"z_zone.h\"\n\ntypedef struct\n{\n    wad_file_t wad;\n    int fd;\n} stdc_wad_file_t;\n\nextern wad_file_class_t stdc_wad_file;\n\nstatic unsigned int W_StdC_FileLength(int fd)\n{\n    long savedpos;\n    long length;\n\n    // save the current position in the file\n    savedpos = lseek(fd, 0, SEEK_CUR);\n    \n    // jump to the end and find the length\n    length = lseek(fd, 0, SEEK_END);\n\n    // go back to the old location\n    lseek(fd, savedpos, SEEK_SET);\n\n    return length;\n}\n\nstatic wad_file_t *W_StdC_OpenFile(char *path)\n{\n    stdc_wad_file_t *result;\n    int fd;\n\n    fd = open(path, O_RDONLY);\n\n    if (fd <= 0)\n    {\n        return NULL;\n    }\n\n    // Create a new stdc_wad_file_t to hold the file handle.\n\n    result = Z_Malloc(sizeof(stdc_wad_file_t), PU_STATIC, 0);\n    result->wad.file_class = &stdc_wad_file;\n    result->wad.mapped = NULL;\n    result->wad.length = W_StdC_FileLength(fd);\n    result->fd = fd;\n\n    return &result->wad;\n}\n\nstatic void W_StdC_CloseFile(wad_file_t *wad)\n{\n    stdc_wad_file_t *stdc_wad;\n\n    stdc_wad = (stdc_wad_file_t *) wad;\n\n    close(stdc_wad->fd);\n    Z_Free(stdc_wad);\n}\n\n// Read data from the specified position in the file into the \n// provided buffer.  Returns the number of bytes read.\n\nsize_t W_StdC_Read(wad_file_t *wad, unsigned int offset,\n                   void *buffer, size_t buffer_len)\n{\n    stdc_wad_file_t *stdc_wad;\n    size_t result = 0;\n    int rc;\n\n    stdc_wad = (stdc_wad_file_t *) wad;\n\n    // Jump to the specified position in the file.\n\n    lseek(stdc_wad->fd, offset, SEEK_SET);\n\n    // Read into the buffer.\n\n    do {\n\n        rc = read(stdc_wad->fd, (char *)buffer + result, buffer_len - result);\n\n        if (rc <= 0) {\n\n            if (rc < 0 && errno == EINTR)\n                continue;\n\n            break;\n        }\n\n        result += rc;\n\n    } while (result < buffer_len);\n\n    return result;\n}\n\n\nwad_file_class_t stdc_wad_file = \n{\n    W_StdC_OpenFile,\n    W_StdC_CloseFile,\n    W_StdC_Read,\n};\n\n\n"
  },
  {
    "path": "fbdoom/w_main.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     Common code to parse command line, identifying WAD files to load.\n//\n\n#include \"doomfeatures.h\"\n#include \"d_iwad.h\"\n#include \"m_argv.h\"\n#include \"w_main.h\"\n#include \"w_merge.h\"\n#include \"w_wad.h\"\n#include \"z_zone.h\"\n\n// Parse the command line, merging WAD files that are sppecified.\n// Returns true if at least one file was added.\n\nboolean W_ParseCommandLine(void)\n{\n    boolean modifiedgame = false;\n    int p;\n\n#ifdef FEATURE_WAD_MERGE\n\n    // Merged PWADs are loaded first, because they are supposed to be \n    // modified IWADs.\n\n    //!\n    // @arg <files>\n    // @category mod\n    //\n    // Simulates the behavior of deutex's -merge option, merging a PWAD\n    // into the main IWAD.  Multiple files may be specified.\n    //\n\n    p = M_CheckParmWithArgs(\"-merge\", 1);\n\n    if (p > 0)\n    {\n        for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)\n        {\n            char *filename;\n\n            modifiedgame = true;\n\n            filename = D_TryFindWADByName(myargv[p]);\n\n            printf(\" merging %s\\n\", filename);\n            W_MergeFile(filename);\n        }\n    }\n\n    // NWT-style merging:\n\n    // NWT's -merge option:\n\n    //!\n    // @arg <files>\n    // @category mod\n    //\n    // Simulates the behavior of NWT's -merge option.  Multiple files\n    // may be specified.\n\n    p = M_CheckParmWithArgs(\"-nwtmerge\", 1);\n\n    if (p > 0)\n    {\n        for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)\n        {\n            char *filename;\n\n            modifiedgame = true;\n\n            filename = D_TryFindWADByName(myargv[p]);\n\n            printf(\" performing NWT-style merge of %s\\n\", filename);\n            W_NWTDashMerge(filename);\n        }\n    }\n    \n    // Add flats\n\n    //!\n    // @arg <files>\n    // @category mod\n    //\n    // Simulates the behavior of NWT's -af option, merging flats into\n    // the main IWAD directory.  Multiple files may be specified.\n    //\n\n    p = M_CheckParmWithArgs(\"-af\", 1);\n\n    if (p > 0)\n    {\n        for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)\n        {\n            char *filename;\n\n            modifiedgame = true;\n\n            filename = D_TryFindWADByName(myargv[p]);\n\n            printf(\" merging flats from %s\\n\", filename);\n            W_NWTMergeFile(filename, W_NWT_MERGE_FLATS);\n        }\n    }\n\n    //!\n    // @arg <files>\n    // @category mod\n    //\n    // Simulates the behavior of NWT's -as option, merging sprites\n    // into the main IWAD directory.  Multiple files may be specified.\n    //\n\n    p = M_CheckParmWithArgs(\"-as\", 1);\n\n    if (p > 0)\n    {\n        for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)\n        {\n            char *filename;\n\n            modifiedgame = true;\n            filename = D_TryFindWADByName(myargv[p]);\n\n            printf(\" merging sprites from %s\\n\", filename);\n            W_NWTMergeFile(filename, W_NWT_MERGE_SPRITES);\n        }\n    }\n\n    //!\n    // @arg <files>\n    // @category mod\n    //\n    // Equivalent to \"-af <files> -as <files>\".\n    //\n\n    p = M_CheckParmWithArgs(\"-aa\", 1);\n\n    if (p > 0)\n    {\n        for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)\n        {\n            char *filename;\n\n            modifiedgame = true;\n\n            filename = D_TryFindWADByName(myargv[p]);\n\n            printf(\" merging sprites and flats from %s\\n\", filename);\n            W_NWTMergeFile(filename, W_NWT_MERGE_SPRITES | W_NWT_MERGE_FLATS);\n        }\n    }\n\n#endif\n\n    //!\n    // @arg <files>\n    // @vanilla\n    //\n    // Load the specified PWAD files.\n    //\n\n    p = M_CheckParmWithArgs (\"-file\", 1);\n    if (p)\n    {\n\t// the parms after p are wadfile/lump names,\n\t// until end of parms or another - preceded parm\n\tmodifiedgame = true;            // homebrew levels\n\twhile (++p != myargc && myargv[p][0] != '-')\n        {\n            char *filename;\n\n            filename = D_TryFindWADByName(myargv[p]);\n\n            printf(\" adding %s\\n\", filename);\n\t    W_AddFile(filename);\n        }\n    }\n\n//    W_PrintDirectory();\n\n    return modifiedgame;\n}\n\n"
  },
  {
    "path": "fbdoom/w_main.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//     Common code to parse command line, identifying WAD files to load.\n//\n\n#ifndef W_MAIN_H\n#define W_MAIN_H\n\nboolean W_ParseCommandLine(void);\n\n#endif /* #ifndef W_MAIN_H */\n\n"
  },
  {
    "path": "fbdoom/w_merge.h",
    "content": "//\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n// Handles merging of PWADs, similar to deutex's -merge option\n//\n// Ideally this should work exactly the same as in deutex, but trying to\n// read the deutex source code made my brain hurt.\n//\n\n#ifndef W_MERGE_H\n#define W_MERGE_H\n\n#define W_NWT_MERGE_SPRITES   0x1\n#define W_NWT_MERGE_FLATS     0x2\n\n// Add a new WAD and merge it into the main directory\n\nvoid W_MergeFile(char *filename);\n\n// NWT-style merging\n\nvoid W_NWTMergeFile(char *filename, int flags);\n\n// Acts the same as NWT's \"-merge\" option.\n\nvoid W_NWTDashMerge(char *filename);\n\n// Debug function that prints the WAD directory.\n\nvoid W_PrintDirectory(void);\n\n#endif /* #ifndef W_MERGE_H */\n\n"
  },
  {
    "path": "fbdoom/w_wad.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tHandles WAD file header, directory, lump I/O.\n//\n\n\n\n\n#include <ctype.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"doomtype.h\"\n\n#include \"config.h\"\n#include \"d_iwad.h\"\n#include \"i_swap.h\"\n#include \"i_system.h\"\n#include \"i_video.h\"\n#include \"m_misc.h\"\n#include \"z_zone.h\"\n\n#include \"w_wad.h\"\n\ntypedef struct\n{\n    // Should be \"IWAD\" or \"PWAD\".\n    char\t\tidentification[4];\t\t\n    int\t\t\tnumlumps;\n    int\t\t\tinfotableofs;\n} PACKEDATTR wadinfo_t;\n\n\ntypedef struct\n{\n    int\t\t\tfilepos;\n    int\t\t\tsize;\n    char\t\tname[8];\n} PACKEDATTR filelump_t;\n\n//\n// GLOBALS\n//\n\n// Location of each lump on disk.\n\nlumpinfo_t *lumpinfo;\t\t\nunsigned int numlumps = 0;\n\n// Hash table for fast lookups\n\nstatic lumpinfo_t **lumphash;\n\n// Hash function used for lump names.\n\nunsigned int W_LumpNameHash(const char *s)\n{\n    // This is the djb2 string hash function, modded to work on strings\n    // that have a maximum length of 8.\n\n    unsigned int result = 5381;\n    unsigned int i;\n\n    for (i=0; i < 8 && s[i] != '\\0'; ++i)\n    {\n        result = ((result << 5) ^ result ) ^ toupper((int)s[i]);\n    }\n\n    return result;\n}\n\n// Increase the size of the lumpinfo[] array to the specified size.\nstatic void ExtendLumpInfo(int newnumlumps)\n{\n    lumpinfo_t *newlumpinfo;\n    unsigned int i;\n\n    newlumpinfo = calloc(newnumlumps, sizeof(lumpinfo_t));\n\n    if (newlumpinfo == NULL)\n    {\n\tI_Error (\"Couldn't realloc lumpinfo\");\n    }\n\n    // Copy over lumpinfo_t structures from the old array. If any of\n    // these lumps have been cached, we need to update the user\n    // pointers to the new location.\n    for (i = 0; i < numlumps && i < newnumlumps; ++i)\n    {\n        memcpy(&newlumpinfo[i], &lumpinfo[i], sizeof(lumpinfo_t));\n\n        if (newlumpinfo[i].cache != NULL)\n        {\n            Z_ChangeUser(newlumpinfo[i].cache, &newlumpinfo[i].cache);\n        }\n\n        // We shouldn't be generating a hash table until after all WADs have\n        // been loaded, but just in case...\n        if (lumpinfo[i].next != NULL)\n        {\n            int nextlumpnum = lumpinfo[i].next - lumpinfo;\n            newlumpinfo[i].next = &newlumpinfo[nextlumpnum];\n        }\n    }\n\n    // All done.\n    free(lumpinfo);\n    lumpinfo = newlumpinfo;\n    numlumps = newnumlumps;\n}\n\n//\n// LUMP BASED ROUTINES.\n//\n\n//\n// W_AddFile\n// All files are optional, but at least one file must be\n//  found (PWAD, if all required lumps are present).\n// Files with a .wad extension are wadlink files\n//  with multiple lumps.\n// Other files are single lumps with the base filename\n//  for the lump name.\n\nwad_file_t *W_AddFile (char *filename)\n{\n    wadinfo_t header;\n    lumpinfo_t *lump_p;\n    unsigned int i;\n    wad_file_t *wad_file;\n    int length;\n    int startlump;\n    filelump_t *fileinfo;\n    filelump_t *filerover;\n    int newnumlumps;\n\n    // open the file and add to directory\n\n    wad_file = W_OpenFile(filename);\n\n    if (wad_file == NULL)\n    {\n\t\tprintf (\" couldn't open %s\\n\", filename);\n\t\treturn NULL;\n    }\n\n    newnumlumps = numlumps;\n\n    if (strcasecmp(filename+strlen(filename)-3 , \"wad\" ) )\n    {\n    \t// single lump file\n\n        // fraggle: Swap the filepos and size here.  The WAD directory\n        // parsing code expects a little-endian directory, so will swap\n        // them back.  Effectively we're constructing a \"fake WAD directory\"\n        // here, as it would appear on disk.\n\n\t\tfileinfo = Z_Malloc(sizeof(filelump_t), PU_STATIC, 0);\n\t\tfileinfo->filepos = LONG(0);\n\t\tfileinfo->size = LONG(wad_file->length);\n\n        // Name the lump after the base of the filename (without the\n        // extension).\n\n\t\tM_ExtractFileBase (filename, fileinfo->name);\n\t\tnewnumlumps++;\n    }\n    else \n    {\n    \t// WAD file\n        W_Read(wad_file, 0, &header, sizeof(header));\n\n\t\tif (strncmp(header.identification,\"IWAD\",4))\n\t\t{\n\t\t\t// Homebrew levels?\n\t\t\tif (strncmp(header.identification,\"PWAD\",4))\n\t\t\t{\n\t\t\tI_Error (\"Wad file %s doesn't have IWAD \"\n\t\t\t\t \"or PWAD id\\n\", filename);\n\t\t\t}\n\n\t\t\t// ???modifiedgame = true;\n\t\t}\n\n\t\theader.numlumps = LONG(header.numlumps);\n\t\theader.infotableofs = LONG(header.infotableofs);\n\t\tlength = header.numlumps*sizeof(filelump_t);\n\t\tfileinfo = Z_Malloc(length, PU_STATIC, 0);\n\n        W_Read(wad_file, header.infotableofs, fileinfo, length);\n        newnumlumps += header.numlumps;\n    }\n\n    // Increase size of numlumps array to accomodate the new file.\n    startlump = numlumps;\n    ExtendLumpInfo(newnumlumps);\n\n    lump_p = &lumpinfo[startlump];\n\n    filerover = fileinfo;\n\n    for (i=startlump; i<numlumps; ++i)\n    {\n\t\tlump_p->wad_file = wad_file;\n\t\tlump_p->position = LONG(filerover->filepos);\n\t\tlump_p->size = LONG(filerover->size);\n\t\t\tlump_p->cache = NULL;\n\t\tstrncpy(lump_p->name, filerover->name, 8);\n\n\t\t\t++lump_p;\n\t\t\t++filerover;\n    }\n\n    Z_Free(fileinfo);\n\n    if (lumphash != NULL)\n    {\n        Z_Free(lumphash);\n        lumphash = NULL;\n    }\n\n    return wad_file;\n}\n\n\n\n//\n// W_NumLumps\n//\nint W_NumLumps (void)\n{\n    return numlumps;\n}\n\n\n\n//\n// W_CheckNumForName\n// Returns -1 if name not found.\n//\n\nint W_CheckNumForName (char* name)\n{\n    lumpinfo_t *lump_p;\n    int i;\n\n    // Do we have a hash table yet?\n\n    if (lumphash != NULL)\n    {\n        int hash;\n        \n        // We do! Excellent.\n\n        hash = W_LumpNameHash(name) % numlumps;\n        \n        for (lump_p = lumphash[hash]; lump_p != NULL; lump_p = lump_p->next)\n        {\n            if (!strncasecmp(lump_p->name, name, 8))\n            {\n                return lump_p - lumpinfo;\n            }\n        }\n    } \n    else\n    {\n        // We don't have a hash table generate yet. Linear search :-(\n        // \n        // scan backwards so patch lump files take precedence\n\n        for (i=numlumps-1; i >= 0; --i)\n        {\n            if (!strncasecmp(lumpinfo[i].name, name, 8))\n            {\n                return i;\n            }\n        }\n    }\n\n    // TFB. Not found.\n\n    return -1;\n}\n\n\n\n\n//\n// W_GetNumForName\n// Calls W_CheckNumForName, but bombs out if not found.\n//\nint W_GetNumForName (char* name)\n{\n    int\ti;\n\n    i = W_CheckNumForName (name);\n\n    if (i < 0)\n    {\n        I_Error (\"W_GetNumForName: %s not found!\", name);\n    }\n \n    return i;\n}\n\n\n//\n// W_LumpLength\n// Returns the buffer size needed to load the given lump.\n//\nint W_LumpLength (unsigned int lump)\n{\n    if (lump >= numlumps)\n    {\n\tI_Error (\"W_LumpLength: %i >= numlumps\", lump);\n    }\n\n    return lumpinfo[lump].size;\n}\n\n\n\n//\n// W_ReadLump\n// Loads the lump into the given buffer,\n//  which must be >= W_LumpLength().\n//\nvoid W_ReadLump(unsigned int lump, void *dest)\n{\n    int c;\n    lumpinfo_t *l;\n\t\n    if (lump >= numlumps)\n    {\n\tI_Error (\"W_ReadLump: %i >= numlumps\", lump);\n    }\n\n    l = lumpinfo+lump;\n\t\n    I_BeginRead ();\n\t\n    c = W_Read(l->wad_file, l->position, dest, l->size);\n\n    if (c < l->size)\n    {\n\tI_Error (\"W_ReadLump: only read %i of %i on lump %i\",\n\t\t c, l->size, lump);\t\n    }\n\n    I_EndRead ();\n}\n\n\n\n\n//\n// W_CacheLumpNum\n//\n// Load a lump into memory and return a pointer to a buffer containing\n// the lump data.\n//\n// 'tag' is the type of zone memory buffer to allocate for the lump\n// (usually PU_STATIC or PU_CACHE).  If the lump is loaded as \n// PU_STATIC, it should be released back using W_ReleaseLumpNum\n// when no longer needed (do not use Z_ChangeTag).\n//\n\nvoid *W_CacheLumpNum(int lumpnum, int tag)\n{\n    byte *result;\n    lumpinfo_t *lump;\n\n    if ((unsigned)lumpnum >= numlumps)\n    {\n\tI_Error (\"W_CacheLumpNum: %i >= numlumps\", lumpnum);\n    }\n\n    lump = &lumpinfo[lumpnum];\n\n    // Get the pointer to return.  If the lump is in a memory-mapped\n    // file, we can just return a pointer to within the memory-mapped\n    // region.  If the lump is in an ordinary file, we may already\n    // have it cached; otherwise, load it into memory.\n\n    if (lump->wad_file->mapped != NULL)\n    {\n        // Memory mapped file, return from the mmapped region.\n\n        result = lump->wad_file->mapped + lump->position;\n    }\n    else if (lump->cache != NULL)\n    {\n        // Already cached, so just switch the zone tag.\n\n        result = lump->cache;\n        Z_ChangeTag(lump->cache, tag);\n    }\n    else\n    {\n        // Not yet loaded, so load it now\n\n        lump->cache = Z_Malloc(W_LumpLength(lumpnum), tag, &lump->cache);\n\tW_ReadLump (lumpnum, lump->cache);\n        result = lump->cache;\n    }\n\t\n    return result;\n}\n\n\n\n//\n// W_CacheLumpName\n//\nvoid *W_CacheLumpName(char *name, int tag)\n{\n    return W_CacheLumpNum(W_GetNumForName(name), tag);\n}\n\n// \n// Release a lump back to the cache, so that it can be reused later \n// without having to read from disk again, or alternatively, discarded\n// if we run out of memory.\n//\n// Back in Vanilla Doom, this was just done using Z_ChangeTag \n// directly, but now that we have WAD mmap, things are a bit more\n// complicated ...\n//\n\nvoid W_ReleaseLumpNum(int lumpnum)\n{\n    lumpinfo_t *lump;\n\n    if ((unsigned)lumpnum >= numlumps)\n    {\n\tI_Error (\"W_ReleaseLumpNum: %i >= numlumps\", lumpnum);\n    }\n\n    lump = &lumpinfo[lumpnum];\n\n    if (lump->wad_file->mapped != NULL)\n    {\n        // Memory-mapped file, so nothing needs to be done here.\n    }\n    else\n    {\n        Z_ChangeTag(lump->cache, PU_CACHE);\n    }\n}\n\nvoid W_ReleaseLumpName(char *name)\n{\n    W_ReleaseLumpNum(W_GetNumForName(name));\n}\n\n#if 0\n\n//\n// W_Profile\n//\nint\t\tinfo[2500][10];\nint\t\tprofilecount;\n\nvoid W_Profile (void)\n{\n    int\t\ti;\n    memblock_t*\tblock;\n    void*\tptr;\n    char\tch;\n    FILE*\tf;\n    int\t\tj;\n    char\tname[9];\n\t\n\t\n    for (i=0 ; i<numlumps ; i++)\n    {\t\n\tptr = lumpinfo[i].cache;\n\tif (!ptr)\n\t{\n\t    ch = ' ';\n\t    continue;\n\t}\n\telse\n\t{\n\t    block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));\n\t    if (block->tag < PU_PURGELEVEL)\n\t\tch = 'S';\n\t    else\n\t\tch = 'P';\n\t}\n\tinfo[i][profilecount] = ch;\n    }\n    profilecount++;\n#if ORIGCODE\n    f = fopen (\"waddump.txt\",\"w\");\n    name[8] = 0;\n\n    for (i=0 ; i<numlumps ; i++)\n    {\n\tmemcpy (name,lumpinfo[i].name,8);\n\n\tfor (j=0 ; j<8 ; j++)\n\t    if (!name[j])\n\t\tbreak;\n\n\tfor ( ; j<8 ; j++)\n\t    name[j] = ' ';\n\n\tfprintf (f,\"%s \",name);\n\n\tfor (j=0 ; j<profilecount ; j++)\n\t    fprintf (f,\"    %c\",info[i][j]);\n\n\tfprintf (f,\"\\n\");\n    }\n    fclose (f);\n#endif\n}\n\n\n#endif\n\n// Generate a hash table for fast lookups\n\nvoid W_GenerateHashTable(void)\n{\n    unsigned int i;\n\n    // Free the old hash table, if there is one\n\n    if (lumphash != NULL)\n    {\n        Z_Free(lumphash);\n    }\n\n    // Generate hash table\n    if (numlumps > 0)\n    {\n        lumphash = Z_Malloc(sizeof(lumpinfo_t *) * numlumps, PU_STATIC, NULL);\n        memset(lumphash, 0, sizeof(lumpinfo_t *) * numlumps);\n\n        for (i=0; i<numlumps; ++i)\n        {\n            unsigned int hash;\n\n            hash = W_LumpNameHash(lumpinfo[i].name) % numlumps;\n\n            // Hook into the hash table\n\n            lumpinfo[i].next = lumphash[hash];\n            lumphash[hash] = &lumpinfo[i];\n        }\n    }\n\n    // All done!\n}\n\n// Lump names that are unique to particular game types. This lets us check\n// the user is not trying to play with the wrong executable, eg.\n// chocolate-doom -iwad hexen.wad.\nstatic const struct\n{\n    GameMission_t mission;\n    char *lumpname;\n} unique_lumps[] = {\n    { doom,    \"POSSA1\" },\n    { heretic, \"IMPXA1\" },\n    { hexen,   \"ETTNA1\" },\n    { strife,  \"AGRDA1\" },\n};\n\nvoid W_CheckCorrectIWAD(GameMission_t mission)\n{\n    int i;\n    int lumpnum;\n\n    for (i = 0; i < arrlen(unique_lumps); ++i)\n    {\n        if (mission != unique_lumps[i].mission)\n        {\n            lumpnum = W_CheckNumForName(unique_lumps[i].lumpname);\n\n            if (lumpnum >= 0)\n            {\n                I_Error(\"\\nYou are trying to use a %s IWAD file with \"\n                        \"the %s%s binary.\\nThis isn't going to work.\\n\"\n                        \"You probably want to use the %s%s binary.\",\n                        D_SuggestGameName(unique_lumps[i].mission,\n                                          indetermined),\n                        PROGRAM_PREFIX,\n                        D_GameMissionString(mission),\n                        PROGRAM_PREFIX,\n                        D_GameMissionString(unique_lumps[i].mission));\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "fbdoom/w_wad.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tWAD I/O functions.\n//\n\n\n#ifndef __W_WAD__\n#define __W_WAD__\n\n#include <stdio.h>\n\n#include \"doomtype.h\"\n#include \"d_mode.h\"\n\n#include \"w_file.h\"\n\n\n//\n// TYPES\n//\n\n//\n// WADFILE I/O related stuff.\n//\n\ntypedef struct lumpinfo_s lumpinfo_t;\n\nstruct lumpinfo_s\n{\n    char\tname[8];\n    wad_file_t *wad_file;\n    int\t\tposition;\n    int\t\tsize;\n    void       *cache;\n\n    // Used for hash table lookups\n\n    lumpinfo_t *next;\n};\n\n\nextern lumpinfo_t *lumpinfo;\nextern unsigned int numlumps;\n\nwad_file_t *W_AddFile (char *filename);\n\nint\tW_CheckNumForName (char* name);\nint\tW_GetNumForName (char* name);\n\nint\tW_LumpLength (unsigned int lump);\nvoid    W_ReadLump (unsigned int lump, void *dest);\n\nvoid*\tW_CacheLumpNum (int lump, int tag);\nvoid*\tW_CacheLumpName (char* name, int tag);\n\nvoid    W_GenerateHashTable(void);\n\nextern unsigned int W_LumpNameHash(const char *s);\n\nvoid    W_ReleaseLumpNum(int lump);\nvoid    W_ReleaseLumpName(char *name);\n\nvoid W_CheckCorrectIWAD(GameMission_t mission);\n\n#endif\n"
  },
  {
    "path": "fbdoom/wi_stuff.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tIntermission screens.\n//\n\n\n#include <stdio.h>\n\n#include \"z_zone.h\"\n\n#include \"m_misc.h\"\n#include \"m_random.h\"\n\n#include \"deh_main.h\"\n#include \"i_swap.h\"\n#include \"i_system.h\"\n\n#include \"w_wad.h\"\n\n#include \"g_game.h\"\n\n#include \"r_local.h\"\n#include \"s_sound.h\"\n\n#include \"doomstat.h\"\n\n// Data.\n#include \"sounds.h\"\n\n// Needs access to LFB.\n#include \"v_video.h\"\n\n#include \"wi_stuff.h\"\n\n//\n// Data needed to add patches to full screen intermission pics.\n// Patches are statistics messages, and animations.\n// Loads of by-pixel layout and placement, offsets etc.\n//\n\n\n//\n// Different vetween registered DOOM (1994) and\n//  Ultimate DOOM - Final edition (retail, 1995?).\n// This is supposedly ignored for commercial\n//  release (aka DOOM II), which had 34 maps\n//  in one episode. So there.\n#define NUMEPISODES\t4\n#define NUMMAPS\t\t9\n\n\n// in tics\n//U #define PAUSELEN\t\t(TICRATE*2) \n//U #define SCORESTEP\t\t100\n//U #define ANIMPERIOD\t\t32\n// pixel distance from \"(YOU)\" to \"PLAYER N\"\n//U #define STARDIST\t\t10 \n//U #define WK 1\n\n\n// GLOBAL LOCATIONS\n#define WI_TITLEY\t\t2\n#define WI_SPACINGY    \t\t33\n\n// SINGPLE-PLAYER STUFF\n#define SP_STATSX\t\t50\n#define SP_STATSY\t\t50\n\n#define SP_TIMEX\t\t16\n#define SP_TIMEY\t\t(SCREENHEIGHT-32)\n\n\n// NET GAME STUFF\n#define NG_STATSY\t\t50\n#define NG_STATSX\t\t(32 + SHORT(star->width)/2 + 32*!dofrags)\n\n#define NG_SPACINGX    \t\t64\n\n\n// DEATHMATCH STUFF\n#define DM_MATRIXX\t\t42\n#define DM_MATRIXY\t\t68\n\n#define DM_SPACINGX\t\t40\n\n#define DM_TOTALSX\t\t269\n\n#define DM_KILLERSX\t\t10\n#define DM_KILLERSY\t\t100\n#define DM_VICTIMSX    \t\t5\n#define DM_VICTIMSY\t\t50\n\n\n\n\ntypedef enum\n{\n    ANIM_ALWAYS,\n    ANIM_RANDOM,\n    ANIM_LEVEL\n\n} animenum_t;\n\ntypedef struct\n{\n    int\t\tx;\n    int\t\ty;\n    \n} point_t;\n\n\n//\n// Animation.\n// There is another anim_t used in p_spec.\n//\ntypedef struct\n{\n    animenum_t\ttype;\n\n    // period in tics between animations\n    int\t\tperiod;\n\n    // number of animation frames\n    int\t\tnanims;\n\n    // location of animation\n    point_t\tloc;\n\n    // ALWAYS: n/a,\n    // RANDOM: period deviation (<256),\n    // LEVEL: level\n    int\t\tdata1;\n\n    // ALWAYS: n/a,\n    // RANDOM: random base period,\n    // LEVEL: n/a\n    int\t\tdata2; \n\n    // actual graphics for frames of animations\n    patch_t*\tp[3]; \n\n    // following must be initialized to zero before use!\n\n    // next value of bcnt (used in conjunction with period)\n    int\t\tnexttic;\n\n    // last drawn animation frame\n    int\t\tlastdrawn;\n\n    // next frame number to animate\n    int\t\tctr;\n    \n    // used by RANDOM and LEVEL when animating\n    int\t\tstate;  \n\n} anim_t;\n\n\nstatic point_t lnodes[NUMEPISODES][NUMMAPS] =\n{\n    // Episode 0 World Map\n    {\n\t{ 185, 164 },\t// location of level 0 (CJ)\n\t{ 148, 143 },\t// location of level 1 (CJ)\n\t{ 69, 122 },\t// location of level 2 (CJ)\n\t{ 209, 102 },\t// location of level 3 (CJ)\n\t{ 116, 89 },\t// location of level 4 (CJ)\n\t{ 166, 55 },\t// location of level 5 (CJ)\n\t{ 71, 56 },\t// location of level 6 (CJ)\n\t{ 135, 29 },\t// location of level 7 (CJ)\n\t{ 71, 24 }\t// location of level 8 (CJ)\n    },\n\n    // Episode 1 World Map should go here\n    {\n\t{ 254, 25 },\t// location of level 0 (CJ)\n\t{ 97, 50 },\t// location of level 1 (CJ)\n\t{ 188, 64 },\t// location of level 2 (CJ)\n\t{ 128, 78 },\t// location of level 3 (CJ)\n\t{ 214, 92 },\t// location of level 4 (CJ)\n\t{ 133, 130 },\t// location of level 5 (CJ)\n\t{ 208, 136 },\t// location of level 6 (CJ)\n\t{ 148, 140 },\t// location of level 7 (CJ)\n\t{ 235, 158 }\t// location of level 8 (CJ)\n    },\n\n    // Episode 2 World Map should go here\n    {\n\t{ 156, 168 },\t// location of level 0 (CJ)\n\t{ 48, 154 },\t// location of level 1 (CJ)\n\t{ 174, 95 },\t// location of level 2 (CJ)\n\t{ 265, 75 },\t// location of level 3 (CJ)\n\t{ 130, 48 },\t// location of level 4 (CJ)\n\t{ 279, 23 },\t// location of level 5 (CJ)\n\t{ 198, 48 },\t// location of level 6 (CJ)\n\t{ 140, 25 },\t// location of level 7 (CJ)\n\t{ 281, 136 }\t// location of level 8 (CJ)\n    }\n\n};\n\n\n//\n// Animation locations for episode 0 (1).\n// Using patches saves a lot of space,\n//  as they replace 320x200 full screen frames.\n//\n\n#define ANIM(type, period, nanims, x, y, nexttic)            \\\n   { (type), (period), (nanims), { (x), (y) }, (nexttic),    \\\n     0, { NULL, NULL, NULL }, 0, 0, 0, 0 }\n\n\nstatic anim_t epsd0animinfo[] =\n{\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 224, 104, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 184, 160, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 112, 136, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 72, 112, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 88, 96, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 64, 48, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 192, 40, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 136, 16, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 80, 16, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 64, 24, 0),\n};\n\nstatic anim_t epsd1animinfo[] =\n{\n    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 1),\n    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 2),\n    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 3),\n    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 4),\n    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 5),\n    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 6),\n    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 7),\n    ANIM(ANIM_LEVEL, TICRATE/3, 3, 192, 144, 8),\n    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 8),\n};\n\nstatic anim_t epsd2animinfo[] =\n{\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 104, 168, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 40, 136, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 160, 96, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 104, 80, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 120, 32, 0),\n    ANIM(ANIM_ALWAYS, TICRATE/4, 3, 40, 0, 0),\n};\n\nstatic int NUMANIMS[NUMEPISODES] =\n{\n    arrlen(epsd0animinfo),\n    arrlen(epsd1animinfo),\n    arrlen(epsd2animinfo),\n};\n\nstatic anim_t *anims[NUMEPISODES] =\n{\n    epsd0animinfo,\n    epsd1animinfo,\n    epsd2animinfo\n};\n\n\n//\n// GENERAL DATA\n//\n\n//\n// Locally used stuff.\n//\n\n// States for single-player\n#define SP_KILLS\t\t0\n#define SP_ITEMS\t\t2\n#define SP_SECRET\t\t4\n#define SP_FRAGS\t\t6 \n#define SP_TIME\t\t\t8 \n#define SP_PAR\t\t\tST_TIME\n\n#define SP_PAUSE\t\t1\n\n// in seconds\n#define SHOWNEXTLOCDELAY\t4\n//#define SHOWLASTLOCDELAY\tSHOWNEXTLOCDELAY\n\n\n// used to accelerate or skip a stage\nstatic int\t\tacceleratestage;\n\n// wbs->pnum\nstatic int\t\tme;\n\n // specifies current state\nstatic stateenum_t\tstate;\n\n// contains information passed into intermission\nstatic wbstartstruct_t*\twbs;\n\nstatic wbplayerstruct_t* plrs;  // wbs->plyr[]\n\n// used for general timing\nstatic int \t\tcnt;  \n\n// used for timing of background animation\nstatic int \t\tbcnt;\n\n// signals to refresh everything for one frame\nstatic int \t\tfirstrefresh; \n\nstatic int\t\tcnt_kills[MAXPLAYERS];\nstatic int\t\tcnt_items[MAXPLAYERS];\nstatic int\t\tcnt_secret[MAXPLAYERS];\nstatic int\t\tcnt_time;\nstatic int\t\tcnt_par;\nstatic int\t\tcnt_pause;\n\n// # of commercial levels\nstatic int\t\tNUMCMAPS; \n\n\n//\n//\tGRAPHICS\n//\n\n// You Are Here graphic\nstatic patch_t*\t\tyah[3] = { NULL, NULL, NULL }; \n\n// splat\nstatic patch_t*\t\tsplat[2] = { NULL, NULL };\n\n// %, : graphics\nstatic patch_t*\t\tpercent;\nstatic patch_t*\t\tcolon;\n\n// 0-9 graphic\nstatic patch_t*\t\tnum[10];\n\n// minus sign\nstatic patch_t*\t\twiminus;\n\n// \"Finished!\" graphics\nstatic patch_t*\t\tfinished;\n\n// \"Entering\" graphic\nstatic patch_t*\t\tentering; \n\n// \"secret\"\nstatic patch_t*\t\tsp_secret;\n\n // \"Kills\", \"Scrt\", \"Items\", \"Frags\"\nstatic patch_t*\t\tkills;\nstatic patch_t*\t\tsecret;\nstatic patch_t*\t\titems;\nstatic patch_t*\t\tfrags;\n\n// Time sucks.\nstatic patch_t*\t\ttimepatch;\nstatic patch_t*\t\tpar;\nstatic patch_t*\t\tsucks;\n\n// \"killers\", \"victims\"\nstatic patch_t*\t\tkillers;\nstatic patch_t*\t\tvictims; \n\n// \"Total\", your face, your dead face\nstatic patch_t*\t\ttotal;\nstatic patch_t*\t\tstar;\nstatic patch_t*\t\tbstar;\n\n// \"red P[1..MAXPLAYERS]\"\nstatic patch_t*\t\tp[MAXPLAYERS];\n\n// \"gray P[1..MAXPLAYERS]\"\nstatic patch_t*\t\tbp[MAXPLAYERS];\n\n // Name graphics of each level (centered)\nstatic patch_t**\tlnames;\n\n// Buffer storing the backdrop\nstatic patch_t *background;\n\n//\n// CODE\n//\n\n// slam background\nvoid WI_slamBackground(void)\n{\n    V_DrawPatch(0, 0, background);\n}\n\n// The ticker is used to detect keys\n//  because of timing issues in netgames.\nboolean WI_Responder(event_t* ev)\n{\n    return false;\n}\n\n\n// Draws \"<Levelname> Finished!\"\nvoid WI_drawLF(void)\n{\n    int y = WI_TITLEY;\n\n    if (gamemode != commercial || wbs->last < NUMCMAPS)\n    {\n        // draw <LevelName> \n        V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->last]->width))/2,\n                    y, lnames[wbs->last]);\n\n        // draw \"Finished!\"\n        y += (5*SHORT(lnames[wbs->last]->height))/4;\n\n        V_DrawPatch((SCREENWIDTH - SHORT(finished->width)) / 2, y, finished);\n    }\n    else if (wbs->last == NUMCMAPS)\n    {\n        // MAP33 - nothing is displayed!\n    }\n    else if (wbs->last > NUMCMAPS)\n    {\n        // > MAP33.  Doom bombs out here with a Bad V_DrawPatch error.\n        // I'm pretty sure that doom2.exe is just reading into random\n        // bits of memory at this point, but let's try to be accurate\n        // anyway.  This deliberately triggers a V_DrawPatch error.\n\n        patch_t tmp = { SCREENWIDTH, SCREENHEIGHT, 1, 1, \n                        { 0, 0, 0, 0, 0, 0, 0, 0 } };\n\n        V_DrawPatch(0, y, &tmp);\n    }\n}\n\n\n\n// Draws \"Entering <LevelName>\"\nvoid WI_drawEL(void)\n{\n    int y = WI_TITLEY;\n\n    // draw \"Entering\"\n    V_DrawPatch((SCREENWIDTH - SHORT(entering->width))/2,\n\t\ty,\n                entering);\n\n    // draw level\n    y += (5*SHORT(lnames[wbs->next]->height))/4;\n\n    V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->next]->width))/2,\n\t\ty, \n                lnames[wbs->next]);\n\n}\n\nvoid\nWI_drawOnLnode\n( int\t\tn,\n  patch_t*\tc[] )\n{\n\n    int\t\ti;\n    int\t\tleft;\n    int\t\ttop;\n    int\t\tright;\n    int\t\tbottom;\n    boolean\tfits = false;\n\n    i = 0;\n    do\n    {\n\tleft = lnodes[wbs->epsd][n].x - SHORT(c[i]->leftoffset);\n\ttop = lnodes[wbs->epsd][n].y - SHORT(c[i]->topoffset);\n\tright = left + SHORT(c[i]->width);\n\tbottom = top + SHORT(c[i]->height);\n\n\tif (left >= 0\n\t    && right < SCREENWIDTH\n\t    && top >= 0\n\t    && bottom < SCREENHEIGHT)\n\t{\n\t    fits = true;\n\t}\n\telse\n\t{\n\t    i++;\n\t}\n    } while (!fits && i!=2 && c[i] != NULL);\n\n    if (fits && i<2)\n    {\n\tV_DrawPatch(lnodes[wbs->epsd][n].x,\n                    lnodes[wbs->epsd][n].y,\n\t\t    c[i]);\n    }\n    else\n    {\n\t// DEBUG\n\tprintf(\"Could not place patch on level %d\", n+1); \n    }\n}\n\n\n\nvoid WI_initAnimatedBack(void)\n{\n    int\t\ti;\n    anim_t*\ta;\n\n    if (gamemode == commercial)\n\treturn;\n\n    if (wbs->epsd > 2)\n\treturn;\n\n    for (i=0;i<NUMANIMS[wbs->epsd];i++)\n    {\n\ta = &anims[wbs->epsd][i];\n\n\t// init variables\n\ta->ctr = -1;\n\n\t// specify the next time to draw it\n\tif (a->type == ANIM_ALWAYS)\n\t    a->nexttic = bcnt + 1 + (M_Random()%a->period);\n\telse if (a->type == ANIM_RANDOM)\n\t    a->nexttic = bcnt + 1 + a->data2+(M_Random()%a->data1);\n\telse if (a->type == ANIM_LEVEL)\n\t    a->nexttic = bcnt + 1;\n    }\n\n}\n\nvoid WI_updateAnimatedBack(void)\n{\n    int\t\ti;\n    anim_t*\ta;\n\n    if (gamemode == commercial)\n\treturn;\n\n    if (wbs->epsd > 2)\n\treturn;\n\n    for (i=0;i<NUMANIMS[wbs->epsd];i++)\n    {\n\ta = &anims[wbs->epsd][i];\n\n\tif (bcnt == a->nexttic)\n\t{\n\t    switch (a->type)\n\t    {\n\t      case ANIM_ALWAYS:\n\t\tif (++a->ctr >= a->nanims) a->ctr = 0;\n\t\ta->nexttic = bcnt + a->period;\n\t\tbreak;\n\n\t      case ANIM_RANDOM:\n\t\ta->ctr++;\n\t\tif (a->ctr == a->nanims)\n\t\t{\n\t\t    a->ctr = -1;\n\t\t    a->nexttic = bcnt+a->data2+(M_Random()%a->data1);\n\t\t}\n\t\telse a->nexttic = bcnt + a->period;\n\t\tbreak;\n\t\t\n\t      case ANIM_LEVEL:\n\t\t// gawd-awful hack for level anims\n\t\tif (!(state == StatCount && i == 7)\n\t\t    && wbs->next == a->data1)\n\t\t{\n\t\t    a->ctr++;\n\t\t    if (a->ctr == a->nanims) a->ctr--;\n\t\t    a->nexttic = bcnt + a->period;\n\t\t}\n\t\tbreak;\n\t    }\n\t}\n\n    }\n\n}\n\nvoid WI_drawAnimatedBack(void)\n{\n    int\t\t\ti;\n    anim_t*\t\ta;\n\n    if (gamemode == commercial)\n\treturn;\n\n    if (wbs->epsd > 2)\n\treturn;\n\n    for (i=0 ; i<NUMANIMS[wbs->epsd] ; i++)\n    {\n\ta = &anims[wbs->epsd][i];\n\n\tif (a->ctr >= 0)\n\t    V_DrawPatch(a->loc.x, a->loc.y, a->p[a->ctr]);\n    }\n\n}\n\n//\n// Draws a number.\n// If digits > 0, then use that many digits minimum,\n//  otherwise only use as many as necessary.\n// Returns new x position.\n//\n\nint\nWI_drawNum\n( int\t\tx,\n  int\t\ty,\n  int\t\tn,\n  int\t\tdigits )\n{\n\n    int\t\tfontwidth = SHORT(num[0]->width);\n    int\t\tneg;\n    int\t\ttemp;\n\n    if (digits < 0)\n    {\n\tif (!n)\n\t{\n\t    // make variable-length zeros 1 digit long\n\t    digits = 1;\n\t}\n\telse\n\t{\n\t    // figure out # of digits in #\n\t    digits = 0;\n\t    temp = n;\n\n\t    while (temp)\n\t    {\n\t\ttemp /= 10;\n\t\tdigits++;\n\t    }\n\t}\n    }\n\n    neg = n < 0;\n    if (neg)\n\tn = -n;\n\n    // if non-number, do not draw it\n    if (n == 1994)\n\treturn 0;\n\n    // draw the new number\n    while (digits--)\n    {\n\tx -= fontwidth;\n\tV_DrawPatch(x, y, num[ n % 10 ]);\n\tn /= 10;\n    }\n\n    // draw a minus sign if necessary\n    if (neg)\n\tV_DrawPatch(x-=8, y, wiminus);\n\n    return x;\n\n}\n\nvoid\nWI_drawPercent\n( int\t\tx,\n  int\t\ty,\n  int\t\tp )\n{\n    if (p < 0)\n\treturn;\n\n    V_DrawPatch(x, y, percent);\n    WI_drawNum(x, y, p, -1);\n}\n\n\n\n//\n// Display level completion time and par,\n//  or \"sucks\" message if overflow.\n//\nvoid\nWI_drawTime\n( int\t\tx,\n  int\t\ty,\n  int\t\tt )\n{\n\n    int\t\tdiv;\n    int\t\tn;\n\n    if (t<0)\n\treturn;\n\n    if (t <= 61*59)\n    {\n\tdiv = 1;\n\n\tdo\n\t{\n\t    n = (t / div) % 60;\n\t    x = WI_drawNum(x, y, n, 2) - SHORT(colon->width);\n\t    div *= 60;\n\n\t    // draw\n\t    if (div==60 || t / div)\n\t\tV_DrawPatch(x, y, colon);\n\t    \n\t} while (t / div);\n    }\n    else\n    {\n\t// \"sucks\"\n\tV_DrawPatch(x - SHORT(sucks->width), y, sucks); \n    }\n}\n\n\nvoid WI_End(void)\n{\n    void WI_unloadData(void);\n    WI_unloadData();\n}\n\nvoid WI_initNoState(void)\n{\n    state = NoState;\n    acceleratestage = 0;\n    cnt = 10;\n}\n\nvoid WI_updateNoState(void) {\n\n    WI_updateAnimatedBack();\n\n    if (!--cnt)\n    {\n        // Don't call WI_End yet.  G_WorldDone doesnt immediately \n        // change gamestate, so WI_Drawer is still going to get\n        // run until that happens.  If we do that after WI_End\n        // (which unloads all the graphics), we're in trouble.\n\t//WI_End();\n\tG_WorldDone();\n    }\n\n}\n\nstatic boolean\t\tsnl_pointeron = false;\n\n\nvoid WI_initShowNextLoc(void)\n{\n    state = ShowNextLoc;\n    acceleratestage = 0;\n    cnt = SHOWNEXTLOCDELAY * TICRATE;\n\n    WI_initAnimatedBack();\n}\n\nvoid WI_updateShowNextLoc(void)\n{\n    WI_updateAnimatedBack();\n\n    if (!--cnt || acceleratestage)\n\tWI_initNoState();\n    else\n\tsnl_pointeron = (cnt & 31) < 20;\n}\n\nvoid WI_drawShowNextLoc(void)\n{\n\n    int\t\ti;\n    int\t\tlast;\n\n    WI_slamBackground();\n\n    // draw animated background\n    WI_drawAnimatedBack(); \n\n    if ( gamemode != commercial)\n    {\n  \tif (wbs->epsd > 2)\n\t{\n\t    WI_drawEL();\n\t    return;\n\t}\n\t\n\tlast = (wbs->last == 8) ? wbs->next - 1 : wbs->last;\n\n\t// draw a splat on taken cities.\n\tfor (i=0 ; i<=last ; i++)\n\t    WI_drawOnLnode(i, splat);\n\n\t// splat the secret level?\n\tif (wbs->didsecret)\n\t    WI_drawOnLnode(8, splat);\n\n\t// draw flashing ptr\n\tif (snl_pointeron)\n\t    WI_drawOnLnode(wbs->next, yah); \n    }\n\n    // draws which level you are entering..\n    if ( (gamemode != commercial)\n\t || wbs->next != 30)\n\tWI_drawEL();  \n\n}\n\nvoid WI_drawNoState(void)\n{\n    snl_pointeron = true;\n    WI_drawShowNextLoc();\n}\n\nint WI_fragSum(int playernum)\n{\n    int\t\ti;\n    int\t\tfrags = 0;\n    \n    for (i=0 ; i<MAXPLAYERS ; i++)\n    {\n\tif (playeringame[i]\n\t    && i!=playernum)\n\t{\n\t    frags += plrs[playernum].frags[i];\n\t}\n    }\n\n\t\n    // JDC hack - negative frags.\n    frags -= plrs[playernum].frags[playernum];\n    // UNUSED if (frags < 0)\n    // \tfrags = 0;\n\n    return frags;\n}\n\n\n\nstatic int\t\tdm_state;\nstatic int\t\tdm_frags[MAXPLAYERS][MAXPLAYERS];\nstatic int\t\tdm_totals[MAXPLAYERS];\n\n\n\nvoid WI_initDeathmatchStats(void)\n{\n\n    int\t\ti;\n    int\t\tj;\n\n    state = StatCount;\n    acceleratestage = 0;\n    dm_state = 1;\n\n    cnt_pause = TICRATE;\n\n    for (i=0 ; i<MAXPLAYERS ; i++)\n    {\n\tif (playeringame[i])\n\t{\n\t    for (j=0 ; j<MAXPLAYERS ; j++)\n\t\tif (playeringame[j])\n\t\t    dm_frags[i][j] = 0;\n\n\t    dm_totals[i] = 0;\n\t}\n    }\n    \n    WI_initAnimatedBack();\n}\n\n\n\nvoid WI_updateDeathmatchStats(void)\n{\n\n    int\t\ti;\n    int\t\tj;\n    \n    boolean\tstillticking;\n\n    WI_updateAnimatedBack();\n\n    if (acceleratestage && dm_state != 4)\n    {\n\tacceleratestage = 0;\n\n\tfor (i=0 ; i<MAXPLAYERS ; i++)\n\t{\n\t    if (playeringame[i])\n\t    {\n\t\tfor (j=0 ; j<MAXPLAYERS ; j++)\n\t\t    if (playeringame[j])\n\t\t\tdm_frags[i][j] = plrs[i].frags[j];\n\n\t\tdm_totals[i] = WI_fragSum(i);\n\t    }\n\t}\n\t\n\n\tS_StartSound(0, sfx_barexp);\n\tdm_state = 4;\n    }\n\n    \n    if (dm_state == 2)\n    {\n\tif (!(bcnt&3))\n\t    S_StartSound(0, sfx_pistol);\n\t\n\tstillticking = false;\n\n\tfor (i=0 ; i<MAXPLAYERS ; i++)\n\t{\n\t    if (playeringame[i])\n\t    {\n\t\tfor (j=0 ; j<MAXPLAYERS ; j++)\n\t\t{\n\t\t    if (playeringame[j]\n\t\t\t&& dm_frags[i][j] != plrs[i].frags[j])\n\t\t    {\n\t\t\tif (plrs[i].frags[j] < 0)\n\t\t\t    dm_frags[i][j]--;\n\t\t\telse\n\t\t\t    dm_frags[i][j]++;\n\n\t\t\tif (dm_frags[i][j] > 99)\n\t\t\t    dm_frags[i][j] = 99;\n\n\t\t\tif (dm_frags[i][j] < -99)\n\t\t\t    dm_frags[i][j] = -99;\n\t\t\t\n\t\t\tstillticking = true;\n\t\t    }\n\t\t}\n\t\tdm_totals[i] = WI_fragSum(i);\n\n\t\tif (dm_totals[i] > 99)\n\t\t    dm_totals[i] = 99;\n\t\t\n\t\tif (dm_totals[i] < -99)\n\t\t    dm_totals[i] = -99;\n\t    }\n\t    \n\t}\n\tif (!stillticking)\n\t{\n\t    S_StartSound(0, sfx_barexp);\n\t    dm_state++;\n\t}\n\n    }\n    else if (dm_state == 4)\n    {\n\tif (acceleratestage)\n\t{\n\t    S_StartSound(0, sfx_slop);\n\n\t    if ( gamemode == commercial)\n\t\tWI_initNoState();\n\t    else\n\t\tWI_initShowNextLoc();\n\t}\n    }\n    else if (dm_state & 1)\n    {\n\tif (!--cnt_pause)\n\t{\n\t    dm_state++;\n\t    cnt_pause = TICRATE;\n\t}\n    }\n}\n\n\n\nvoid WI_drawDeathmatchStats(void)\n{\n\n    int\t\ti;\n    int\t\tj;\n    int\t\tx;\n    int\t\ty;\n    int\t\tw;\n\n    WI_slamBackground();\n    \n    // draw animated background\n    WI_drawAnimatedBack(); \n    WI_drawLF();\n\n    // draw stat titles (top line)\n    V_DrawPatch(DM_TOTALSX-SHORT(total->width)/2,\n\t\tDM_MATRIXY-WI_SPACINGY+10,\n\t\ttotal);\n    \n    V_DrawPatch(DM_KILLERSX, DM_KILLERSY, killers);\n    V_DrawPatch(DM_VICTIMSX, DM_VICTIMSY, victims);\n\n    // draw P?\n    x = DM_MATRIXX + DM_SPACINGX;\n    y = DM_MATRIXY;\n\n    for (i=0 ; i<MAXPLAYERS ; i++)\n    {\n\tif (playeringame[i])\n\t{\n\t    V_DrawPatch(x-SHORT(p[i]->width)/2,\n\t\t\tDM_MATRIXY - WI_SPACINGY,\n\t\t\tp[i]);\n\t    \n\t    V_DrawPatch(DM_MATRIXX-SHORT(p[i]->width)/2,\n\t\t\ty,\n\t\t\tp[i]);\n\n\t    if (i == me)\n\t    {\n\t\tV_DrawPatch(x-SHORT(p[i]->width)/2,\n\t\t\t    DM_MATRIXY - WI_SPACINGY,\n\t\t\t    bstar);\n\n\t\tV_DrawPatch(DM_MATRIXX-SHORT(p[i]->width)/2,\n\t\t\t    y,\n\t\t\t    star);\n\t    }\n\t}\n\telse\n\t{\n\t    // V_DrawPatch(x-SHORT(bp[i]->width)/2,\n\t    //   DM_MATRIXY - WI_SPACINGY, bp[i]);\n\t    // V_DrawPatch(DM_MATRIXX-SHORT(bp[i]->width)/2,\n\t    //   y, bp[i]);\n\t}\n\tx += DM_SPACINGX;\n\ty += WI_SPACINGY;\n    }\n\n    // draw stats\n    y = DM_MATRIXY+10;\n    w = SHORT(num[0]->width);\n\n    for (i=0 ; i<MAXPLAYERS ; i++)\n    {\n\tx = DM_MATRIXX + DM_SPACINGX;\n\n\tif (playeringame[i])\n\t{\n\t    for (j=0 ; j<MAXPLAYERS ; j++)\n\t    {\n\t\tif (playeringame[j])\n\t\t    WI_drawNum(x+w, y, dm_frags[i][j], 2);\n\n\t\tx += DM_SPACINGX;\n\t    }\n\t    WI_drawNum(DM_TOTALSX+w, y, dm_totals[i], 2);\n\t}\n\ty += WI_SPACINGY;\n    }\n}\n\nstatic int\tcnt_frags[MAXPLAYERS];\nstatic int\tdofrags;\nstatic int\tng_state;\n\nvoid WI_initNetgameStats(void)\n{\n\n    int i;\n\n    state = StatCount;\n    acceleratestage = 0;\n    ng_state = 1;\n\n    cnt_pause = TICRATE;\n\n    for (i=0 ; i<MAXPLAYERS ; i++)\n    {\n\tif (!playeringame[i])\n\t    continue;\n\n\tcnt_kills[i] = cnt_items[i] = cnt_secret[i] = cnt_frags[i] = 0;\n\n\tdofrags += WI_fragSum(i);\n    }\n\n    dofrags = !!dofrags;\n\n    WI_initAnimatedBack();\n}\n\n\n\nvoid WI_updateNetgameStats(void)\n{\n\n    int\t\ti;\n    int\t\tfsum;\n    \n    boolean\tstillticking;\n\n    WI_updateAnimatedBack();\n\n    if (acceleratestage && ng_state != 10)\n    {\n\tacceleratestage = 0;\n\n\tfor (i=0 ; i<MAXPLAYERS ; i++)\n\t{\n\t    if (!playeringame[i])\n\t\tcontinue;\n\n\t    cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills;\n\t    cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems;\n\t    cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret;\n\n\t    if (dofrags)\n\t\tcnt_frags[i] = WI_fragSum(i);\n\t}\n\tS_StartSound(0, sfx_barexp);\n\tng_state = 10;\n    }\n\n    if (ng_state == 2)\n    {\n\tif (!(bcnt&3))\n\t    S_StartSound(0, sfx_pistol);\n\n\tstillticking = false;\n\n\tfor (i=0 ; i<MAXPLAYERS ; i++)\n\t{\n\t    if (!playeringame[i])\n\t\tcontinue;\n\n\t    cnt_kills[i] += 2;\n\n\t    if (cnt_kills[i] >= (plrs[i].skills * 100) / wbs->maxkills)\n\t\tcnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills;\n\t    else\n\t\tstillticking = true;\n\t}\n\t\n\tif (!stillticking)\n\t{\n\t    S_StartSound(0, sfx_barexp);\n\t    ng_state++;\n\t}\n    }\n    else if (ng_state == 4)\n    {\n\tif (!(bcnt&3))\n\t    S_StartSound(0, sfx_pistol);\n\n\tstillticking = false;\n\n\tfor (i=0 ; i<MAXPLAYERS ; i++)\n\t{\n\t    if (!playeringame[i])\n\t\tcontinue;\n\n\t    cnt_items[i] += 2;\n\t    if (cnt_items[i] >= (plrs[i].sitems * 100) / wbs->maxitems)\n\t\tcnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems;\n\t    else\n\t\tstillticking = true;\n\t}\n\tif (!stillticking)\n\t{\n\t    S_StartSound(0, sfx_barexp);\n\t    ng_state++;\n\t}\n    }\n    else if (ng_state == 6)\n    {\n\tif (!(bcnt&3))\n\t    S_StartSound(0, sfx_pistol);\n\n\tstillticking = false;\n\n\tfor (i=0 ; i<MAXPLAYERS ; i++)\n\t{\n\t    if (!playeringame[i])\n\t\tcontinue;\n\n\t    cnt_secret[i] += 2;\n\n\t    if (cnt_secret[i] >= (plrs[i].ssecret * 100) / wbs->maxsecret)\n\t\tcnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret;\n\t    else\n\t\tstillticking = true;\n\t}\n\t\n\tif (!stillticking)\n\t{\n\t    S_StartSound(0, sfx_barexp);\n\t    ng_state += 1 + 2*!dofrags;\n\t}\n    }\n    else if (ng_state == 8)\n    {\n\tif (!(bcnt&3))\n\t    S_StartSound(0, sfx_pistol);\n\n\tstillticking = false;\n\n\tfor (i=0 ; i<MAXPLAYERS ; i++)\n\t{\n\t    if (!playeringame[i])\n\t\tcontinue;\n\n\t    cnt_frags[i] += 1;\n\n\t    if (cnt_frags[i] >= (fsum = WI_fragSum(i)))\n\t\tcnt_frags[i] = fsum;\n\t    else\n\t\tstillticking = true;\n\t}\n\t\n\tif (!stillticking)\n\t{\n\t    S_StartSound(0, sfx_pldeth);\n\t    ng_state++;\n\t}\n    }\n    else if (ng_state == 10)\n    {\n\tif (acceleratestage)\n\t{\n\t    S_StartSound(0, sfx_sgcock);\n\t    if ( gamemode == commercial )\n\t\tWI_initNoState();\n\t    else\n\t\tWI_initShowNextLoc();\n\t}\n    }\n    else if (ng_state & 1)\n    {\n\tif (!--cnt_pause)\n\t{\n\t    ng_state++;\n\t    cnt_pause = TICRATE;\n\t}\n    }\n}\n\n\n\nvoid WI_drawNetgameStats(void)\n{\n    int\t\ti;\n    int\t\tx;\n    int\t\ty;\n    int\t\tpwidth = SHORT(percent->width);\n\n    WI_slamBackground();\n    \n    // draw animated background\n    WI_drawAnimatedBack(); \n\n    WI_drawLF();\n\n    // draw stat titles (top line)\n    V_DrawPatch(NG_STATSX+NG_SPACINGX-SHORT(kills->width),\n\t\tNG_STATSY, kills);\n\n    V_DrawPatch(NG_STATSX+2*NG_SPACINGX-SHORT(items->width),\n\t\tNG_STATSY, items);\n\n    V_DrawPatch(NG_STATSX+3*NG_SPACINGX-SHORT(secret->width),\n\t\tNG_STATSY, secret);\n    \n    if (dofrags)\n\tV_DrawPatch(NG_STATSX+4*NG_SPACINGX-SHORT(frags->width),\n\t\t    NG_STATSY, frags);\n\n    // draw stats\n    y = NG_STATSY + SHORT(kills->height);\n\n    for (i=0 ; i<MAXPLAYERS ; i++)\n    {\n\tif (!playeringame[i])\n\t    continue;\n\n\tx = NG_STATSX;\n\tV_DrawPatch(x-SHORT(p[i]->width), y, p[i]);\n\n\tif (i == me)\n\t    V_DrawPatch(x-SHORT(p[i]->width), y, star);\n\n\tx += NG_SPACINGX;\n\tWI_drawPercent(x-pwidth, y+10, cnt_kills[i]);\tx += NG_SPACINGX;\n\tWI_drawPercent(x-pwidth, y+10, cnt_items[i]);\tx += NG_SPACINGX;\n\tWI_drawPercent(x-pwidth, y+10, cnt_secret[i]);\tx += NG_SPACINGX;\n\n\tif (dofrags)\n\t    WI_drawNum(x, y+10, cnt_frags[i], -1);\n\n\ty += WI_SPACINGY;\n    }\n\n}\n\nstatic int\tsp_state;\n\nvoid WI_initStats(void)\n{\n    state = StatCount;\n    acceleratestage = 0;\n    sp_state = 1;\n    cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1;\n    cnt_time = cnt_par = -1;\n    cnt_pause = TICRATE;\n\n    WI_initAnimatedBack();\n}\n\nvoid WI_updateStats(void)\n{\n\n    WI_updateAnimatedBack();\n\n    if (acceleratestage && sp_state != 10)\n    {\n\tacceleratestage = 0;\n\tcnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills;\n\tcnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems;\n\tcnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret;\n\tcnt_time = plrs[me].stime / TICRATE;\n\tcnt_par = wbs->partime / TICRATE;\n\tS_StartSound(0, sfx_barexp);\n\tsp_state = 10;\n    }\n\n    if (sp_state == 2)\n    {\n\tcnt_kills[0] += 2;\n\n\tif (!(bcnt&3))\n\t    S_StartSound(0, sfx_pistol);\n\n\tif (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills)\n\t{\n\t    cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills;\n\t    S_StartSound(0, sfx_barexp);\n\t    sp_state++;\n\t}\n    }\n    else if (sp_state == 4)\n    {\n\tcnt_items[0] += 2;\n\n\tif (!(bcnt&3))\n\t    S_StartSound(0, sfx_pistol);\n\n\tif (cnt_items[0] >= (plrs[me].sitems * 100) / wbs->maxitems)\n\t{\n\t    cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems;\n\t    S_StartSound(0, sfx_barexp);\n\t    sp_state++;\n\t}\n    }\n    else if (sp_state == 6)\n    {\n\tcnt_secret[0] += 2;\n\n\tif (!(bcnt&3))\n\t    S_StartSound(0, sfx_pistol);\n\n\tif (cnt_secret[0] >= (plrs[me].ssecret * 100) / wbs->maxsecret)\n\t{\n\t    cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret;\n\t    S_StartSound(0, sfx_barexp);\n\t    sp_state++;\n\t}\n    }\n\n    else if (sp_state == 8)\n    {\n\tif (!(bcnt&3))\n\t    S_StartSound(0, sfx_pistol);\n\n\tcnt_time += 3;\n\n\tif (cnt_time >= plrs[me].stime / TICRATE)\n\t    cnt_time = plrs[me].stime / TICRATE;\n\n\tcnt_par += 3;\n\n\tif (cnt_par >= wbs->partime / TICRATE)\n\t{\n\t    cnt_par = wbs->partime / TICRATE;\n\n\t    if (cnt_time >= plrs[me].stime / TICRATE)\n\t    {\n\t\tS_StartSound(0, sfx_barexp);\n\t\tsp_state++;\n\t    }\n\t}\n    }\n    else if (sp_state == 10)\n    {\n\tif (acceleratestage)\n\t{\n\t    S_StartSound(0, sfx_sgcock);\n\n\t    if (gamemode == commercial)\n\t\tWI_initNoState();\n\t    else\n\t\tWI_initShowNextLoc();\n\t}\n    }\n    else if (sp_state & 1)\n    {\n\tif (!--cnt_pause)\n\t{\n\t    sp_state++;\n\t    cnt_pause = TICRATE;\n\t}\n    }\n\n}\n\nvoid WI_drawStats(void)\n{\n    // line height\n    int lh;\t\n\n    lh = (3*SHORT(num[0]->height))/2;\n\n    WI_slamBackground();\n\n    // draw animated background\n    WI_drawAnimatedBack();\n    \n    WI_drawLF();\n\n    V_DrawPatch(SP_STATSX, SP_STATSY, kills);\n    WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY, cnt_kills[0]);\n\n    V_DrawPatch(SP_STATSX, SP_STATSY+lh, items);\n    WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+lh, cnt_items[0]);\n\n    V_DrawPatch(SP_STATSX, SP_STATSY+2*lh, sp_secret);\n    WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0]);\n\n    V_DrawPatch(SP_TIMEX, SP_TIMEY, timepatch);\n    WI_drawTime(SCREENWIDTH/2 - SP_TIMEX, SP_TIMEY, cnt_time);\n\n    if (wbs->epsd < 3)\n    {\n\tV_DrawPatch(SCREENWIDTH/2 + SP_TIMEX, SP_TIMEY, par);\n\tWI_drawTime(SCREENWIDTH - SP_TIMEX, SP_TIMEY, cnt_par);\n    }\n\n}\n\nvoid WI_checkForAccelerate(void)\n{\n    int   i;\n    player_t  *player;\n\n    // check for button presses to skip delays\n    for (i=0, player = players ; i<MAXPLAYERS ; i++, player++)\n    {\n\tif (playeringame[i])\n\t{\n\t    if (player->cmd.buttons & BT_ATTACK)\n\t    {\n\t\tif (!player->attackdown)\n\t\t    acceleratestage = 1;\n\t\tplayer->attackdown = true;\n\t    }\n\t    else\n\t\tplayer->attackdown = false;\n\t    if (player->cmd.buttons & BT_USE)\n\t    {\n\t\tif (!player->usedown)\n\t\t    acceleratestage = 1;\n\t\tplayer->usedown = true;\n\t    }\n\t    else\n\t\tplayer->usedown = false;\n\t}\n    }\n}\n\n\n\n// Updates stuff each tick\nvoid WI_Ticker(void)\n{\n    // counter for general background animation\n    bcnt++;  \n\n    if (bcnt == 1)\n    {\n\t// intermission music\n  \tif ( gamemode == commercial )\n\t  S_ChangeMusic(mus_dm2int, true);\n\telse\n\t  S_ChangeMusic(mus_inter, true); \n    }\n\n    WI_checkForAccelerate();\n\n    switch (state)\n    {\n      case StatCount:\n\tif (deathmatch) WI_updateDeathmatchStats();\n\telse if (netgame) WI_updateNetgameStats();\n\telse WI_updateStats();\n\tbreak;\n\t\n      case ShowNextLoc:\n\tWI_updateShowNextLoc();\n\tbreak;\n\t\n      case NoState:\n\tWI_updateNoState();\n\tbreak;\n    }\n\n}\n\ntypedef void (*load_callback_t)(char *lumpname, patch_t **variable);\n\n// Common load/unload function.  Iterates over all the graphics\n// lumps to be loaded/unloaded into memory.\n\nstatic void WI_loadUnloadData(load_callback_t callback)\n{\n    int i, j;\n    char name[9];\n    anim_t *a;\n\n    if (gamemode == commercial)\n    {\n\tfor (i=0 ; i<NUMCMAPS ; i++)\n\t{\n\t    DEH_snprintf(name, 9, \"CWILV%2.2d\", i);\n            callback(name, &lnames[i]);\n\t}\n    }\n    else\n    {\n\tfor (i=0 ; i<NUMMAPS ; i++)\n\t{\n\t    DEH_snprintf(name, 9, \"WILV%d%d\", wbs->epsd, i);\n            callback(name, &lnames[i]);\n\t}\n\n\t// you are here\n        callback(DEH_String(\"WIURH0\"), &yah[0]);\n\n\t// you are here (alt.)\n        callback(DEH_String(\"WIURH1\"), &yah[1]);\n\n\t// splat\n        callback(DEH_String(\"WISPLAT\"), &splat[0]);\n\n\tif (wbs->epsd < 3)\n\t{\n\t    for (j=0;j<NUMANIMS[wbs->epsd];j++)\n\t    {\n\t\ta = &anims[wbs->epsd][j];\n\t\tfor (i=0;i<a->nanims;i++)\n\t\t{\n\t\t    // MONDO HACK!\n\t\t    if (wbs->epsd != 1 || j != 8)\n\t\t    {\n\t\t\t// animations\n\t\t\tDEH_snprintf(name, 9, \"WIA%d%.2d%.2d\", wbs->epsd, j, i);\n                        callback(name, &a->p[i]);\n\t\t    }\n\t\t    else\n\t\t    {\n\t\t\t// HACK ALERT!\n\t\t\ta->p[i] = anims[1][4].p[i];\n\t\t    }\n\t\t}\n\t    }\n\t}\n    }\n\n    // More hacks on minus sign.\n    callback(DEH_String(\"WIMINUS\"), &wiminus);\n\n    for (i=0;i<10;i++)\n    {\n\t // numbers 0-9\n\tDEH_snprintf(name, 9, \"WINUM%d\", i);\n        callback(name, &num[i]);\n    }\n\n    // percent sign\n    callback(DEH_String(\"WIPCNT\"), &percent);\n\n    // \"finished\"\n    callback(DEH_String(\"WIF\"), &finished);\n\n    // \"entering\"\n    callback(DEH_String(\"WIENTER\"), &entering);\n\n    // \"kills\"\n    callback(DEH_String(\"WIOSTK\"), &kills);\n\n    // \"scrt\"\n    callback(DEH_String(\"WIOSTS\"), &secret);\n\n     // \"secret\"\n    callback(DEH_String(\"WISCRT2\"), &sp_secret);\n\n    // french wad uses WIOBJ (?)\n    if (W_CheckNumForName(DEH_String(\"WIOBJ\")) >= 0)\n    {\n    \t// \"items\"\n    \tif (netgame && !deathmatch)\n            callback(DEH_String(\"WIOBJ\"), &items);\n    \telse\n            callback(DEH_String(\"WIOSTI\"), &items);\n    } else {\n        callback(DEH_String(\"WIOSTI\"), &items);\n    }\n\n    // \"frgs\"\n    callback(DEH_String(\"WIFRGS\"), &frags);\n\n    // \":\"\n    callback(DEH_String(\"WICOLON\"), &colon);\n\n    // \"time\"\n    callback(DEH_String(\"WITIME\"), &timepatch);\n\n    // \"sucks\"\n    callback(DEH_String(\"WISUCKS\"), &sucks);\n\n    // \"par\"\n    callback(DEH_String(\"WIPAR\"), &par);\n\n    // \"killers\" (vertical)\n    callback(DEH_String(\"WIKILRS\"), &killers);\n\n    // \"victims\" (horiz)\n    callback(DEH_String(\"WIVCTMS\"), &victims);\n\n    // \"total\"\n    callback(DEH_String(\"WIMSTT\"), &total);\n\n    for (i=0 ; i<MAXPLAYERS ; i++)\n    {\n\t// \"1,2,3,4\"\n\tDEH_snprintf(name, 9, \"STPB%d\", i);\n        callback(name, &p[i]);\n\n\t// \"1,2,3,4\"\n\tDEH_snprintf(name, 9, \"WIBP%d\", i+1);\n        callback(name, &bp[i]);\n    }\n\n    // Background image\n\n    if (gamemode == commercial)\n    {\n        M_StringCopy(name, DEH_String(\"INTERPIC\"), sizeof(name));\n    }\n    else if (gamemode == retail && wbs->epsd == 3)\n    {\n        M_StringCopy(name, DEH_String(\"INTERPIC\"), sizeof(name));\n    }\n    else\n    {\n\tDEH_snprintf(name, sizeof(name), \"WIMAP%d\", wbs->epsd);\n    }\n\n    // Draw backdrop and save to a temporary buffer\n\n    callback(name, &background);\n}\n\nstatic void WI_loadCallback(char *name, patch_t **variable)\n{\n    *variable = W_CacheLumpName(name, PU_STATIC);\n}\n\nvoid WI_loadData(void)\n{\n    if (gamemode == commercial)\n    {\n\tNUMCMAPS = 32;\n\tlnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMCMAPS,\n\t\t\t\t       PU_STATIC, NULL);\n    }\n    else\n    {\n\tlnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMMAPS,\n\t\t\t\t       PU_STATIC, NULL);\n    }\n\n    WI_loadUnloadData(WI_loadCallback);\n\n    // These two graphics are special cased because we're sharing\n    // them with the status bar code\n\n    // your face\n    star = W_CacheLumpName(DEH_String(\"STFST01\"), PU_STATIC);\n\n    // dead face\n    bstar = W_CacheLumpName(DEH_String(\"STFDEAD0\"), PU_STATIC);\n}\n\nstatic void WI_unloadCallback(char *name, patch_t **variable)\n{\n    W_ReleaseLumpName(name);\n    *variable = NULL;\n}\n\nvoid WI_unloadData(void)\n{\n    WI_loadUnloadData(WI_unloadCallback);\n\n    // We do not free these lumps as they are shared with the status\n    // bar code.\n   \n    // W_ReleaseLumpName(\"STFST01\");\n    // W_ReleaseLumpName(\"STFDEAD0\");\n}\n\nvoid WI_Drawer (void)\n{\n    switch (state)\n    {\n      case StatCount:\n\tif (deathmatch)\n\t    WI_drawDeathmatchStats();\n\telse if (netgame)\n\t    WI_drawNetgameStats();\n\telse\n\t    WI_drawStats();\n\tbreak;\n\t\n      case ShowNextLoc:\n\tWI_drawShowNextLoc();\n\tbreak;\n\t\n      case NoState:\n\tWI_drawNoState();\n\tbreak;\n    }\n}\n\n\nvoid WI_initVariables(wbstartstruct_t* wbstartstruct)\n{\n\n    wbs = wbstartstruct;\n\n#ifdef RANGECHECKING\n    if (gamemode != commercial)\n    {\n      if ( gamemode == retail )\n\tRNGCHECK(wbs->epsd, 0, 3);\n      else\n\tRNGCHECK(wbs->epsd, 0, 2);\n    }\n    else\n    {\n\tRNGCHECK(wbs->last, 0, 8);\n\tRNGCHECK(wbs->next, 0, 8);\n    }\n    RNGCHECK(wbs->pnum, 0, MAXPLAYERS);\n    RNGCHECK(wbs->pnum, 0, MAXPLAYERS);\n#endif\n\n    acceleratestage = 0;\n    cnt = bcnt = 0;\n    firstrefresh = 1;\n    me = wbs->pnum;\n    plrs = wbs->plyr;\n\n    if (!wbs->maxkills)\n\twbs->maxkills = 1;\n\n    if (!wbs->maxitems)\n\twbs->maxitems = 1;\n\n    if (!wbs->maxsecret)\n\twbs->maxsecret = 1;\n\n    if ( gamemode != retail )\n      if (wbs->epsd > 2)\n\twbs->epsd -= 3;\n}\n\nvoid WI_Start(wbstartstruct_t* wbstartstruct)\n{\n    WI_initVariables(wbstartstruct);\n    WI_loadData();\n\n    if (deathmatch)\n\tWI_initDeathmatchStats();\n    else if (netgame)\n\tWI_initNetgameStats();\n    else\n\tWI_initStats();\n}\n"
  },
  {
    "path": "fbdoom/wi_stuff.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//  Intermission.\n//\n\n#ifndef __WI_STUFF__\n#define __WI_STUFF__\n\n//#include \"v_video.h\"\n\n#include \"doomdef.h\"\n\n// States for the intermission\n\ntypedef enum\n{\n    NoState = -1,\n    StatCount,\n    ShowNextLoc,\n} stateenum_t;\n\n// Called by main loop, animate the intermission.\nvoid WI_Ticker (void);\n\n// Called by main loop,\n// draws the intermission directly into the screen buffer.\nvoid WI_Drawer (void);\n\n// Setup for an intermission screen.\nvoid WI_Start(wbstartstruct_t*\t wbstartstruct);\n\n// Shut down the intermission screen\nvoid WI_End(void);\n\n#endif\n"
  },
  {
    "path": "fbdoom/z_zone.c",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//\tZone Memory Allocation. Neat.\n//\n\n\n#include \"z_zone.h\"\n#include \"i_system.h\"\n#include \"doomtype.h\"\n\n\n//\n// ZONE MEMORY ALLOCATION\n//\n// There is never any space between memblocks,\n//  and there will never be two contiguous free memblocks.\n// The rover can be left pointing at a non-empty block.\n//\n// It is of no value to free a cachable block,\n//  because it will get overwritten automatically if needed.\n// \n \n#define MEM_ALIGN sizeof(void *)\n#define ZONEID\t0x1d4a11\n\ntypedef struct memblock_s\n{\n    int\t\t\tsize;\t// including the header and possibly tiny fragments\n    void**\t\tuser;\n    int\t\t\ttag;\t// PU_FREE if this is free\n    int\t\t\tid;\t// should be ZONEID\n    struct memblock_s*\tnext;\n    struct memblock_s*\tprev;\n} memblock_t;\n\n\ntypedef struct\n{\n    // total bytes malloced, including header\n    int\t\tsize;\n\n    // start / end cap for linked list\n    memblock_t\tblocklist;\n    \n    memblock_t*\trover;\n    \n} memzone_t;\n\n\n\nmemzone_t*\tmainzone;\n\n\n\n//\n// Z_ClearZone\n//\nvoid Z_ClearZone (memzone_t* zone)\n{\n    memblock_t*\t\tblock;\n\t\n    // set the entire zone to one free block\n    zone->blocklist.next =\n\tzone->blocklist.prev =\n\tblock = (memblock_t *)( (byte *)zone + sizeof(memzone_t) );\n    \n    zone->blocklist.user = (void *)zone;\n    zone->blocklist.tag = PU_STATIC;\n    zone->rover = block;\n\t\n    block->prev = block->next = &zone->blocklist;\n    \n    // a free block.\n    block->tag = PU_FREE;\n\n    block->size = zone->size - sizeof(memzone_t);\n}\n\n\n\n//\n// Z_Init\n//\nvoid Z_Init (void)\n{\n    memblock_t*\tblock;\n    int\t\tsize;\n\n    mainzone = (memzone_t *)I_ZoneBase (&size);\n    mainzone->size = size;\n\n    // set the entire zone to one free block\n    mainzone->blocklist.next =\n\tmainzone->blocklist.prev =\n\tblock = (memblock_t *)( (byte *)mainzone + sizeof(memzone_t) );\n\n    mainzone->blocklist.user = (void *)mainzone;\n    mainzone->blocklist.tag = PU_STATIC;\n    mainzone->rover = block;\n\t\n    block->prev = block->next = &mainzone->blocklist;\n\n    // free block\n    block->tag = PU_FREE;\n    \n    block->size = mainzone->size - sizeof(memzone_t);\n}\n\n\n//\n// Z_Free\n//\nvoid Z_Free (void* ptr)\n{\n    memblock_t*\t\tblock;\n    memblock_t*\t\tother;\n\t\n    block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));\n\n    if (block->id != ZONEID)\n\tI_Error (\"Z_Free: freed a pointer without ZONEID\");\n\t\t\n    if (block->tag != PU_FREE && block->user != NULL)\n    {\n    \t// clear the user's mark\n\t    *block->user = 0;\n    }\n\n    // mark as free\n    block->tag = PU_FREE;\n    block->user = NULL;\n    block->id = 0;\n\t\n    other = block->prev;\n\n    if (other->tag == PU_FREE)\n    {\n        // merge with previous free block\n        other->size += block->size;\n        other->next = block->next;\n        other->next->prev = other;\n\n        if (block == mainzone->rover)\n            mainzone->rover = other;\n\n        block = other;\n    }\n\t\n    other = block->next;\n    if (other->tag == PU_FREE)\n    {\n        // merge the next free block onto the end\n        block->size += other->size;\n        block->next = other->next;\n        block->next->prev = block;\n\n        if (other == mainzone->rover)\n            mainzone->rover = block;\n    }\n}\n\n\n\n//\n// Z_Malloc\n// You can pass a NULL user if the tag is < PU_PURGELEVEL.\n//\n#define MINFRAGMENT\t\t64\n\n\nvoid*\nZ_Malloc\n( int\t\tsize,\n  int\t\ttag,\n  void*\t\tuser )\n{\n    int\t\textra;\n    memblock_t*\tstart;\n    memblock_t* rover;\n    memblock_t* newblock;\n    memblock_t*\tbase;\n    void *result;\n\n    size = (size + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1);\n    \n    // scan through the block list,\n    // looking for the first free block\n    // of sufficient size,\n    // throwing out any purgable blocks along the way.\n\n    // account for size of block header\n    size += sizeof(memblock_t);\n    \n    // if there is a free block behind the rover,\n    //  back up over them\n    base = mainzone->rover;\n    \n    if (base->prev->tag == PU_FREE)\n        base = base->prev;\n\t\n    rover = base;\n    start = base->prev;\n\t\n    do\n    {\n        if (rover == start)\n        {\n            // scanned all the way around the list\n            I_Error (\"Z_Malloc: failed on allocation of %i bytes\", size);\n        }\n\t\n        if (rover->tag != PU_FREE)\n        {\n            if (rover->tag < PU_PURGELEVEL)\n            {\n                // hit a block that can't be purged,\n                // so move base past it\n                base = rover = rover->next;\n            }\n            else\n            {\n                // free the rover block (adding the size to base)\n\n                // the rover can be the base block\n                base = base->prev;\n                Z_Free ((byte *)rover+sizeof(memblock_t));\n                base = base->next;\n                rover = base->next;\n            }\n        }\n        else\n        {\n            rover = rover->next;\n        }\n\n    } while (base->tag != PU_FREE || base->size < size);\n\n    \n    // found a block big enough\n    extra = base->size - size;\n    \n    if (extra >  MINFRAGMENT)\n    {\n        // there will be a free fragment after the allocated block\n        newblock = (memblock_t *) ((byte *)base + size );\n        newblock->size = extra;\n\t\n        newblock->tag = PU_FREE;\n        newblock->user = NULL;\t\n        newblock->prev = base;\n        newblock->next = base->next;\n        newblock->next->prev = newblock;\n\n        base->next = newblock;\n        base->size = size;\n    }\n\t\n\tif (user == NULL && tag >= PU_PURGELEVEL)\n\t    I_Error (\"Z_Malloc: an owner is required for purgable blocks\");\n\n    base->user = user;\n    base->tag = tag;\n\n    result  = (void *) ((byte *)base + sizeof(memblock_t));\n\n    if (base->user)\n    {\n        *base->user = result;\n    }\n\n    // next allocation will start looking here\n    mainzone->rover = base->next;\t\n\t\n    base->id = ZONEID;\n    \n    return result;\n}\n\n\n\n//\n// Z_FreeTags\n//\nvoid\nZ_FreeTags\n( int\t\tlowtag,\n  int\t\thightag )\n{\n    memblock_t*\tblock;\n    memblock_t*\tnext;\n\t\n    for (block = mainzone->blocklist.next ;\n\t block != &mainzone->blocklist ;\n\t block = next)\n    {\n\t// get link before freeing\n\tnext = block->next;\n\n\t// free block?\n\tif (block->tag == PU_FREE)\n\t    continue;\n\t\n\tif (block->tag >= lowtag && block->tag <= hightag)\n\t    Z_Free ( (byte *)block+sizeof(memblock_t));\n    }\n}\n\n\n\n//\n// Z_DumpHeap\n// Note: TFileDumpHeap( stdout ) ?\n//\nvoid\nZ_DumpHeap\n( int\t\tlowtag,\n  int\t\thightag )\n{\n    memblock_t*\tblock;\n\t\n    printf (\"zone size: %i  location: %p\\n\",\n\t    mainzone->size,mainzone);\n    \n    printf (\"tag range: %i to %i\\n\",\n\t    lowtag, hightag);\n\t\n    for (block = mainzone->blocklist.next ; ; block = block->next)\n    {\n\tif (block->tag >= lowtag && block->tag <= hightag)\n\t    printf (\"block:%p    size:%7i    user:%p    tag:%3i\\n\",\n\t\t    block, block->size, block->user, block->tag);\n\t\t\n\tif (block->next == &mainzone->blocklist)\n\t{\n\t    // all blocks have been hit\n\t    break;\n\t}\n\t\n\tif ( (byte *)block + block->size != (byte *)block->next)\n\t    printf (\"ERROR: block size does not touch the next block\\n\");\n\n\tif ( block->next->prev != block)\n\t    printf (\"ERROR: next block doesn't have proper back link\\n\");\n\n\tif (block->tag == PU_FREE && block->next->tag == PU_FREE)\n\t    printf (\"ERROR: two consecutive free blocks\\n\");\n    }\n}\n\n\n//\n// Z_FileDumpHeap\n//\nvoid Z_FileDumpHeap (FILE* f)\n{\n    memblock_t*\tblock;\n\t\n    fprintf (f,\"zone size: %i  location: %p\\n\",mainzone->size,mainzone);\n\t\n    for (block = mainzone->blocklist.next ; ; block = block->next)\n    {\n\tfprintf (f,\"block:%p    size:%7i    user:%p    tag:%3i\\n\",\n\t\t block, block->size, block->user, block->tag);\n\t\t\n\tif (block->next == &mainzone->blocklist)\n\t{\n\t    // all blocks have been hit\n\t    break;\n\t}\n\t\n\tif ( (byte *)block + block->size != (byte *)block->next)\n\t    fprintf (f,\"ERROR: block size does not touch the next block\\n\");\n\n\tif ( block->next->prev != block)\n\t    fprintf (f,\"ERROR: next block doesn't have proper back link\\n\");\n\n\tif (block->tag == PU_FREE && block->next->tag == PU_FREE)\n\t    fprintf (f,\"ERROR: two consecutive free blocks\\n\");\n    }\n}\n\n\n\n//\n// Z_CheckHeap\n//\nvoid Z_CheckHeap (void)\n{\n    memblock_t*\tblock;\n\t\n    for (block = mainzone->blocklist.next ; ; block = block->next)\n    {\n\tif (block->next == &mainzone->blocklist)\n\t{\n\t    // all blocks have been hit\n\t    break;\n\t}\n\t\n\tif ( (byte *)block + block->size != (byte *)block->next)\n\t    I_Error (\"Z_CheckHeap: block size does not touch the next block\\n\");\n\n\tif ( block->next->prev != block)\n\t    I_Error (\"Z_CheckHeap: next block doesn't have proper back link\\n\");\n\n\tif (block->tag == PU_FREE && block->next->tag == PU_FREE)\n\t    I_Error (\"Z_CheckHeap: two consecutive free blocks\\n\");\n    }\n}\n\n\n\n\n//\n// Z_ChangeTag\n//\nvoid Z_ChangeTag2(void *ptr, int tag, char *file, int line)\n{\n    memblock_t*\tblock;\n\t\n    block = (memblock_t *) ((byte *)ptr - sizeof(memblock_t));\n\n    if (block->id != ZONEID)\n        I_Error(\"%s:%i: Z_ChangeTag: block without a ZONEID!\",\n                file, line);\n\n    if (tag >= PU_PURGELEVEL && block->user == NULL)\n        I_Error(\"%s:%i: Z_ChangeTag: an owner is required \"\n                \"for purgable blocks\", file, line);\n\n    block->tag = tag;\n}\n\nvoid Z_ChangeUser(void *ptr, void **user)\n{\n    memblock_t*\tblock;\n\n    block = (memblock_t *) ((byte *)ptr - sizeof(memblock_t));\n\n    if (block->id != ZONEID)\n    {\n        I_Error(\"Z_ChangeUser: Tried to change user for invalid block!\");\n    }\n\n    block->user = user;\n    *user = ptr;\n}\n\n\n\n//\n// Z_FreeMemory\n//\nint Z_FreeMemory (void)\n{\n    memblock_t*\t\tblock;\n    int\t\t\tfree;\n\t\n    free = 0;\n    \n    for (block = mainzone->blocklist.next ;\n         block != &mainzone->blocklist;\n         block = block->next)\n    {\n        if (block->tag == PU_FREE || block->tag >= PU_PURGELEVEL)\n            free += block->size;\n    }\n\n    return free;\n}\n\nunsigned int Z_ZoneSize(void)\n{\n    return mainzone->size;\n}\n\n"
  },
  {
    "path": "fbdoom/z_zone.h",
    "content": "//\n// Copyright(C) 1993-1996 Id Software, Inc.\n// Copyright(C) 2005-2014 Simon Howard\n//\n// This program is free software; you can redistribute it and/or\n// modify it under the terms of the GNU General Public License\n// as published by the Free Software Foundation; either version 2\n// of the License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// DESCRIPTION:\n//      Zone Memory Allocation, perhaps NeXT ObjectiveC inspired.\n//\tRemark: this was the only stuff that, according\n//\t to John Carmack, might have been useful for\n//\t Quake.\n//\n\n\n\n#ifndef __Z_ZONE__\n#define __Z_ZONE__\n\n#include <stdio.h>\n\n//\n// ZONE MEMORY\n// PU - purge tags.\n\nenum\n{\n    PU_STATIC = 1,                  // static entire execution time\n    PU_SOUND,                       // static while playing\n    PU_MUSIC,                       // static while playing\n    PU_FREE,                        // a free block\n    PU_LEVEL,                       // static until level exited\n    PU_LEVSPEC,                     // a special thinker in a level\n    \n    // Tags >= PU_PURGELEVEL are purgable whenever needed.\n\n    PU_PURGELEVEL,\n    PU_CACHE,\n\n    // Total number of different tag types\n\n    PU_NUM_TAGS\n};\n        \n\nvoid\tZ_Init (void);\nvoid*\tZ_Malloc (int size, int tag, void *ptr);\nvoid    Z_Free (void *ptr);\nvoid    Z_FreeTags (int lowtag, int hightag);\nvoid    Z_DumpHeap (int lowtag, int hightag);\nvoid    Z_FileDumpHeap (FILE *f);\nvoid    Z_CheckHeap (void);\nvoid    Z_ChangeTag2 (void *ptr, int tag, char *file, int line);\nvoid    Z_ChangeUser(void *ptr, void **user);\nint     Z_FreeMemory (void);\nunsigned int Z_ZoneSize(void);\n\n//\n// This is used to get the local FILE:LINE info from CPP\n// prior to really call the function in question.\n//\n#define Z_ChangeTag(p,t)                                       \\\n    Z_ChangeTag2((p), (t), __FILE__, __LINE__)\n\n\n#endif\n"
  },
  {
    "path": "ipx/DOOMNET.C",
    "content": "//#define DOOM2\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <process.h>\n#include <conio.h>\n#include <dos.h>\n\n#include \"doomnet.h\"\n//#include \"ipxstr.h\"\n#include \"ipx_frch.h\"\t\t// FRENCH VERSION\n\ndoomcom_t doomcom;\nint            vectorishooked;\nvoid interrupt (*olddoomvect) (void);\n\n\n\n/*\n=============\n=\n= LaunchDOOM\n=\nThese fields in doomcom should be filled in before calling:\n\n     short     numnodes;      // console is allways node 0\n     short     ticdup;             // 1 = no duplication, 2-5 = dup for \nslow nets\n     short     extratics;          // 1 = send a backup tic in every \npacket\n\n\t short     consoleplayer; // 0-3 = player number\n\t short     numplayers;         // 1-4\n\t short     angleoffset;   // 1 = left, 0 = center, -1 = right\n\t short     drone;              // 1 = drone\n=============\n*/\n\nvoid LaunchDOOM (void)\n{\n\t char *newargs[99];\n\t char adrstring[10];\n\t long      flatadr;\n\n// prepare for DOOM\n\t doomcom.id = DOOMCOM_ID;\n\n// hook the interrupt vector\n\t olddoomvect = getvect (doomcom.intnum);\n     setvect (doomcom.intnum,(void interrupt (*)(void))MK_FP(_CS, \n(int)NetISR));\n     vectorishooked = 1;\n\n// build the argument list for DOOM, adding a -net &doomcom\n     memcpy (newargs, _argv, (_argc+1)*2);\n\t newargs[_argc] = \"-net\";\n\t flatadr = (long)_DS*16 + (unsigned)&doomcom;\n\t sprintf (adrstring,\"%lu\",flatadr);\n\t newargs[_argc+1] = adrstring;\n\t newargs[_argc+2] = NULL;\n\n\t if (!access(\"doom2.exe\",0))\n\t\tspawnv  (P_WAIT, \"doom2\", newargs);\n\t else\n\t\tspawnv  (P_WAIT, \"doom\", newargs);\n\n\t #ifdef DOOM2\n\t printf (STR_RETURNED\"\\n\");\n\t #else\n\t printf (\"Returned from DOOM\\n\");\n\t #endif\n}\n"
  },
  {
    "path": "ipx/DOOMNET.H",
    "content": "// doomnet.h\n\n#define PEL_WRITE_ADR   0x3c8\n#define PEL_DATA        0x3c9\n\n#define I_ColorBlack(r,g,b) {outp(PEL_WRITE_ADR,0);outp(PEL_DATA,r);outp(PEL_DATA,g);outp(PEL_DATA,b);};\n\n\n\n#define   MAXNETNODES         8              // max computers in a game\n#define   MAXPLAYERS          4              // 4 players max + drones\n\n\n#define   CMD_SEND  1\n#define   CMD_GET        2\n\n#define   DOOMCOM_ID          0x12345678l\n\ntypedef struct\n{\n     long id;\n     short     intnum;             // DOOM executes an int to send commands\n\n// communication between DOOM and the driver\n     short     command;       // CMD_SEND or CMD_GET\n     short     remotenode;         // dest for send, set by get (-1 = no packet)\n     short     datalength;         // bytes in doomdata to be sent / bytes read\n\n// info common to all nodes\n     short     numnodes;      // console is allways node 0\n     short     ticdup;             // 1 = no duplication, 2-5 = dup for slow nets\n     short     extratics;          // 1 = send a backup tic in every packet\n     short     deathmatch;         // 1 = deathmatch\n     short     savegame;      // -1 = new game, 0-5 = load savegame\n     short     episode;       // 1-3\n     short     map;           // 1-9\n     short     skill;              // 1-5\n\n// info specific to this node\n     short     consoleplayer; // 0-3 = player number\n     short     numplayers;         // 1-4\n     short     angleoffset;   // 1 = left, 0 = center, -1 = right\n     short     drone;              // 1 = drone\n\n// packet data to be sent\n     char data[512];\n} doomcom_t;\n\n\n\nextern    doomcom_t doomcom;\nextern    void interrupt (*olddoomvect) (void);\nextern    int            vectorishooked;\n\nint CheckParm (char *check);\nvoid LaunchDOOM (void);\nvoid interrupt NetISR (void);\n\n"
  },
  {
    "path": "ipx/IPXNET.C",
    "content": "// ipxnet.c\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <dos.h>\n#include <string.h>\n#include <process.h>\n#include <values.h>\n\n#include \"ipxnet.h\"\n\n/*\n==========================================================================\n===\n\n                              IPX PACKET DRIVER\n\n==========================================================================\n===\n*/\n\npacket_t        packets[NUMPACKETS];\n\nnodeadr_t      nodeadr[MAXNETNODES+1];  // first is local, last is broadcast\n\nnodeadr_t      remoteadr;               // set by each GetPacket\n\nlocaladr_t          localadr;           // set at startup\n\nextern int socketid;\n\nvoid far (*IPX)(void);\n\nlong           localtime;          // for time stamp in packets\nlong           remotetime;\n\n//===========================================================================\n\nint OpenSocket(short socketNumber)\n{\n     _DX = socketNumber;\n     _BX = 0;\n     _AL = 0;\n     IPX();\n     if(_AL)\n\t  Error (\"OpenSocket: 0x%x\", _AL);\n     return _DX;\n}\n\n\nvoid CloseSocket(short socketNumber)\n{\n     _DX = socketNumber;\n     _BX = 1;\n     IPX();\n}\n\nvoid ListenForPacket(ECB *ecb)\n{\n     _SI = FP_OFF(ecb);\n     _ES = FP_SEG(ecb);\n     _BX = 4;\n     IPX();\n     if(_AL)\n          Error (\"ListenForPacket: 0x%x\", _AL);\n}\n\n\nvoid GetLocalAddress (void)\n{\n     _SI = FP_OFF(&localadr);\n     _ES = FP_SEG(&localadr);\n     _BX = 9;\n     IPX();\n}\n\n\n\n/*\n====================\n=\n= InitNetwork\n=\n====================\n*/\n\nvoid InitNetwork (void)\n{\n     int     i,j;\n\n//\n// get IPX function address\n//\n     _AX = 0x7a00;\n     geninterrupt(0x2f);\n     if(_AL != 0xff)\n\t  Error (\"IPX not detected\\n\");\n     IPX = MK_FP(_ES, _DI);\n\n\n//\n// allocate a socket for sending and receiving\n//\n     socketid = OpenSocket ( (socketid>>8) + ((socketid&255)<<8) );\n\n     GetLocalAddress();\n\n//\n// set up several receiving ECBs\n//\n     memset (packets,0,NUMPACKETS*sizeof(packet_t));\n\n     for (i=1 ; i<NUMPACKETS ; i++)\n     {\n          packets[i].ecb.ECBSocket = socketid;\n          packets[i].ecb.FragmentCount = 1;\n          packets[i].ecb.fAddress[0] = FP_OFF(&packets[i].ipx);\n          packets[i].ecb.fAddress[1] = FP_SEG(&packets[i].ipx);\n          packets[i].ecb.fSize = sizeof(packet_t)-sizeof(ECB);\n\n          ListenForPacket (&packets[i].ecb);\n     }\n\n//\n// set up a sending ECB\n//\n     memset (&packets[0],0,sizeof(packets[0]));\n\n     packets[0].ecb.ECBSocket = socketid;\n     packets[0].ecb.FragmentCount = 2;\n     packets[0].ecb.fAddress[0] = FP_OFF(&packets[0].ipx);\n     packets[0].ecb.fAddress[1] = FP_SEG(&packets[0].ipx);\n     for (j=0 ; j<4 ; j++)\n          packets[0].ipx.dNetwork[j] = localadr.network[j];\n     packets[0].ipx.dSocket[0] = socketid&255;\n     packets[0].ipx.dSocket[1] = socketid>>8;\n     packets[0].ecb.f2Address[0] = FP_OFF(&doomcom.data);\n     packets[0].ecb.f2Address[1] = FP_SEG(&doomcom.data);\n\n// known local node at 0\n     for (i=0 ; i<6 ; i++)\n\t  nodeadr[0].node[i] = localadr.node[i];\n\n// broadcast node at MAXNETNODES\n     for (j=0 ; j<6 ; j++)\n          nodeadr[MAXNETNODES].node[j] = 0xff;\n}\n\n\n/*\n====================\n=\n= ShutdownNetwork\n=\n====================\n*/\n\nvoid ShutdownNetwork (void)\n{\n\tif (IPX)\n\t\t\tCloseSocket (socketid);\n}\n\n\n/*\n==============\n=\n= SendPacket\n=\n= A destination of MAXNETNODES is a broadcast\n==============\n*/\n\nvoid SendPacket (int destination)\n{\n     int             j;\n\n// set the time\n     packets[0].time = localtime;\n\n// set the address\n          for (j=0 ; j<6 ; j++)\n               packets[0].ipx.dNode[j] = \npackets[0].ecb.ImmediateAddress[j] =\n               nodeadr[destination].node[j];\n\n// set the length (ipx + time + datalength)\n     packets[0].ecb.fSize = sizeof(IPXPacket) + 4;\n     packets[0].ecb.f2Size = doomcom.datalength + 4;\n\n// send the packet\n     _SI = FP_OFF(&packets[0]);\n     _ES = FP_SEG(&packets[0]);\n     _BX = 3;\n     IPX();\n     if(_AL)\n          Error(\"SendPacket: 0x%x\", _AL);\n\n     while(packets[0].ecb.InUseFlag != 0)\n     {\n          // IPX Relinquish Control - polled drivers MUST have this here!\n          _BX = 10;\n          IPX();\n     }\n}\n\n\nunsigned short ShortSwap (unsigned short i)\n{\n     return ((i&255)<<8) + ((i>>8)&255);\n}\n\n/*\n==============\n=\n= GetPacket\n=\n= Returns false if no packet is waiting\n=\n==============\n*/\n\nint GetPacket (void)\n{\n     int             packetnum;\n     int             i, j;\n     long           besttic;\n     packet_t       *packet;\n\n// if multiple packets are waiting, return them in order by time\n\n     besttic = MAXLONG;\n     packetnum = -1;\n     doomcom.remotenode = -1;\n\n     for ( i = 1 ; i < NUMPACKETS ; i++)\n     {\n          if (packets[i].ecb.InUseFlag)\n          {\n               continue;\n          }\n\n          if (packets[i].time < besttic)\n          {\n               besttic = packets[i].time;\n               packetnum = i;\n          }\n     }\n\n     if (besttic == MAXLONG)\n          return 0;                           // no packets\n\n     packet = &packets[packetnum];\n\n     if (besttic == -1 && localtime != -1)\n     {\n          ListenForPacket (&packet->ecb);\n\t  return 0;            \t// setup broadcast from other game\n     }\n\n     remotetime = besttic;\n\n//\n// got a good packet\n//\n     if (packet->ecb.CompletionCode)\n\t  Error (\"GetPacket: ecb.ComletionCode = 0x%x\",packet->ecb.CompletionCode);\n\n// set remoteadr to the sender of the packet\n     memcpy (&remoteadr, packet->ipx.sNode, sizeof(remoteadr));\n     for (i=0 ; i<doomcom.numnodes ; i++)\n          if (!memcmp(&remoteadr, &nodeadr[i], sizeof(remoteadr)))\n               break;\n     if (i < doomcom.numnodes)\n          doomcom.remotenode = i;\n     else\n     {\n\t  if (localtime != -1)\n          {    // this really shouldn't happen\n               ListenForPacket (&packet->ecb);\n               return 0;\n          }\n     }\n\n// copy out the data\n     doomcom.datalength = ShortSwap(packet->ipx.PacketLength) - 38;\n     memcpy (&doomcom.data, &packet->data, doomcom.datalength);\n\n// repost the ECB\n     ListenForPacket (&packet->ecb);\n\n     return 1;\n}\n\n"
  },
  {
    "path": "ipx/IPXNET.H",
    "content": "// ipxnet.h\n\n\ntypedef struct\n{\n     char private[512];\n} doomdata_t;\n\n\n#include \"DoomNet.h\"\n\n//===========================================================================\n\n#define NUMPACKETS      10              // max outstanding packets before loss\n\n// setupdata_t is used as doomdata_t during setup\ntypedef struct\n{\n     short     gameid;                       // so multiple games can setup at once\n     short     drone;\n     short     nodesfound;\n     short     nodeswanted;\n} setupdata_t;\n\n\n\ntypedef unsigned char BYTE;\ntypedef unsigned short WORD;\ntypedef unsigned long LONG;\n\ntypedef struct IPXPacketStructure\n{\n     WORD    PacketCheckSum;         /* high-low */\n     WORD    PacketLength;           /* high-low */\n     BYTE    PacketTransportControl;\n     BYTE    PacketType;\n\n     BYTE    dNetwork[4];            /* high-low */\n     BYTE    dNode[6];               /* high-low */\n     BYTE    dSocket[2];             /* high-low */\n\n     BYTE    sNetwork[4];            /* high-low */\n     BYTE    sNode[6];               /* high-low */\n     BYTE    sSocket[2];             /* high-low */\n} IPXPacket;\n\n\ntypedef struct\n{\n     BYTE    network[4];             /* high-low */\n     BYTE    node[6];                /* high-low */\n} localadr_t;\n\ntypedef struct\n{\n     BYTE    node[6];                /* high-low */\n} nodeadr_t;\n\ntypedef struct ECBStructure\n{\n     WORD    Link[2];                /* offset-segment */\n     WORD    ESRAddress[2];          /* offset-segment */\n     BYTE    InUseFlag;\n     BYTE    CompletionCode;\n     WORD    ECBSocket;              /* high-low */\n     BYTE    IPXWorkspace[4];        /* N/A */\n     BYTE    DriverWorkspace[12];    /* N/A */\n     BYTE    ImmediateAddress[6];    /* high-low */\n     WORD    FragmentCount;          /* low-high */\n\n     WORD    fAddress[2];            /* offset-segment */\n     WORD    fSize;                  /* low-high */\n\n     WORD    f2Address[2];            /* offset-segment */\n     WORD    f2Size;                  /* low-high */\n} ECB;\n\n\n// time is used by the communication driver to sequence packets returned\n// to DOOM when more than one is waiting\n\ntypedef struct\n{\n     ECB             ecb;\n     IPXPacket       ipx;\n\n     long           time;\n     doomdata_t          data;\n} packet_t;\n\n\nextern    doomcom_t doomcom;\nextern    int            gameid;\n\nextern    nodeadr_t nodeadr[MAXNETNODES+1];\nextern    int            localnodenum;\n\nextern    long           localtime;          // for time stamp in packets\nextern    long      remotetime;         // timestamp of last packet gotten\n\nextern    nodeadr_t remoteadr;\n\nextern\t  int\tmyargc;\n\nextern\t  char  **myargv;\n\nvoid Error (char *error, ...);\n\n\nvoid InitNetwork (void);\nvoid ShutdownNetwork (void);\nvoid SendPacket (int destination);\nint GetPacket (void);\nint CheckParm (char *check);\n\nvoid PrintAddress (nodeadr_t *adr, char *str);\n\n"
  },
  {
    "path": "ipx/IPXSETUP.C",
    "content": "// ipxsetup.c\n\n#define DOOM2\n\n#include <conio.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <dos.h>\n#include <string.h>\n#include <process.h>\n#include <stdarg.h>\n#include <bios.h>\n\n#include \"ipxnet.h\"\n//#include \"ipxstr.h\"\n#include \"ipx_frch.h\"\t// FRENCH VERSION\n\nint gameid;\nint numnetnodes;\nint socketid = 0x869c;        // 0x869c is the official DOOM socket\nint\tmyargc;\nchar **myargv;\n\nsetupdata_t    nodesetup[MAXNETNODES];\n\n\n/*\n=================\n=\n= Error\n=\n= For abnormal program terminations\n=\n=================\n*/\n\nvoid Error (char *error, ...)\n{\n     va_list argptr;\n\n     if (vectorishooked)\n          setvect (doomcom.intnum,olddoomvect);\n\n     va_start (argptr,error);\n     vprintf (error,argptr);\n     va_end (argptr);\n\t printf (\"\\n\");\n     ShutdownNetwork ();\n     exit (1);\n}\n\n\n/*\n=================\n=\n= CheckParm\n=\n= Checks for the given parameter in the program's command line arguments\n=\n= Returns the argument number (1 to argc-1) or 0 if not present\n=\n=================\n*/\n\nint CheckParm(char *parm)\n     {\n     int i;\n\n\t for(i = 1; i < myargc; i++)\n\t\t  if(stricmp(parm, myargv[i]) == 0)\n               return i;\n\n     return 0;\n     }\n\n\n/*\n=============\n=\n= NetISR\n=\n=============\n*/\n\nvoid interrupt NetISR (void)\n{\n     if (doomcom.command == CMD_SEND)\n     {\n          localtime++;\n          SendPacket (doomcom.remotenode);\n     }\n\t else if (doomcom.command == CMD_GET)\n     {\n          GetPacket ();\n     }\n}\n\n\n\n/*\n===================\n=\n= LookForNodes\n=\n= Finds all the nodes for the game and works out player numbers among \nthem\n=\n= Exits with nodesetup[0..numnodes] and nodeadr[0..numnodes] filled in\n===================\n*/\n\nvoid LookForNodes (void)\n{\n     int             i,j,k;\n     int             netids[MAXNETNODES];\n     int             netplayer[MAXNETNODES];\n\t struct time         time;\n     int                 oldsec;\n     setupdata_t         *setup, *dest;\n     char           str[80];\n     int            total, console;\n\n//\n// wait until we get [numnetnodes] packets, then start playing\n// the playernumbers are assigned by netid\n//\n\t printf(STR_ATTEMPT, numnetnodes);\n\n\t printf (STR_LOOKING);\n\n     oldsec = -1;\n     setup = (setupdata_t *)&doomcom.data;\n     localtime = -1;          // in setup time, not game time\n\n//\n// build local setup info\n//\n     nodesetup[0].nodesfound = 1;\n\t nodesetup[0].nodeswanted = numnetnodes;\n     doomcom.numnodes = 1;\n\n     do\n     {\n//\n// check for aborting\n//\n          while ( bioskey(1) )\n          {\n               if ( (bioskey (0) & 0xff) == 27)\n\t\t\t\t\tError (\"\\n\\n\"STR_NETABORT);\n\t\t  }\n\n//\n// listen to the network\n//\n\t\t  while (GetPacket ())\n\t\t  {\n\t\t\t   if (doomcom.remotenode == -1)\n\t\t\t\t\tdest = &nodesetup[doomcom.numnodes];\n\t\t\t   else\n\t\t\t\t\tdest = &nodesetup[doomcom.remotenode];\n\n\t\t\t   if (remotetime != -1)\n\t\t\t   {    // an early game packet, not a setup packet\n\t\t\t\t\tif (doomcom.remotenode == -1)\n\t\t\t Error (STR_UNKNOWN);\n\t\t\t// if it allready started, it must have found all nodes\n\t\t    dest->nodesfound = dest->nodeswanted;\n\t\t    continue;\n\t       }\n\n\t       // update setup ingo\n\t       memcpy (dest, setup, sizeof(*dest) );\n\n\t       if (doomcom.remotenode != -1)\n\t\t    continue;           // allready know that node address\n\n\t       //\n\t       // this is a new node\n\t       //\n\t\t   memcpy (&nodeadr[doomcom.numnodes], &remoteadr\n               , sizeof(nodeadr[doomcom.numnodes]) );\n\n               //\n               // if this node has a lower address, take all startup info\n               //\n               if ( memcmp (&remoteadr, &nodeadr[0], sizeof(&remoteadr) ) \n< 0 )\n               {\n               }\n\n               doomcom.numnodes++;\n\n\t\t\t   printf (\"\\n\"STR_FOUND\"\\n\");\n\n               if (doomcom.numnodes < numnetnodes)\n\t\t\t\t\tprintf (STR_LOOKING);\n          }\n//\n// we are done if all nodes have found all other nodes\n//\n          for (i=0 ; i<doomcom.numnodes ; i++)\n               if (nodesetup[i].nodesfound != nodesetup[i].nodeswanted)\n                    break;\n\n\t\t  if (i == nodesetup[0].nodeswanted)\n\t\t\t   break;         // got them all\n\n//\n// send out a broadcast packet every second\n//\n\t\t  gettime (&time);\n\t\t  if (time.ti_sec == oldsec)\n\t\t\t   continue;\n\t\t  oldsec = time.ti_sec;\n\n\t\t  printf (\".\");\n\t\t  doomcom.datalength = sizeof(*setup);\n\n\t\t  nodesetup[0].nodesfound = doomcom.numnodes;\n\n\t\t  memcpy (&doomcom.data, &nodesetup[0], sizeof(*setup));\n\n\t\t  SendPacket (MAXNETNODES);     // send to all\n\n\t } while (1);\n\n//\n// count players\n//\n\t total = 0;\n\t console = 0;\n\n     for (i=0 ; i<numnetnodes ; i++)\n     {\n          if (nodesetup[i].drone)\n               continue;\n          total++;\n          if (total > MAXPLAYERS)\n\t\t\t   Error (STR_MORETHAN,MAXPLAYERS);\n          if (memcmp (&nodeadr[i], &nodeadr[0], sizeof(nodeadr[0])) < 0)\n               console++;\n     }\n\n\n     if (!total)\n\t\t  Error (STR_NONESPEC);\n\n\t doomcom.consoleplayer = console;\n\t doomcom.numplayers = total;\n\n\t printf (STR_CONSOLEIS\"\\n\", console+1, total);\n}\n\n\n//========================================================\n//\n//\tFind a Response File\n//\n//========================================================\nvoid FindResponseFile (void)\n{\n\tint\t\ti;\n\t#define\tMAXARGVS\t100\n\n\tfor (i = 1;i < myargc;i++)\n\t\tif (myargv[i][0] == '@')\n\t\t{\n\t\t\tFILE *\t\thandle;\n\t\t\tint\t\tsize;\n\t\t\tint\t\tk;\n\t\t\tint\t\tindex;\n\t\t\tint\t\tindexinfile;\n\t\t\tchar\t*infile;\n\t\t\tchar\t*file;\n\t\t\tchar\t*moreargs[20];\n\t\t\tchar\t*firstargv;\n\n\t\t\t// READ THE RESPONSE FILE INTO MEMORY\n\t\t\thandle = fopen (&myargv[i][1],\"rb\");\n\t\t\tif (!handle)\n\t\t\t\tError (STR_NORESP);\n\t\t\tprintf(STR_FOUNDRESP\" \\\"%s\\\"!\\n\",strupr(&myargv[i][1]));\n\t\t\tfseek (handle,0,SEEK_END);\n\t\t\tsize = ftell(handle);\n\t\t\tfseek (handle,0,SEEK_SET);\n\t\t\tfile = malloc (size);\n\t\t\tfread (file,size,1,handle);\n\t\t\tfclose (handle);\n\n\t\t\t// KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG\n\t\t\tfor (index = 0,k = i+1; k < myargc; k++)\n\t\t\t\tmoreargs[index++] = myargv[k];\n\n\t\t\tfirstargv = myargv[0];\n\t\t\tmyargv = malloc(sizeof(char *)*MAXARGVS);\n\t\t\tmemset(myargv,0,sizeof(char *)*MAXARGVS);\n\t\t\tmyargv[0] = firstargv;\n\n\t\t\tinfile = file;\n\t\t\tindexinfile = k = 0;\n\t\t\tindexinfile++;\t// SKIP PAST ARGV[0] (KEEP IT)\n\t\t\tdo\n\t\t\t{\n\t\t\t\tmyargv[indexinfile++] = infile+k;\n\t\t\t\twhile(k < size &&\n\t\t\t\t\t((*(infile+k)>= ' '+1) && (*(infile+k)<='z')))\n\t\t\t\t\tk++;\n\t\t\t\t*(infile+k) = 0;\n\t\t\t\twhile(k < size &&\n\t\t\t\t\t((*(infile+k)<= ' ') || (*(infile+k)>'z')))\n\t\t\t\t\tk++;\n\t\t\t} while(k < size);\n\n\t\t\tfor (k = 0;k < index;k++)\n\t\t\t\tmyargv[indexinfile++] = moreargs[k];\n\t\t\tmyargc = indexinfile;\n\n\t\t\t// DISPLAY ARGS\n//\t\t\tprintf(\"%d command-line args:\\n\",myargc);\n//\t\t\tfor (k=1;k<myargc;k++)\n//\t\t\t\tprintf(\"%s\\n\",myargv[k]);\n\n\t\t\tbreak;\n\t\t}\n}\n\n\n/*\n=============\n=\n= main\n=\n=============\n*/\n\nvoid main (void)\n\t {\n\t int  i;\n\t unsigned char far *vector;\n\n//\n// determine game parameters\n//\n\t gameid = 0;\n\t numnetnodes = 2;\n\t doomcom.ticdup = 1;\n\t doomcom.extratics = 1;\n\t doomcom.episode = 1;\n\t doomcom.map = 1;\n\t doomcom.skill = 2;\n\t doomcom.deathmatch = 0;\n\n\t printf(\"\\n\"\n\t\t\t \"-----------------------------\\n\"\n\t #ifdef DOOM2\n\t\t\t STR_DOOMNETDRV\"\\n\"\n\t #else\n\t\t\t \"DOOM NETWORK DEVICE DRIVER\\n\"\n\t #endif\n\t\t\t \"v1.22\\n\"\n\t\t\t \"-----------------------------\\n\");\n\n\t myargc = _argc;\n\t myargv = _argv;\n\t FindResponseFile();\n\n\t if((i = CheckParm(\"-nodes\")) != 0)\n\t\t  numnetnodes = atoi(myargv[i+1]);\n\n\t if((i = CheckParm(\"-vector\")) != 0)\n\t\t  {\n\t\t  doomcom.intnum = sscanf (\"0x%x\",myargv[i+1]);\n\t\t  vector = *(char far * far *)(doomcom.intnum*4);\n\t\t  if(vector != NULL && *vector != 0xcf)\n\t\t\t   {\n\t\t   printf(STR_VECTSPEC\"\\n\", doomcom.intnum);\n\t\t\t   exit(-1);\n\t\t\t   }\n\t\t  }\n\t else\n\t\t  {\n\t\t  for(doomcom.intnum = 0x60 ; doomcom.intnum <= 0x66 ;\ndoomcom.intnum++)\n\t\t\t   {\n\t\t\t   vector = *(char far * far *)(doomcom.intnum*4);\n\t\t\t   if(vector == NULL || *vector == 0xcf)\n\t\t\t\t\tbreak;\n\t\t\t   }\n\t\t  if(doomcom.intnum == 0x67)\n\t\t\t   {\n\t\t   printf(STR_NONULL\"\\n\");\n\t\t   exit(-1);\n\t\t   }\n\t  }\n\t printf(STR_COMMVECT\"\\n\",doomcom.intnum);\n\n\t if((i = CheckParm(\"-port\")) != 0)\n\t  {\n\t  socketid = atoi (myargv[i+1]);\n\t  printf (STR_USEALT\"\\n\", socketid);\n\t\t  }\n\n\t InitNetwork ();\n\n\t LookForNodes ();\n\n     localtime = 0;\n\n\t LaunchDOOM ();\n\n\t ShutdownNetwork ();\n\n\t if (vectorishooked)\n\t\t  setvect (doomcom.intnum,olddoomvect);\n\n\t exit(0);\n\t }\n\n\n"
  },
  {
    "path": "ipx/IPXSTR.H",
    "content": "#define STR_NETABORT\t\"Network game synchronization aborted.\"\n#define STR_UNKNOWN\t\t\"Got an unknown game packet during setup\"\n#define STR_FOUND\t\t\"Found a node!\"\n#define STR_LOOKING\t\t\"Looking for a node\"\n#define STR_MORETHAN\t\"More than %i players specified!\"\n#define STR_NONESPEC\t\"No players specified for game!\"\n#define STR_CONSOLEIS\t\"Console is player %i of %i\"\n#define STR_NORESP\t\t\"No such response file!\"\n#define STR_FOUNDRESP   \"Found response file\"\n#define STR_DOOMNETDRV\t\"DOOM II NETWORK DEVICE DRIVER\"\n#define STR_VECTSPEC\t\"The specified vector (0x%02x) was already hooked.\"\n#define STR_NONULL\t\\\n\"Warning: no NULL or iret interrupt vectors were found in the 0x60 to 0x66\\n\"\\\n\"range.  You can specify a vector with the -vector 0x<num> parameter.\"\n#define STR_COMMVECT\t\"Communicating with interrupt vector 0x%x\"\n#define STR_USEALT\t\t\"Using alternate port %i for network\"\n#define STR_RETURNED\t\"Returned from DOOM II\"\n#define STR_ATTEMPT\t\t\"Attempting to find all players for %i player net play. \"\\\n\t\t\t\t\t\t\"Press ESC to exit.\\n\"\n"
  },
  {
    "path": "ipx/IPX_FRCH.H",
    "content": "#define STR_NETABORT\t\"Synchronisation du jeu sur rseau annule.\"\n#define STR_UNKNOWN\t\"Paquet de jeu inconnu durant la configuration\"\n#define STR_FOUND\t\t\"Noeud dtect!\"\n#define STR_LOOKING\t\t\"Recherche d'un noeud\"\n#define STR_MORETHAN\t\"Plus de %i joueurs spcifis!\"\n#define STR_NONESPEC\t\"Pas de joueurs spcifis pour le jeu!\"\n#define STR_CONSOLEIS\t\"Console: joueur %i sur %i\"\n#define STR_NORESP\t\t\"Ce fichier de rponse n'existe pas!\"\n#define STR_FOUNDRESP   \"Fichier de rponse trouv\"\n#define STR_DOOMNETDRV\t\"GESTIONNAIRE DE RESEAU DOOM II\"\n#define STR_VECTSPEC\t\"Le vecteur spcifi (0x%02x) tait dj connect.\"\n#define STR_NONULL\t\\\n\"Attention: pas de vecteurs d'interruption NULL ou iret trouvs entre 0x60 et 0x66.\\n\"\\\n\"Vous pouvez spcifier un vecteur avec le paramtre -vector 0x<numro>.\"\n#define STR_COMMVECT\t\"Communication avec le vecteur d'interruption 0x%x\"\n#define STR_USEALT\t\t\"Utilisation du port alternatif %i pour le rseau\"\n#define STR_RETURNED\t\"Retour de DOOM II\"\n#define STR_ATTEMPT \\\n\"Tentatative de recherche de tous les joueurs pour le jeu en riseau `%i jouers\\n\" \\\n\"Appuyez sur ECHAP pour quitter.\\n\"\n\n"
  },
  {
    "path": "ipx/README",
    "content": "This is the source for the DOOM ipx network driver.\n"
  },
  {
    "path": "sersrc/DOOMNET.C",
    "content": "#include <stdio.h>\n#include <io.h>\n#include <stdlib.h>\n#include <string.h>\n#include <process.h>\n#include <dos.h>\n#include \"doomnet.h\"\n\n//#include \"serstr.h\"\n#include \"ser_frch.h\"\t\t// FRENCH VERSION\n\n#define DOOM2\n\nextern\tint\tmyargc;\nextern\tchar **myargv;\n\ndoomcom_t\tdoomcom;\nint\t\t\tvectorishooked;\nvoid interrupt (*olddoomvect) (void);\n\n\n\n/*\n=================\n=\n= CheckParm\n=\n= Checks for the given parameter in the program's command line arguments\n=\n= Returns the argument number (1 to argc-1) or 0 if not present\n=\n=================\n*/\n\nint CheckParm (char *check)\n{\n\tint             i;\n\n\tfor (i = 1;i<myargc;i++)\n\t\tif ( !stricmp(check,myargv[i]) )\n\t\t\treturn i;\n\n\treturn 0;\n}\n\n\n/*\n=============\n=\n= LaunchDOOM\n=\nThese fields in doomcom should be filled in before calling:\n\n\tshort\tnumnodes;\t\t// console is allways node 0\n\tshort\tticdup;\t\t\t// 1 = no duplication, 2-5 = dup for slow nets\n\tshort\textratics;\t\t// 1 = send a backup tic in every packet\n\n\tshort\tconsoleplayer;\t// 0-3 = player number\n\tshort\tnumplayers;\t\t// 1-4\n\tshort\tangleoffset;\t// 1 = left, 0 = center, -1 = right\n\tshort\tdrone;\t\t\t// 1 = drone\n=============\n*/\n\nvoid LaunchDOOM (void)\n{\n\tchar\t*newargs[99];\n\tchar\tadrstring[10];\n\tlong  \tflatadr;\n\tint\t\tp;\n\tunsigned char\tfar\t*vector;\n\n// prepare for DOOM\n\tdoomcom.id = DOOMCOM_ID;\n\n// hook an interrupt vector\n\tp= CheckParm (\"-vector\");\n\n\tif (p)\n\t{\n\t\tdoomcom.intnum = sscanf (\"0x%x\",_argv[p+1]);\n\t}\n\telse\n\t{\n\t\tfor (doomcom.intnum = 0x60 ; doomcom.intnum <= 0x66 ; doomcom.intnum++)\n\t\t{\n\t\t\tvector = *(char far * far *)(doomcom.intnum*4);\n\t\t\tif ( !vector || *vector == 0xcf )\n\t\t\t\tbreak;\n\t\t}\n\t\tif (doomcom.intnum == 0x67)\n\t\t{\n\t\t\tprintf (STR_WARNING);\n\t\t\tdoomcom.intnum = 0x66;\n\t\t}\n\t}\n\tprintf (STR_COMM\"\\n\",doomcom.intnum);\n\n\tolddoomvect = getvect (doomcom.intnum);\n\tsetvect (doomcom.intnum,NetISR);\n\tvectorishooked = 1;\n\n// build the argument list for DOOM, adding a -net &doomcom\n\tmemcpy (newargs, myargv, (myargc+1)*2);\n\tnewargs[myargc] = \"-net\";\n\tflatadr = (long)_DS*16 + (unsigned)&doomcom;\n\tsprintf (adrstring,\"%lu\",flatadr);\n\tnewargs[myargc+1] = adrstring;\n\tnewargs[myargc+2] = NULL;\n\n//\tspawnv  (P_WAIT, \"m:\\\\newdoom\\\\doom\", newargs);\n\tif (!access(\"doom2.exe\",0))\n\t\tspawnv  (P_WAIT, \"doom2\", newargs);\n\telse\n\t\tspawnv  (P_WAIT, \"doom\", newargs);\n\n\t#ifdef DOOM2\n\tprintf (STR_RETURNED\"\\n\");\n\t#else\n\tprintf (\"Returned from DOOM\\n\");\n\t#endif\n\n\n}\n\n\n"
  },
  {
    "path": "sersrc/DOOMNET.H",
    "content": "// doomnet.h\n\n\n#define PEL_WRITE_ADR   0x3c8\n#define PEL_DATA        0x3c9\n\n#define I_ColorBlack(r,g,b) {outp(PEL_WRITE_ADR,0);outp(PEL_DATA,r);outp(PEL_DATA,g);outp(PEL_DATA,b);};\n\n\n\n\n#define\tMAXNETNODES\t\t8\t\t\t// max computers in a game\n#define\tMAXPLAYERS\t\t4\t\t\t// 4 players max + drones\n\n\n#define\tCMD_SEND\t1\n#define\tCMD_GET\t\t2\n\n#define\tDOOMCOM_ID\t\t0x12345678l\n\ntypedef struct\n{\n\tlong\tid;\n\tshort\tintnum;\t\t\t// DOOM executes an int to send commands\n\n// communication between DOOM and the driver\n\tshort\tcommand;\t\t// CMD_SEND or CMD_GET\n\tshort\tremotenode;\t\t// dest for send, set by get (-1 = no packet)\n\tshort\tdatalength;\t\t// bytes in doomdata to be sent / bytes read\n\n// info common to all nodes\n\tshort\tnumnodes;\t\t// console is allways node 0\n\tshort\tticdup;\t\t\t// 1 = no duplication, 2-5 = dup for slow nets\n\tshort\textratics;\t\t// 1 = send a backup tic in every packet\n\tshort\tdeathmatch;\t\t// 1 = deathmatch\n\tshort\tsavegame;\t\t// -1 = new game, 0-5 = load savegame\n\tshort\tepisode;\t\t// 1-3\n\tshort\tmap;\t\t\t// 1-9\n\tshort\tskill;\t\t\t// 1-5\n\n// info specific to this node\n\tshort\tconsoleplayer;\t// 0-3 = player number\n\tshort\tnumplayers;\t\t// 1-4\n\tshort\tangleoffset;\t// 1 = left, 0 = center, -1 = right\n\tshort\tdrone;\t\t\t// 1 = drone\n\n// packet data to be sent\n\tchar\tdata[512];\n} doomcom_t;\n\n\n\nextern\tdoomcom_t\tdoomcom;\nextern\tvoid interrupt (*olddoomvect) (void);\nextern\tint\t\t\tvectorishooked;\n\nint CheckParm (char *check);\nvoid LaunchDOOM (void);\nvoid interrupt NetISR (void);\n\n"
  },
  {
    "path": "sersrc/PORT.C",
    "content": "// port.c\n\n#include \"doomnet.h\"\n#include \"sersetup.h\"\n//#include \"serstr.h\"\n#include \"ser_frch.h\"\t\t// FRENCH VERSION\n\n\nvoid jump_start( void );\n\nvoid interrupt isr_8250 (void);\nvoid interrupt isr_16550 (void);\n\nunion\tREGS\tregs;\nstruct\tSREGS\tsregs;\n\nque_t\t\tinque, outque;\n\n\nint\t\t\tuart;\t\t\t// io address\nenum {UART_8250, UART_16550} uart_type;\nint\t\t\tirq;\n\nint\t\t\tmodem_status = -1;\nint\t\t\tline_status = -1;\n\nvoid interrupt (*oldirqvect) (void);\nint\t\t\tirqintnum;\n\nint\t   \t\tcomport;\n\nint\t\t\tbaudbits;\n\n\n/*\n==============\n=\n= GetUart\n=\n==============\n*/\n\nvoid GetUart (void)\n{\n\tchar   far *system_data;\n\tstatic int ISA_uarts[] = {0x3f8,0x2f8,0x3e8,0x2e8};\n\tstatic int ISA_IRQs[] = {4,3,4,3};\n\tstatic int MCA_uarts[] = {0x03f8,0x02f8,0x3220,0x3228};\n\tstatic int MCA_IRQs[] = {4,3,3,3};\n\tint\t\tp;\n\n\tif (CheckParm (\"-com2\"))\n\t\tcomport = 2;\n\telse if (CheckParm (\"-com3\"))\n\t\tcomport = 3;\n\telse if (CheckParm (\"-com4\"))\n\t\tcomport = 4;\n\telse\n\t\tcomport = 1;\n\n\tregs.h.ah = 0xc0;\n\tint86x( 0x15, &regs, &regs, &sregs );\n\tif ( regs.x.cflag )\n\t{\n\t\tirq = ISA_IRQs[ comport-1 ];\n\t\tuart = ISA_uarts[ comport-1 ];\n\t\treturn;\n\t}\n\tsystem_data = ( char far *) ( ( (long) sregs.es << 16 ) + regs.x.bx );\n\tif ( system_data[ 5 ] & 0x02 )\n\t{\n\t\tirq = MCA_IRQs[ comport-1 ];\n\t\tuart = MCA_uarts[ comport-1 ];\n\t}\n\telse\n\t{\n\t\tirq = ISA_IRQs[ comport-1 ];\n\t\tuart = ISA_uarts[ comport-1 ];\n\t}\n\n\tp = CheckParm (\"-port\");\n\tif (p)\n\t\tsscanf (_argv[p+1],\"0x%x\",&uart);\n\tp = CheckParm (\"-irq\");\n\tif (p)\n\t\tsscanf (_argv[p+1],\"%i\",&irq);\n\n\n\tprintf (STR_PORTLOOK\" 0x%x, irq %i\\n\",uart,irq);\n}\n\n\n\n\n/*\n===============\n=\n= InitPort\n=\n===============\n*/\n\nvoid InitPort (void)\n{\n\tint mcr;\n\tint\ttemp;\n\tint\tu;\n\n//\n// find the irq and io address of the port\n//\n\tGetUart ();\n\n//\n// disable all uart interrupts\n//\n\tOUTPUT( uart + INTERRUPT_ENABLE_REGISTER, 0 );\n\n//\n// init com port settings\n//\n\n\tprintf (STR_PORTSET\"\\n\",115200l/baudbits);\n\n// set baud rate\n\tOUTPUT(uart + LINE_CONTROL_REGISTER, 0x83);\n\tOUTPUT(uart, baudbits);\n\tOUTPUT(uart + 1, 0);\n\n// set line control register (N81)\n\tOUTPUT(uart + LINE_CONTROL_REGISTER, 0x03);\n\n// set modem control register (OUT2+RTS+DTR)\n\tOUTPUT( uart + MODEM_CONTROL_REGISTER,8+2+1);\n\n//\n// check for a 16550\n//\n\tif (CheckParm(\"-8250\"))\n\t// allow a forced 8250\n\t{\n\t\tuart_type = UART_8250;\n\t\tprintf (STR_UART8250\"\\n\\n\");\n\t}\n\telse\n\t{\t\n\t\tOUTPUT ( uart + FIFO_CONTROL_REGISTER,\n\t\t\tFCR_FIFO_ENABLE + FCR_TRIGGER_04 );\n\t\ttemp = INPUT( uart + INTERRUPT_ID_REGISTER );\n\t\tif ( ( temp & 0xf8 ) == 0xc0 )\n\t\t{\n\t\t\tuart_type = UART_16550;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tuart_type = UART_8250;\n\t\t\tOUTPUT( uart + FIFO_CONTROL_REGISTER, 0 );\n\t\t}\n\t}\n\n//\n// clear out any pending uart events\n//\n\tprintf (STR_CLEARPEND);\n\tfor (u=0 ; u<16 ; u++)\t\t// clear an entire 16550 silo\n\t\tINPUT( uart + RECEIVE_BUFFER_REGISTER );\n\n\tdo\n\t{\n\t\tswitch( u = INPUT( uart + INTERRUPT_ID_REGISTER ) & 7 )\n\t\t{\n\t\tcase IIR_MODEM_STATUS_INTERRUPT :\n\t\t\tmodem_status = INPUT( uart + MODEM_STATUS_REGISTER );\n\t\t\tbreak;\n\n\t\tcase IIR_LINE_STATUS_INTERRUPT :\n\t\t\tline_status = INPUT( uart + LINE_STATUS_REGISTER );\n\t\t\tbreak;\n\n\t\tcase IIR_TX_HOLDING_REGISTER_INTERRUPT :\n\t\t\tbreak;\n\n\t\tcase IIR_RX_DATA_READY_INTERRUPT :\n\t\t\tINPUT( uart + RECEIVE_BUFFER_REGISTER );\n\t\t\tbreak;\n\t\t}\n\t} while (! (u&1) );\n\n\n//\n// hook the irq vector\n//\n\tirqintnum = irq + 8;\n\n\toldirqvect = getvect (irqintnum);\n\tif (uart_type == UART_16550)\n\t{\n\t\tsetvect (irqintnum,isr_16550);\n\t\tprintf (STR_UART16550\"\\n\\n\");\n\t}\n\telse\n\t{\n\t\tsetvect (irqintnum,isr_8250);\n\t\tprintf (STR_UART8250\"\\n\\n\");\n\n\t}\n\n\n\tOUTPUT( 0x20 + 1, INPUT( 0x20 + 1 ) & ~(1<<irq) );\n\n\tCLI();\n\n// enable RX and TX interrupts at the uart\n\n\tOUTPUT( uart + INTERRUPT_ENABLE_REGISTER,IER_RX_DATA_READY + IER_TX_HOLDING_REGISTER_EMPTY);\n\n// enable interrupts through the interrupt controller\n\n\tOUTPUT( 0x20, 0xc2 );\n\n\tSTI();\n}\n\n\n/*\n=============\n=\n= ShutdownPort\n=\n=============\n*/\n\nvoid ShutdownPort ( void )\n{\n\tint\t\tu;\n\t\n\tOUTPUT( uart + INTERRUPT_ENABLE_REGISTER, 0 );\n\tOUTPUT( uart + MODEM_CONTROL_REGISTER, 0 );\n\n\tfor (u=0 ; u<16 ; u++)\t\t// clear an entire 16550 silo\n\t\tINPUT( uart + RECEIVE_BUFFER_REGISTER );\n\n\tOUTPUT( 0x20 + 1, INPUT( 0x20 + 1 ) | (1<<irq) );\n\n\tsetvect (irqintnum,oldirqvect);\n\n//\n// init com port settings to defaults\n//\n\tregs.x.ax = 0xf3;\t\t//f3= 9600 n 8 1\n\tregs.x.dx = comport - 1;\n\tint86 (0x14, &regs, &regs);\n}\n\n\nint read_byte( void )\n{\n\tint\tc;\n\n\tif (inque.tail >= inque.head)\n\t\treturn -1;\n\tc = inque.data[inque.tail&(QUESIZE-1)];\n\tinque.tail++;\n\treturn c;\n}\n\n\nvoid write_byte( int c )\n{\n\toutque.data[outque.head&(QUESIZE-1)] = c;\n\toutque.head++;\n}\n\n\n\n//==========================================================================\n\n\n/*\n==============\n=\n= isr_8250\n=\n==============\n*/\n\nvoid interrupt isr_8250(void)\n{\n\tint c;\n\n\twhile (1)\n\t{\n\t\tswitch( INPUT( uart + INTERRUPT_ID_REGISTER ) & 7 )\n\t\t{\n// not enabled\n\t\tcase IIR_MODEM_STATUS_INTERRUPT :\n\t\t\tmodem_status = INPUT( uart + MODEM_STATUS_REGISTER );\n\t\t\tbreak;\n\n// not enabled\n\t\tcase IIR_LINE_STATUS_INTERRUPT :\n\t\t\tline_status = INPUT( uart + LINE_STATUS_REGISTER );\n\t\t\tbreak;\n\n//\n// transmit\n//\n\t\tcase IIR_TX_HOLDING_REGISTER_INTERRUPT :\n//I_ColorBlack (63,0,0);\n\t\t\tif (outque.tail < outque.head)\n\t\t\t{\n\t\t\t\tc = outque.data[outque.tail&(QUESIZE-1)];\n\t\t\t\toutque.tail++;\n\t\t\t\tOUTPUT( uart + TRANSMIT_HOLDING_REGISTER, c );\n\t\t\t}\n\t\t\tbreak;\n\n//\n// receive\n//\n\t\tcase IIR_RX_DATA_READY_INTERRUPT :\n//I_ColorBlack (0,63,0);\n\t\t\tc = INPUT( uart + RECEIVE_BUFFER_REGISTER );\n\t\t\tinque.data[inque.head&(QUESIZE-1)] = c;\n\t\t\tinque.head++;\n\t\t\tbreak;\n\n//\n// done\n//\n\t\tdefault :\n//I_ColorBlack (0,0,0);\n\t\t\tOUTPUT( 0x20, 0x20 );\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n\n/*\n==============\n=\n= isr_16550\n=\n==============\n*/\n\nvoid interrupt isr_16550(void)\n{\n\tint c;\n\tint\tcount;\n\n\twhile (1)\n\t{\n\t\tswitch( INPUT( uart + INTERRUPT_ID_REGISTER ) & 7 )\n\t\t{\n// not enabled\n\t\tcase IIR_MODEM_STATUS_INTERRUPT :\n\t\t\tmodem_status = INPUT( uart + MODEM_STATUS_REGISTER );\n\t\t\tbreak;\n\n// not enabled\n\t\tcase IIR_LINE_STATUS_INTERRUPT :\n\t\t\tline_status = INPUT( uart + LINE_STATUS_REGISTER );\n\t\t\tbreak;\n\n//\n// transmit\n//\n\t\tcase IIR_TX_HOLDING_REGISTER_INTERRUPT :\n//I_ColorBlack (63,0,0);\n\t\t\tcount = 16;\n\t\t\twhile (outque.tail < outque.head && count--)\n\t\t\t{\n\t\t\t\tc = outque.data[outque.tail&(QUESIZE-1)];\n\t\t\t\toutque.tail++;\n\t\t\t\tOUTPUT( uart + TRANSMIT_HOLDING_REGISTER, c );\n\t\t\t}\n\t\t\tbreak;\n\n//\n// receive\n//\n\t\tcase IIR_RX_DATA_READY_INTERRUPT :\n//I_ColorBlack (0,63,0);\n\t\t\tdo\n\t\t\t{\n\t\t\t\tc = INPUT( uart + RECEIVE_BUFFER_REGISTER );\n\t\t\t\tinque.data[inque.head&(QUESIZE-1)] = c;\n\t\t\t\tinque.head++;\n\t\t\t} while (INPUT( uart + LINE_STATUS_REGISTER ) & LSR_DATA_READY );\n\n\t\t\tbreak;\n\n//\n// done\n//\n\t\tdefault :\n//I_ColorBlack (0,0,0);\n\t\t\tOUTPUT( 0x20, 0x20 );\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n\n/*\n===============\n=\n= jump_start\n=\n= Start up the transmition interrupts by sending the first char\n===============\n*/\n\nvoid jump_start( void )\n{\n\tint c;\n\n\tif (outque.tail < outque.head)\n\t{\n\t\tc = outque.data [outque.tail&(QUESIZE-1)];\n\t\toutque.tail++;\n\t\tOUTPUT( uart, c );\n\t}\n}\n\n\n"
  },
  {
    "path": "sersrc/README.TXT",
    "content": "This is the source for the DOOM serial / modem driver.\n"
  },
  {
    "path": "sersrc/SERSETUP.C",
    "content": "// sersetup.c\n#define DOOM2\n#include \"sersetup.h\"\n//#include \"serstr.h\"\n#include \"ser_frch.h\"\t\t// FRENCH VERSION\n#include \"DoomNet.h\"\n\nextern\tque_t\t\tinque, outque;\n\nvoid jump_start( void );\nextern int \tuart;\n\nint\t\t\tusemodem;\nchar\t\tstartup[256], shutdown[256], baudrate[256];\n\nextern\t\tint baudbits;\n\nvoid ModemCommand (char *str);\n\nint\t\tmyargc;\nchar\t**myargv;\n\n//======================================\n//\n// I_Error\n//\n//======================================\nvoid I_Error(char *string)\n{\n\tprintf(\"%s\\n\",string);\n\texit(1);\n}\n\n/*\n================\n=\n= write_buffer\n=\n================\n*/\n\nvoid write_buffer( char *buffer, unsigned int count )\n{\n\tint\ti;\n\n// if this would overrun the buffer, throw everything else out\n\tif (outque.head-outque.tail+count > QUESIZE)\n\t\toutque.tail = outque.head;\n\n\twhile (count--)\n\t\twrite_byte (*buffer++);\n\n\tif ( INPUT( uart + LINE_STATUS_REGISTER ) & 0x40)\n\t\tjump_start();\n}\n\n\n/*\n=================\n=\n= Error\n=\n= For abnormal program terminations\n=\n=================\n*/\n\nvoid Error (char *error, ...)\n{\n\tva_list argptr;\n\n\tif (usemodem)\n\t{\n\t\tprintf (\"\\n\");\n\t\tprintf (\"\\n\"STR_DROPDTR\"\\n\");\n\n\t\tOUTPUT(uart+MODEM_CONTROL_REGISTER, INPUT(uart+MODEM_CONTROL_REGISTER)&~MCR_DTR);\n\t\tdelay (1250);\n\t\tOUTPUT( uart + MODEM_CONTROL_REGISTER, INPUT( uart + MODEM_CONTROL_REGISTER ) | MCR_DTR );\n\t\tModemCommand(\"+++\");\n\t\tdelay (1250);\n\t\tModemCommand(shutdown);\n\t\tdelay (1250);\n\n\t}\n\n\tShutdownPort ();\n\n\tif (vectorishooked)\n\t\tsetvect (doomcom.intnum,olddoomvect);\n\n\tif (error)\n\t{\n\t\tva_start (argptr,error);\n\t\tvprintf (error,argptr);\n\t\tva_end (argptr);\n\t\tprintf (\"\\n\");\n\t\texit (1);\n\t}\n\n\tprintf (STR_CLEANEXIT\"\\n\");\n\texit (0);\n}\n\n\n/*\n================\n=\n= ReadPacket\n=\n================\n*/\n\n#define MAXPACKET\t512\n#define\tFRAMECHAR\t0x70\n\nchar\tpacket[MAXPACKET];\nint\t\tpacketlen;\nint\t\tinescape;\nint\t\tnewpacket;\n\nboolean ReadPacket (void)\n{\n\tint\tc;\n\n// if the buffer has overflowed, throw everything out\n\n\tif (inque.head-inque.tail > QUESIZE - 4)\t// check for buffer overflow\n\t{\n\t\tinque.tail = inque.head;\n\t\tnewpacket = true;\n\t\treturn false;\n\t}\n\n\tif (newpacket)\n\t{\n\t\tpacketlen = 0;\n\t\tnewpacket = 0;\n\t}\n\n\tdo\n\t{\n\t\tc = read_byte ();\n\t\tif (c < 0)\n\t\t\treturn false;\t\t// haven't read a complete packet\n//printf (\"%c\",c);\n\t\tif (inescape)\n\t\t{\n\t\t\tinescape = false;\n\t\t\tif (c!=FRAMECHAR)\n\t\t\t{\n\t\t\t\tnewpacket = 1;\n\t\t\t\treturn true;\t// got a good packet\n\t\t\t}\n\t\t}\n\t\telse if (c==FRAMECHAR)\n\t\t{\n\t\t\tinescape = true;\n\t\t\tcontinue;\t\t\t// don't know yet if it is a terminator\n\t\t}\t\t\t\t\t\t// or a literal FRAMECHAR\n\n\t\tif (packetlen >= MAXPACKET)\n\t\t\tcontinue;\t\t\t// oversize packet\n\t\tpacket[packetlen] = c;\n\t\tpacketlen++;\n\t} while (1);\n\n}\n\n\n/*\n=============\n=\n= WritePacket\n=\n=============\n*/\n\n\n\nvoid WritePacket (char *buffer, int len)\n{\n\tint\t\tb;\n\tchar\tstatic localbuffer[MAXPACKET*2+2];\n\n\tb = 0;\n\tif (len > MAXPACKET)\n\t\treturn;\n\n\twhile (len--)\n\t{\n\t\tif (*buffer == FRAMECHAR)\n\t\t\tlocalbuffer[b++] = FRAMECHAR;\t// escape it for literal\n\t\tlocalbuffer[b++] = *buffer++;\n\t}\n\n\tlocalbuffer[b++] = FRAMECHAR;\n\tlocalbuffer[b++] = 0;\n\n\twrite_buffer (localbuffer, b);\n}\n\n\n/*\n=============\n=\n= NetISR\n=\n=============\n*/\n\nvoid interrupt NetISR (void)\n{\n\tif (doomcom.command == CMD_SEND)\n\t{\n//I_ColorBlack (0,0,63);\n\t\tWritePacket ((char *)&doomcom.data, doomcom.datalength);\n\t}\n\telse if (doomcom.command == CMD_GET)\n\t{\n//I_ColorBlack (63,63,0);\n\n\t\tif (ReadPacket () && packetlen <= sizeof(doomcom.data) )\n\t\t{\n\t\t\tdoomcom.remotenode = 1;\n\t\t\tdoomcom.datalength = packetlen;\n\t\t\tmemcpy (&doomcom.data, &packet, packetlen);\n\t\t}\n\t\telse\n\t\t\tdoomcom.remotenode = -1;\n\n\t}\n//I_ColorBlack (0,0,0);\n}\n\n\n\n\n/*\n=================\n=\n= Connect\n=\n= Figures out who is player 0 and 1\n=================\n*/\n\nvoid Connect (void)\n{\n\tstruct time\ttime;\n\tint\t\t\toldsec;\n\tint\t\t\tlocalstage, remotestage;\n\tchar\t\tstr[20];\n\tchar\t\tidstr[7];\n\tchar\t\tremoteidstr[7];\n\tunsigned long\t\tidnum;\n\tint\t\t\ti;\n\t\n//\n// wait for a good packet\n//\n\tprintf (STR_ATTEMPT\"\\n\");\n\n//\n// build a (hopefully) unique id string by hashing up the current milliseconds\n// and the interrupt table\n//\n\tif (CheckParm (\"-player1\"))\n\t\tidnum = 0;\n\telse if (CheckParm (\"-player2\"))\n\t\tidnum = 999999;\n\telse\n\t{\n\t\tgettime (&time);\n\t\tidnum = time.ti_sec*100+time.ti_hund;\n\t\tfor (i=0 ; i<512 ; i++)\n\t\t\tidnum += ((unsigned far *)0)[i];\n\t\tidnum %= 1000000;\n\t}\n\t\n\tidstr[0] = '0' + idnum/ 100000l;\n\tidnum -= (idstr[0]-'0')*100000l;\n\tidstr[1] = '0' + idnum/ 10000l;\n\tidnum -= (idstr[1]-'0')*10000l;\n\tidstr[2] = '0' + idnum/ 1000l;\n\tidnum -= (idstr[2]-'0')*1000l;\n\tidstr[3] = '0' + idnum/ 100l;\n\tidnum -= (idstr[3]-'0')*100l;\n\tidstr[4] = '0' + idnum/ 10l;\n\tidnum -= (idstr[4]-'0')*10l;\n\tidstr[5] = '0' + idnum;\n\tidstr[6] = 0;\n\t\n//\n// sit in a loop until things are worked out\n//\n// the packet is:  ID000000_0\n// the first field is the idnum, the second is the acknowledge stage\n// ack stage starts out 0, is bumped to 1 after the other computer's id\n// is known, and is bumped to 2 after the other computer has raised to 1\n//\n\toldsec = -1;\n\tlocalstage = remotestage = 0;\n\n\tdo\n\t{\n\t\twhile ( bioskey(1) )\n\t\t{\n\t\t\tif ( (bioskey (0) & 0xff) == 27)\n\t\t\t\tError (\"\\n\\n\"STR_NETABORT);\n\t\t}\n\n\t\tif (ReadPacket ())\n\t\t{\n\t\t\tpacket[packetlen] = 0;\n\t\t\tprintf (\"read : %s\\n\",packet);\n\t\t\tif (packetlen != 10)\n\t\t\t\tcontinue;\n\t\t\tif (strncmp(packet,\"ID\",2) )\n\t\t\t\tcontinue;\n\t\t\tif (!strncmp (packet+2,idstr,6))\n\t\t\t\tError (\"\\n\\n\"STR_DUPLICATE);\n\t\t\tstrncpy (remoteidstr,packet+2,6);\n\t\t\t\t\n\t\t\tremotestage = packet[9] - '0';\n\t\t\tlocalstage = remotestage+1;\n\t\t\toldsec = -1;\n\t\t}\n\n\t\tgettime (&time);\n\t\tif (time.ti_sec != oldsec)\n\t\t{\n\t\t\toldsec = time.ti_sec;\n\t\t\tsprintf (str,\"ID%s_%i\",idstr,localstage);\n\t\t\tWritePacket (str,strlen(str));\n\t\t\tprintf (\"wrote: %s\\n\",str);\n\t\t}\n\n\t} while (localstage < 2);\n\n//\n// decide who is who\n//\n\tif (strcmp(remoteidstr,idstr) > 0)\n\t\tdoomcom.consoleplayer = 0;\n\telse\n\t\tdoomcom.consoleplayer = 1;\n\t\n\n//\n// flush out any extras\n//\n\twhile (ReadPacket ())\n\t;\n}\n\n\n\n/*\n==============\n=\n= ModemCommand\n=\n==============\n*/\n\nvoid ModemCommand (char *str)\n{\n\tint\t\ti,l;\n\t\n\tprintf (STR_MODEMCMD,str);\n\tl = strlen(str);\n\tfor (i=0 ; i<l ; i++)\n\t{\n\t\twrite_buffer (str+i,1);\n\t\tprintf (\"%c\",str[i]);\n\t\tdelay (100);\n\t}\n\n\twrite_buffer (\"\\r\",1);\n\tprintf (\"\\n\");\n}\n\n\n/*\n==============\n=\n= ModemResponse\n=\n= Waits for OK, RING, CONNECT, etc\n==============\n*/\n\nchar\tresponse[80];\n\nvoid ModemResponse (char *resp)\n{\n\tint\t\tc;\n\tint\t\trespptr;\n\n\tdo\n\t{\n\t\tprintf (STR_MODEMRESP);\n\t\trespptr=0;\n\t\tdo\n\t\t{\n\t\t\twhile ( bioskey(1) )\n\t\t\t{\n\t\t\t\tif ( (bioskey (0) & 0xff) == 27)\n\t\t\t\t\tError (\"\\n\"STR_RESPABORT);\n\t\t\t}\n\t\t\tc = read_byte ();\n\t\t\tif (c==-1)\n\t\t\t\tcontinue;\n\t\t\tif (c=='\\n' || respptr == 79)\n\t\t\t{\n\t\t\t\tresponse[respptr] = 0;\n\t\t\t\tprintf (\"%s\\n\",response);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (c>=' ')\n\t\t\t{\n\t\t\t\tresponse[respptr] = c;\n\t\t\t\trespptr++;\n\t\t\t}\n\t\t} while (1);\n\n\t} while (strncmp(response,resp,strlen(resp)));\n}\n\n\n/*\n=============\n=\n= ReadLine\n=\n=============\n*/\n\nvoid ReadLine (FILE *f, char *dest)\n{\n\tint\tc;\n\n\tdo\n\t{\n\t\tc = fgetc (f);\n\t\tif (c == EOF || c == '\\r' || c == '\\n')\n\t\t\tbreak;\n\t\t*dest++ = c;\n\t} while (1);\n\t*dest = 0;\n}\n\n\n/*\n=============\n=\n= ReadModemCfg\n=\n=============\n*/\n\nvoid ReadModemCfg (void)\n{\n\tint\t\tmcr;\n\tFILE\t*f;\n\tunsigned\tbaud;\n\n\tf = fopen (\"modem.cfg\",\"r\");\n\tif (!f)\n\t\tError (STR_CANTREAD);\n\tReadLine (f, startup);\n\tReadLine (f, shutdown);\n\tReadLine (f, baudrate);\n\tfclose (f);\n\n\tbaud = atol(baudrate);\n\tif (baud)\n\t\tbaudbits = 115200l/baud;\n\n\tusemodem = true;\n}\n\n\n/*\n=============\n=\n= Dial\n=\n=============\n*/\n\nvoid Dial (void)\n{\n\tchar\tcmd[80];\n\tint\t\tp;\n\n\tModemCommand(startup);\n\tModemResponse (\"OK\");\n\n\tprintf (\"\\n\"STR_DIALING\"\\n\\n\");\n\tp = CheckParm (\"-dial\");\n\tsprintf (cmd,\"ATDT%s\",myargv[p+1]);\n\n\tModemCommand(cmd);\n\tModemResponse (STR_CONNECT);\n\tdoomcom.consoleplayer = 1;\n}\n\n\n/*\n=============\n=\n= Answer\n=\n=============\n*/\n\nvoid Answer (void)\n{\n\tModemCommand(startup);\n\tModemResponse (\"OK\");\n\tprintf (\"\\n\"STR_WAITRING\"\\n\\n\");\n\n\tModemResponse (STR_RING);\n\tModemCommand (\"ATA\");\n\tModemResponse (STR_CONNECT);\n\n\tdoomcom.consoleplayer = 0;\n}\n\n//========================================================\n//\n//\tFind a Response File\n//\n//========================================================\nvoid FindResponseFile (void)\n{\n\tint\t\ti;\n\t#define\tMAXARGVS\t100\n\n\tfor (i = 1;i < myargc;i++)\n\t\tif (myargv[i][0] == '@')\n\t\t{\n\t\t\tFILE *\t\thandle;\n\t\t\tint\t\tsize;\n\t\t\tint\t\tk;\n\t\t\tint\t\tindex;\n\t\t\tint\t\tindexinfile;\n\t\t\tchar\t*infile;\n\t\t\tchar\t*file;\n\t\t\tchar\t*moreargs[20];\n\t\t\tchar\t*firstargv;\n\n\t\t\t// READ THE RESPONSE FILE INTO MEMORY\n\t\t\thandle = fopen (&myargv[i][1],\"rb\");\n\t\t\tif (!handle)\n\t\t\t\tI_Error (STR_NORESP);\n\t\t\tprintf(\"Found response file \\\"%s\\\"!\\n\",strupr(&myargv[i][1]));\n\t\t\tfseek (handle,0,SEEK_END);\n\t\t\tsize = ftell(handle);\n\t\t\tfseek (handle,0,SEEK_SET);\n\t\t\tfile = malloc (size);\n\t\t\tfread (file,size,1,handle);\n\t\t\tfclose (handle);\n\n\t\t\t// KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG\n\t\t\tfor (index = 0,k = i+1; k < myargc; k++)\n\t\t\t\tmoreargs[index++] = myargv[k];\n\n\t\t\tfirstargv = myargv[0];\n\t\t\tmyargv = malloc(sizeof(char *)*MAXARGVS);\n\t\t\tmemset(myargv,0,sizeof(char *)*MAXARGVS);\n\t\t\tmyargv[0] = firstargv;\n\n\t\t\tinfile = file;\n\t\t\tindexinfile = k = 0;\n\t\t\tindexinfile++;\t// SKIP PAST ARGV[0] (KEEP IT)\n\t\t\tdo\n\t\t\t{\n\t\t\t\tmyargv[indexinfile++] = infile+k;\n\t\t\t\twhile(k < size &&\n\t\t\t\t\t((*(infile+k)>= ' '+1) && (*(infile+k)<='z')))\n\t\t\t\t\tk++;\n\t\t\t\t*(infile+k) = 0;\n\t\t\t\twhile(k < size &&\n\t\t\t\t\t((*(infile+k)<= ' ') || (*(infile+k)>'z')))\n\t\t\t\t\tk++;\n\t\t\t} while(k < size);\n\n\t\t\tfor (k = 0;k < index;k++)\n\t\t\t\tmyargv[indexinfile++] = moreargs[k];\n\t\t\tmyargc = indexinfile;\n\n\t\t\t// DISPLAY ARGS\n//\t\t\tprintf(\"%d command-line args:\\n\",myargc);\n//\t\t\tfor (k=1;k<myargc;k++)\n//\t\t\t\tprintf(\"%s\\n\",myargv[k]);\n\n\t\t\tbreak;\n\t\t}\n}\n\n\n\n/*\n=================\n=\n= main\n=\n=================\n*/\n\nvoid main(void)\n{\n\tint\t\t\t\tp;\n\n//\n// set network characteristics\n//\n\tdoomcom.ticdup = 1;\n\tdoomcom.extratics = 0;\n\tdoomcom.numnodes = 2;\n\tdoomcom.numplayers = 2;\n\tdoomcom.drone = 0;\n\n\tprintf(\"\\n\"\n\t\t   \"---------------------------------\\n\"\n\t\t   #ifdef DOOM2\n\t\t   STR_DOOMSERIAL\"\\n\"\n\t\t   #else\n\t\t   \"DOOM SERIAL DEVICE DRIVER v1.4\\n\"\n\t\t   #endif\n\t\t   \"---------------------------------\\n\");\n\tmyargc = _argc;\n\tmyargv = _argv;\n\tFindResponseFile();\n\n//\n// allow override of automatic player ordering to allow a slower computer\n// to be set as player 1 allways\n//\n\n//\n// establish communications\n//\n\n\tbaudbits = 0x08;\t\t// default to 9600 if not specified on cmd line\n\t\t\t\t\t\t\t// or in modem.cfg\n\n\tif (CheckParm (\"-dial\") || CheckParm (\"-answer\") )\n\t\tReadModemCfg ();\t\t// may set baudbits\n\n//\n// allow command-line override of modem.cfg baud rate\n//\n\tif (CheckParm (\"-9600\")) baudbits = 0x0c;\n\telse if (CheckParm (\"-14400\")) baudbits = 0x08;\n\telse if (CheckParm (\"-19200\")) baudbits = 0x06;\n\telse if (CheckParm (\"-38400\")) baudbits = 0x03;\n\telse if (CheckParm (\"-57600\")) baudbits = 0x02;\n\telse if (CheckParm (\"-115200\")) baudbits = 0x01;\n\n\tInitPort ();\n\n\tif (CheckParm (\"-dial\"))\n\t\tDial ();\n\telse if (CheckParm (\"-answer\"))\n\t\tAnswer ();\n\n\tConnect ();\n\n//\n// launch DOOM\n//\n\tLaunchDOOM ();\n\n\tError (NULL);\n}\n\n"
  },
  {
    "path": "sersrc/SERSETUP.H",
    "content": "#include <conio.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <conio.h>\n#include <ctype.h>\n#include <dos.h>\n#include <process.h>\n#include <stdarg.h>\n#include <bios.h>\n#include <ctype.h>\n\n\n#define INPUT(port)       \tinportb(port)\n#define OUTPUT(port,data)  \toutportb(port,data)\n#define CLI()               disable()\n#define STI()               enable()\n\n\ntypedef enum {false, true} boolean;\ntypedef unsigned char byte;\n\n\n#define TRANSMIT_HOLDING_REGISTER            0x00\n#define RECEIVE_BUFFER_REGISTER              0x00\n#define INTERRUPT_ENABLE_REGISTER            0x01\n#define   IER_RX_DATA_READY                  0x01\n#define   IER_TX_HOLDING_REGISTER_EMPTY      0x02\n#define   IER_LINE_STATUS                    0x04\n#define   IER_MODEM_STATUS                   0x08\n#define INTERRUPT_ID_REGISTER                0x02\n#define   IIR_MODEM_STATUS_INTERRUPT         0x00\n#define   IIR_TX_HOLDING_REGISTER_INTERRUPT  0x02\n#define   IIR_RX_DATA_READY_INTERRUPT        0x04\n#define   IIR_LINE_STATUS_INTERRUPT          0x06\n#define FIFO_CONTROL_REGISTER                0x02\n#define   FCR_FIFO_ENABLE                    0x01\n#define   FCR_RCVR_FIFO_RESET                0x02\n#define   FCR_XMIT_FIFO_RESET                0x04\n#define   FCR_RCVR_TRIGGER_LSB               0x40\n#define   FCR_RCVR_TRIGGER_MSB               0x80\n#define   FCR_TRIGGER_01                     0x00\n#define   FCR_TRIGGER_04                     0x40\n#define   FCR_TRIGGER_08                     0x80\n#define   FCR_TRIGGER_14                     0xc0\n#define LINE_CONTROL_REGISTER                0x03\n#define   LCR_WORD_LENGTH_MASK               0x03\n#define   LCR_WORD_LENGTH_SELECT_0           0x01\n#define   LCR_WORD_LENGTH_SELECT_1           0x02\n#define   LCR_STOP_BITS                      0x04\n#define   LCR_PARITY_MASK                    0x38\n#define   LCR_PARITY_ENABLE                  0x08\n#define   LCR_EVEN_PARITY_SELECT             0x10\n#define   LCR_STICK_PARITY                   0x20\n#define   LCR_SET_BREAK                      0x40\n#define   LCR_DLAB                           0x80\n#define MODEM_CONTROL_REGISTER               0x04\n#define   MCR_DTR                            0x01\n#define   MCR_RTS                            0x02\n#define   MCR_OUT1                           0x04\n#define   MCR_OUT2                           0x08\n#define   MCR_LOOPBACK                       0x10\n#define LINE_STATUS_REGISTER                 0x05\n#define   LSR_DATA_READY                     0x01\n#define   LSR_OVERRUN_ERROR                  0x02\n#define   LSR_PARITY_ERROR                   0x04\n#define   LSR_FRAMING_ERROR                  0x08\n#define   LSR_BREAK_DETECT                   0x10\n#define   LSR_THRE                           0x20\n#define MODEM_STATUS_REGISTER                0x06\n#define   MSR_DELTA_CTS                      0x01\n#define   MSR_DELTA_DSR                      0x02\n#define   MSR_TERI                           0x04\n#define   MSR_DELTA_CD                       0x08\n#define   MSR_CTS                            0x10\n#define   MSR_DSR                            0x20\n#define   MSR_RI                             0x40\n#define   MSR_CD                             0x80\n#define DIVISOR_LATCH_LOW                    0x00\n#define DIVISOR_LATCH_HIGH                   0x01\n\n\n\n#define\tQUESIZE\t2048\n\ntypedef struct\n{\n\tlong\thead, tail;\t\t// bytes are put on head and pulled from tail\n\tunsigned char\tdata[QUESIZE];\n} que_t;\n\nvoid InitPort (void);\nvoid ShutdownPort (void);\n\nint read_byte( void );\nvoid write_byte( int c );\n\n\nvoid Error (char *error, ...);\n\nextern\tint\t\targc;\nextern\tchar\t**argv;\n"
  },
  {
    "path": "sersrc/SERSTR.H",
    "content": "#define STR_DROPDTR\t\t\"Dropping DTR\"\n#define STR_CLEANEXIT\t\"Clean exit from SERSETUP\"\n#define STR_ATTEMPT\t\t\"Attempting to connect across serial link, press escape to abort.\"\n#define STR_NETABORT\t\"Network game synchronization aborted.\"\n#define STR_DUPLICATE\t\"Duplicate id string, try again or check modem init string.\"\n#define STR_MODEMCMD\t\"Modem command : \"\n#define STR_MODEMRESP\t\"Modem response: \"\n#define STR_RESPABORT\t\"Modem response aborted.\"\n#define STR_CANTREAD\t\"Couldn't read MODEM.CFG\"\n#define STR_DIALING\t\t\"Dialing...\"\n#define STR_CONNECT\t\t\"CONNECT\"\n#define STR_WAITRING\t\"Waiting for ring...\"\n#define STR_RING\t\t\"RING\"\n#define STR_NORESP\t\t\"No such response file!\"\n#define STR_DOOMSERIAL\t\"DOOM II SERIAL DEVICE DRIVER v1.4\"\n#define STR_WARNING \\\n\"Warning: no NULL or iret interrupt vectors were found in the 0x60 to 0x66\\n\"\\\n\"range.  You can specify a vector with the -vector 0x<num> parameter.\\n\"\n#define STR_COMM\t\t\"Communicating with interrupt vector 0x%x\"\n#define STR_RETURNED\t\"Returned from DOOM II\"\n#define STR_PORTSET\t\t\"Setting port to %lu baud\"\n"
  },
  {
    "path": "sersrc/SER_FRCH.H",
    "content": "#define STR_DROPDTR\t\t\"Abandon de DTR\"\n#define STR_CLEANEXIT\t\"Sortie normale de SERSETUP\"\n#define STR_ATTEMPT\t\t\"Tentative de connexion en srie, appuyez sur ESC pour annuler.\"\n#define STR_NETABORT\t\"Synchronisation de jeu sur rseau annule.\"\n#define STR_DUPLICATE\t\"Chane id en double. Ressayez ou vrifiez la chane d'initialistion du modem.\"\n#define STR_MODEMCMD\t\"Commande du modem: \"\n#define STR_MODEMRESP\t\"Rponse du modem: \"\n#define STR_RESPABORT\t\"Rponse du modem annule.\"\n#define STR_CANTREAD\t\"Lecture de MODEM.CFG impossible\"\n#define STR_DIALING\t\t\"Composition du numro...\"\n#define STR_CONNECT\t\t\"CONNECTION\"\n#define STR_WAITRING\t\"Attente d'appel...\"\n#define STR_RING\t\t\"APPEL\"\n#define STR_NORESP\t\t\"Ce fichier de rponse n'existe pas!\"\n#define STR_DOOMSERIAL\t\"GESTIONNAIRE DE LIAISON SERIE DOOM II v1.4\"\n#define STR_WARNING \\\n\"Attention: pas de vecteurs d'interruption NULL ou iret trouvs entre 0x60 et 0x66.\\n\"\\\n\"Vous pouvez spcifier un vecteur avec le paramtre -vector 0x<numro>.\"\n#define STR_COMM\t\t\"Communication avec le vecteur d'interruption 0x%x\"\n#define STR_RETURNED\t\"Retour de DOOM II\"\n#define STR_PORTLOOK\t\"Recherche de l'UART sur le port\"\n#define STR_UART8250\t\"UART = 8250\"\n#define STR_UART16550\t\"UART = 16550\"\n#define STR_CLEARPEND\t\"Riinitilisation des interruptions en attente.\\n\"\n#define STR_PORTSET\t\t\"Rglage du port  %lu baud\"\n"
  },
  {
    "path": "sndserv/Makefile",
    "content": "##########################################################\n#\n# $Id:$\n#\n# $Log:$\n#\n#\n\nCC=gcc\nCFLAGS=-O -DNORMALUNIX -DLINUX\nLDFLAGS=\nLIBS=-lm\n\nO=linux\n\nall:\t $(O)/sndserver\n\nclean:\n\trm -f *.o *~ *.flc\n\trm -f linux/*\n\n# Target\n$(O)/sndserver: \\\n\t$(O)/soundsrv.o \\\n\t$(O)/sounds.o \\\n\t$(O)/wadread.o \\\n\t$(O)/linux.o\n\t$(CC) $(CFLAGS) $(LDFLAGS) \\\n\t$(O)/soundsrv.o \\\n\t$(O)/sounds.o \\\n\t$(O)/wadread.o \\\n\t$(O)/linux.o -o $(O)/sndserver $(LIBS)\n\techo make complete.\n\n# Rule\n$(O)/%.o: %.c\n\t$(CC) $(CFLAGS) -c $< -o $@\n\n\n"
  },
  {
    "path": "sndserv/README.sndserv",
    "content": "\nThis is the soundserver as used by the original\nLinuxdoom release. I separated the source from\nthe actual Linuxduum source. For various reasons\nthe separate sound process seems to give the\nbest results - both synchronous and timer driven\noutput demonstrate glitches. These might either\nbe timing issues, or introduced by the changes\nI made to the linux sound code while merging it\nback into the main tree.\n\nNote that neither John Carmack nor Dave Taylor\nare responsible for the current sound handling.\n\n "
  },
  {
    "path": "sndserv/linux.c",
    "content": "// Emacs style mode select   -*- C++ -*- \n//-----------------------------------------------------------------------------\n//\n// $Id: linux.c,v 1.3 1997/01/26 07:45:01 b1 Exp $\n//\n// Copyright (C) 1993-1996 by id Software, Inc.\n//\n// This source is available for distribution and/or modification\n// only under the terms of the DOOM Source Code License as\n// published by id Software. All rights reserved.\n//\n// The source is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License\n// for more details.\n//\n//\n// $Log: linux.c,v $\n// Revision 1.3  1997/01/26 07:45:01  b1\n// 2nd formatting run, fixed a few warnings as well.\n//\n// Revision 1.2  1997/01/21 19:00:01  b1\n// First formatting run:\n//  using Emacs cc-mode.el indentation for C++ now.\n//\n// Revision 1.1  1997/01/19 17:22:45  b1\n// Initial check in DOOM sources as of Jan. 10th, 1997\n//\n//\n// DESCRIPTION:\n//\tUNIX, soundserver for Linux i386.\n//\n//-----------------------------------------------------------------------------\n\nstatic const char rcsid[] = \"$Id: linux.c,v 1.3 1997/01/26 07:45:01 b1 Exp $\";\n\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <fcntl.h>\n#include <unistd.h>\n\n#include <linux/soundcard.h>\n\n#include \"soundsrv.h\"\n\nint\taudio_fd;\n\nvoid\nmyioctl\n( int\tfd,\n  int\tcommand,\n  int*\targ )\n{   \n    int\t\trc;\n    extern int\terrno;\n    \n    rc = ioctl(fd, command, arg);  \n    if (rc < 0)\n    {\n\tfprintf(stderr, \"ioctl(dsp,%d,arg) failed\\n\", command);\n\tfprintf(stderr, \"errno=%d\\n\", errno);\n\texit(-1);\n    }\n}\n\nvoid I_InitMusic(void)\n{\n}\n\nvoid\nI_InitSound\n( int\tsamplerate,\n  int\tsamplesize )\n{\n\n    int i;\n                \n    audio_fd = open(\"/dev/dsp\", O_WRONLY);\n    if (audio_fd<0)\n        fprintf(stderr, \"Could not open /dev/dsp\\n\");\n         \n                     \n    i = 11 | (2<<16);                                           \n    myioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &i);\n                    \n    myioctl(audio_fd, SNDCTL_DSP_RESET, 0);\n    i=11025;\n    myioctl(audio_fd, SNDCTL_DSP_SPEED, &i);\n    i=1;    \n    myioctl(audio_fd, SNDCTL_DSP_STEREO, &i);\n            \n    myioctl(audio_fd, SNDCTL_DSP_GETFMTS, &i);\n    if (i&=AFMT_S16_LE)    \n        myioctl(audio_fd, SNDCTL_DSP_SETFMT, &i);\n    else\n        fprintf(stderr, \"Could not play signed 16 data\\n\");\n\n}\n\nvoid\nI_SubmitOutputBuffer\n( void*\tsamples,\n  int\tsamplecount )\n{\n    write(audio_fd, samples, samplecount*4);\n}\n\nvoid I_ShutdownSound(void)\n{\n\n    close(audio_fd);\n\n}\n\nvoid I_ShutdownMusic(void)\n{\n}\n"
  },
  {
    "path": "sndserv/sounds.c",
    "content": "// Emacs style mode select   -*- C++ -*- \n//-----------------------------------------------------------------------------\n//\n// $Id: sounds.c,v 1.3 1997/01/29 22:40:44 b1 Exp $\n//\n// Copyright (C) 1993-1996 by id Software, Inc.\n//\n// This source is available for distribution and/or modification\n// only under the terms of the DOOM Source Code License as\n// published by id Software. All rights reserved.\n//\n// The source is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License\n// for more details.\n//\n//\n// $Log: sounds.c,v $\n// Revision 1.3  1997/01/29 22:40:44  b1\n// Reformatting, S (sound) module files.\n//\n// Revision 1.2  1997/01/21 19:00:07  b1\n// First formatting run:\n//  using Emacs cc-mode.el indentation for C++ now.\n//\n// Revision 1.1  1997/01/19 17:22:50  b1\n// Initial check in DOOM sources as of Jan. 10th, 1997\n//\n//\n// DESCRIPTION:\n//\tCreated by Dave Taylor's sound utility.\n//\tKept as a sample, DOOM sounds.\n//\n//-----------------------------------------------------------------------------\n\n\nstatic const char rcsid[] = \"$Id: sounds.c,v 1.3 1997/01/29 22:40:44 b1 Exp $\";\n\n\n\n// Not exactly a good idea.\nenum { false, true };\n\n#include \"sounds.h\"\n\n//\n// Information about all the music\n//\n\nmusicinfo_t S_music[] =\n{\n    { 0 },\n    { \"e1m1\", 0 },\n    { \"e1m2\", 0 },\n    { \"e1m3\", 0 },\n    { \"e1m4\", 0 },\n    { \"e1m5\", 0 },\n    { \"e1m6\", 0 },\n    { \"e1m7\", 0 },\n    { \"e1m8\", 0 },\n    { \"e1m9\", 0 },\n    { \"e2m1\", 0 },\n    { \"e2m2\", 0 },\n    { \"e2m3\", 0 },\n    { \"e2m4\", 0 },\n    { \"e2m5\", 0 },\n    { \"e2m6\", 0 },\n    { \"e2m7\", 0 },\n    { \"e2m8\", 0 },\n    { \"e2m9\", 0 },\n    { \"e3m1\", 0 },\n    { \"e3m2\", 0 },\n    { \"e3m3\", 0 },\n    { \"e3m4\", 0 },\n    { \"e3m5\", 0 },\n    { \"e3m6\", 0 },\n    { \"e3m7\", 0 },\n    { \"e3m8\", 0 },\n    { \"e3m9\", 0 },\n    { \"inter\", 0 },\n    { \"intro\", 0 },\n    { \"bunny\", 0 },\n    { \"victor\", 0 },\n    { \"introa\", 0 },\n    { \"runnin\", 0 },\n    { \"stalks\", 0 },\n    { \"countd\", 0 },\n    { \"betwee\", 0 },\n    { \"doom\", 0 },\n    { \"the_da\", 0 },\n    { \"shawn\", 0 },\n    { \"ddtblu\", 0 },\n    { \"in_cit\", 0 },\n    { \"dead\", 0 },\n    { \"stlks2\", 0 },\n    { \"theda2\", 0 },\n    { \"doom2\", 0 },\n    { \"ddtbl2\", 0 },\n    { \"runni2\", 0 },\n    { \"dead2\", 0 },\n    { \"stlks3\", 0 },\n    { \"romero\", 0 },\n    { \"shawn2\", 0 },\n    { \"messag\", 0 },\n    { \"count2\", 0 },\n    { \"ddtbl3\", 0 },\n    { \"ampie\", 0 },\n    { \"theda3\", 0 },\n    { \"adrian\", 0 },\n    { \"messg2\", 0 },\n    { \"romer2\", 0 },\n    { \"tense\", 0 },\n    { \"shawn3\", 0 },\n    { \"openin\", 0 },\n    { \"evil\", 0 },\n    { \"ultima\", 0 },\n    { \"read_m\", 0 },\n    { \"dm2ttl\", 0 },\n    { \"dm2int\", 0 } \n};\n\n\n//\n// Information about all the sfx\n//\n\nsfxinfo_t S_sfx[] =\n{\n    { 0 },\n    { \"pistol\", false, 64, 0, -1, -1, 0 },\n    { \"shotgn\", false, 64, 0, -1, -1, 0 },\n    { \"sgcock\", false, 64, 0, -1, -1, 0 },\n    { \"dshtgn\", false, 64, 0, -1, -1, 0 },\n    { \"dbopn\", false, 64, 0, -1, -1, 0 },\n    { \"dbcls\", false, 64, 0, -1, -1, 0 },\n    { \"dbload\", false, 64, 0, -1, -1, 0 },\n    { \"plasma\", false, 64, 0, -1, -1, 0 },\n    { \"bfg\", false, 64, 0, -1, -1, 0 },\n    { \"sawup\", false, 64, 0, -1, -1, 0 },\n    { \"sawidl\", false, 118, 0, -1, -1, 0 },\n    { \"sawful\", false, 64, 0, -1, -1, 0 },\n    { \"sawhit\", false, 64, 0, -1, -1, 0 },\n    { \"rlaunc\", false, 64, 0, -1, -1, 0 },\n    { \"rxplod\", false, 70, 0, -1, -1, 0 },\n    { \"firsht\", false, 70, 0, -1, -1, 0 },\n    { \"firxpl\", false, 70, 0, -1, -1, 0 },\n    { \"pstart\", false, 100, 0, -1, -1, 0 },\n    { \"pstop\", false, 100, 0, -1, -1, 0 },\n    { \"doropn\", false, 100, 0, -1, -1, 0 },\n    { \"dorcls\", false, 100, 0, -1, -1, 0 },\n    { \"stnmov\", false, 119, 0, -1, -1, 0 },\n    { \"swtchn\", false, 78, 0, -1, -1, 0 },\n    { \"swtchx\", false, 78, 0, -1, -1, 0 },\n    { \"plpain\", false, 96, 0, -1, -1, 0 },\n    { \"dmpain\", false, 96, 0, -1, -1, 0 },\n    { \"popain\", false, 96, 0, -1, -1, 0 },\n    { \"vipain\", false, 96, 0, -1, -1, 0 },\n    { \"mnpain\", false, 96, 0, -1, -1, 0 },\n    { \"pepain\", false, 96, 0, -1, -1, 0 },\n    { \"slop\", false, 78, 0, -1, -1, 0 },\n    { \"itemup\", true, 78, 0, -1, -1, 0 },\n    { \"wpnup\", true, 78, 0, -1, -1, 0 },\n    { \"oof\", false, 96, 0, -1, -1, 0 },\n    { \"telept\", false, 32, 0, -1, -1, 0 },\n    { \"posit1\", true, 98, 0, -1, -1, 0 },\n    { \"posit2\", true, 98, 0, -1, -1, 0 },\n    { \"posit3\", true, 98, 0, -1, -1, 0 },\n    { \"bgsit1\", true, 98, 0, -1, -1, 0 },\n    { \"bgsit2\", true, 98, 0, -1, -1, 0 },\n    { \"sgtsit\", true, 98, 0, -1, -1, 0 },\n    { \"cacsit\", true, 98, 0, -1, -1, 0 },\n    { \"brssit\", true, 94, 0, -1, -1, 0 },\n    { \"cybsit\", true, 92, 0, -1, -1, 0 },\n    { \"spisit\", true, 90, 0, -1, -1, 0 },\n    { \"bspsit\", true, 90, 0, -1, -1, 0 },\n    { \"kntsit\", true, 90, 0, -1, -1, 0 },\n    { \"vilsit\", true, 90, 0, -1, -1, 0 },\n    { \"mansit\", true, 90, 0, -1, -1, 0 },\n    { \"pesit\", true, 90, 0, -1, -1, 0 },\n    { \"sklatk\", false, 70, 0, -1, -1, 0 },\n    { \"sgtatk\", false, 70, 0, -1, -1, 0 },\n    { \"skepch\", false, 70, 0, -1, -1, 0 },\n    { \"vilatk\", false, 70, 0, -1, -1, 0 },\n    { \"claw\", false, 70, 0, -1, -1, 0 },\n    { \"skeswg\", false, 70, 0, -1, -1, 0 },\n    { \"pldeth\", false, 32, 0, -1, -1, 0 },\n    { \"pdiehi\", false, 32, 0, -1, -1, 0 },\n    { \"podth1\", false, 70, 0, -1, -1, 0 },\n    { \"podth2\", false, 70, 0, -1, -1, 0 },\n    { \"podth3\", false, 70, 0, -1, -1, 0 },\n    { \"bgdth1\", false, 70, 0, -1, -1, 0 },\n    { \"bgdth2\", false, 70, 0, -1, -1, 0 },\n    { \"sgtdth\", false, 70, 0, -1, -1, 0 },\n    { \"cacdth\", false, 70, 0, -1, -1, 0 },\n    { \"skldth\", false, 70, 0, -1, -1, 0 },\n    { \"brsdth\", false, 32, 0, -1, -1, 0 },\n    { \"cybdth\", false, 32, 0, -1, -1, 0 },\n    { \"spidth\", false, 32, 0, -1, -1, 0 },\n    { \"bspdth\", false, 32, 0, -1, -1, 0 },\n    { \"vildth\", false, 32, 0, -1, -1, 0 },\n    { \"kntdth\", false, 32, 0, -1, -1, 0 },\n    { \"pedth\", false, 32, 0, -1, -1, 0 },\n    { \"skedth\", false, 32, 0, -1, -1, 0 },\n    { \"posact\", true, 120, 0, -1, -1, 0 },\n    { \"bgact\", true, 120, 0, -1, -1, 0 },\n    { \"dmact\", true, 120, 0, -1, -1, 0 },\n    { \"bspact\", true, 100, 0, -1, -1, 0 },\n    { \"bspwlk\", true, 100, 0, -1, -1, 0 },\n    { \"vilact\", true, 100, 0, -1, -1, 0 },\n    { \"noway\", false, 78, 0, -1, -1, 0 },\n    { \"barexp\", false, 60, 0, -1, -1, 0 },\n    { \"punch\", false, 64, 0, -1, -1, 0 },\n    { \"hoof\", false, 70, 0, -1, -1, 0 },\n    { \"metal\", false, 70, 0, -1, -1, 0 },\n    { \"chgun\", false, 64, &S_sfx[sfx_pistol], 150, 0, 0 },\n    { \"tink\", false, 60, 0, -1, -1, 0 },\n    { \"bdopn\", false, 100, 0, -1, -1, 0 },\n    { \"bdcls\", false, 100, 0, -1, -1, 0 },\n    { \"itmbk\", false, 100, 0, -1, -1, 0 },\n    { \"flame\", false, 32, 0, -1, -1, 0 },\n    { \"flamst\", false, 32, 0, -1, -1, 0 },\n    { \"getpow\", false, 60, 0, -1, -1, 0 },\n    { \"bospit\", false, 70, 0, -1, -1, 0 },\n    { \"boscub\", false, 70, 0, -1, -1, 0 },\n    { \"bossit\", false, 70, 0, -1, -1, 0 },\n    { \"bospn\", false, 70, 0, -1, -1, 0 },\n    { \"bosdth\", false, 70, 0, -1, -1, 0 },\n    { \"manatk\", false, 70, 0, -1, -1, 0 },\n    { \"mandth\", false, 70, 0, -1, -1, 0 },\n    { \"sssit\", false, 70, 0, -1, -1, 0 },\n    { \"ssdth\", false, 70, 0, -1, -1, 0 },\n    { \"keenpn\", false, 70, 0, -1, -1, 0 },\n    { \"keendt\", false, 70, 0, -1, -1, 0 },\n    { \"skeact\", false, 70, 0, -1, -1, 0 },\n    { \"skesit\", false, 70, 0, -1, -1, 0 },\n    { \"skeatk\", false, 70, 0, -1, -1, 0 },\n    { \"radio\", false, 60, 0, -1, -1, 0 } \n};\n\n"
  },
  {
    "path": "sndserv/sounds.h",
    "content": "// Emacs style mode select   -*- C++ -*- \n//-----------------------------------------------------------------------------\n//\n// $Id: sounds.h,v 1.3 1997/01/29 22:40:44 b1 Exp $\n//\n// Copyright (C) 1993-1996 by id Software, Inc.\n//\n// This source is available for distribution and/or modification\n// only under the terms of the DOOM Source Code License as\n// published by id Software. All rights reserved.\n//\n// The source is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License\n// for more details.\n//\n//\n// $Log: sounds.h,v $\n// Revision 1.3  1997/01/29 22:40:44  b1\n// Reformatting, S (sound) module files.\n//\n// Revision 1.2  1997/01/21 19:00:07  b1\n// First formatting run:\n//  using Emacs cc-mode.el indentation for C++ now.\n//\n// Revision 1.1  1997/01/19 17:22:50  b1\n// Initial check in DOOM sources as of Jan. 10th, 1997\n//\n//\n// DESCRIPTION:\n//\tCreated by Dave Taylor's sound utility.\n//\tKept as a sample, DOOM sounds.\n//\n//-----------------------------------------------------------------------------\n\n#ifndef __SOUNDSH__\n#define __SOUNDSH__\n\n#include \"soundst.h\"\n\n//\n// Identifiers for all music in game.\n//\n\ntypedef enum\n{\n    mus_None,\n    mus_e1m1,\n    mus_e1m2,\n    mus_e1m3,\n    mus_e1m4,\n    mus_e1m5,\n    mus_e1m6,\n    mus_e1m7,\n    mus_e1m8,\n    mus_e1m9,\n    mus_e2m1,\n    mus_e2m2,\n    mus_e2m3,\n    mus_e2m4,\n    mus_e2m5,\n    mus_e2m6,\n    mus_e2m7,\n    mus_e2m8,\n    mus_e2m9,\n    mus_e3m1,\n    mus_e3m2,\n    mus_e3m3,\n    mus_e3m4,\n    mus_e3m5,\n    mus_e3m6,\n    mus_e3m7,\n    mus_e3m8,\n    mus_e3m9,\n    mus_inter,\n    mus_intro,\n    mus_bunny,\n    mus_victor,\n    mus_introa,\n    mus_runnin,\n    mus_stalks,\n    mus_countd,\n    mus_betwee,\n    mus_doom,\n    mus_the_da,\n    mus_shawn,\n    mus_ddtblu,\n    mus_in_cit,\n    mus_dead,\n    mus_stlks2,\n    mus_theda2,\n    mus_doom2,\n    mus_ddtbl2,\n    mus_runni2,\n    mus_dead2,\n    mus_stlks3,\n    mus_romero,\n    mus_shawn2,\n    mus_messag,\n    mus_count2,\n    mus_ddtbl3,\n    mus_ampie,\n    mus_theda3,\n    mus_adrian,\n    mus_messg2,\n    mus_romer2,\n    mus_tense,\n    mus_shawn3,\n    mus_openin,\n    mus_evil,\n    mus_ultima,\n    mus_read_m,\n    mus_dm2ttl,\n    mus_dm2int,\n    NUMMUSIC\n} musicenum_t;\n\n\n//\n// Identifiers for all sfx in game.\n//\n\ntypedef enum\n{\n    sfx_None,\n    sfx_pistol,\n    sfx_shotgn,\n    sfx_sgcock,\n    sfx_dshtgn,\n    sfx_dbopn,\n    sfx_dbcls,\n    sfx_dbload,\n    sfx_plasma,\n    sfx_bfg,\n    sfx_sawup,\n    sfx_sawidl,\n    sfx_sawful,\n    sfx_sawhit,\n    sfx_rlaunc,\n    sfx_rxplod,\n    sfx_firsht,\n    sfx_firxpl,\n    sfx_pstart,\n    sfx_pstop,\n    sfx_doropn,\n    sfx_dorcls,\n    sfx_stnmov,\n    sfx_swtchn,\n    sfx_swtchx,\n    sfx_plpain,\n    sfx_dmpain,\n    sfx_popain,\n    sfx_vipain,\n    sfx_mnpain,\n    sfx_pepain,\n    sfx_slop,\n    sfx_itemup,\n    sfx_wpnup,\n    sfx_oof,\n    sfx_telept,\n    sfx_posit1,\n    sfx_posit2,\n    sfx_posit3,\n    sfx_bgsit1,\n    sfx_bgsit2,\n    sfx_sgtsit,\n    sfx_cacsit,\n    sfx_brssit,\n    sfx_cybsit,\n    sfx_spisit,\n    sfx_bspsit,\n    sfx_kntsit,\n    sfx_vilsit,\n    sfx_mansit,\n    sfx_pesit,\n    sfx_sklatk,\n    sfx_sgtatk,\n    sfx_skepch,\n    sfx_vilatk,\n    sfx_claw,\n    sfx_skeswg,\n    sfx_pldeth,\n    sfx_pdiehi,\n    sfx_podth1,\n    sfx_podth2,\n    sfx_podth3,\n    sfx_bgdth1,\n    sfx_bgdth2,\n    sfx_sgtdth,\n    sfx_cacdth,\n    sfx_skldth,\n    sfx_brsdth,\n    sfx_cybdth,\n    sfx_spidth,\n    sfx_bspdth,\n    sfx_vildth,\n    sfx_kntdth,\n    sfx_pedth,\n    sfx_skedth,\n    sfx_posact,\n    sfx_bgact,\n    sfx_dmact,\n    sfx_bspact,\n    sfx_bspwlk,\n    sfx_vilact,\n    sfx_noway,\n    sfx_barexp,\n    sfx_punch,\n    sfx_hoof,\n    sfx_metal,\n    sfx_chgun,\n    sfx_tink,\n    sfx_bdopn,\n    sfx_bdcls,\n    sfx_itmbk,\n    sfx_flame,\n    sfx_flamst,\n    sfx_getpow,\n    sfx_bospit,\n    sfx_boscub,\n    sfx_bossit,\n    sfx_bospn,\n    sfx_bosdth,\n    sfx_manatk,\n    sfx_mandth,\n    sfx_sssit,\n    sfx_ssdth,\n    sfx_keenpn,\n    sfx_keendt,\n    sfx_skeact,\n    sfx_skesit,\n    sfx_skeatk,\n    sfx_radio,\n    NUMSFX\n} sfxenum_t;\n\nextern musicinfo_t S_music[];\nextern sfxinfo_t   S_sfx[];\n\n#endif\n\n"
  },
  {
    "path": "sndserv/soundsrv.c",
    "content": "// Emacs style mode select   -*- C++ -*- \n//-----------------------------------------------------------------------------\n//\n// $Id: soundsrv.c,v 1.3 1997/01/29 22:40:44 b1 Exp $\n//\n// Copyright (C) 1993-1996 by id Software, Inc.\n//\n// This source is available for distribution and/or modification\n// only under the terms of the DOOM Source Code License as\n// published by id Software. All rights reserved.\n//\n// The source is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License\n// for more details.\n//\n//\n// $Log: soundsrv.c,v $\n// Revision 1.3  1997/01/29 22:40:44  b1\n// Reformatting, S (sound) module files.\n//\n// Revision 1.2  1997/01/21 19:00:07  b1\n// First formatting run:\n//  using Emacs cc-mode.el indentation for C++ now.\n//\n// Revision 1.1  1997/01/19 17:22:50  b1\n// Initial check in DOOM sources as of Jan. 10th, 1997\n//\n//\n// DESCRIPTION:\n//\tUNIX soundserver, run as a separate process,\n//\t started by DOOM program.\n//\tOriginally conceived fopr SGI Irix,\n//\t mostly used with Linux voxware.\n//\n//-----------------------------------------------------------------------------\n\n\nstatic const char rcsid[] = \"$Id: soundsrv.c,v 1.3 1997/01/29 22:40:44 b1 Exp $\";\n\n\n\n#include <math.h>\n#include <sys/types.h>\n#include <stdio.h>\n#include <fcntl.h>\n#include <sys/ioctl.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <malloc.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n\n#include \"sounds.h\"\n#include \"soundsrv.h\"\n#include \"wadread.h\"\n\n\n\n//\n// Department of Redundancy Department.\n//\ntypedef struct wadinfo_struct\n{\n    // should be IWAD\n    char\tidentification[4];\t\n    int\t\tnumlumps;\n    int\t\tinfotableofs;\n    \n} wadinfo_t;\n\n\ntypedef struct filelump_struct\n{\n    int\t\tfilepos;\n    int\t\tsize;\n    char\tname[8];\n    \n} filelump_t;\n\n\n// an internal time keeper\nstatic int\tmytime = 0;\n\n// number of sound effects\nint \t\tnumsounds;\n\n// longest sound effect\nint \t\tlongsound;\n\n// lengths of all sound effects\nint \t\tlengths[NUMSFX];\n\n// mixing buffer\nsigned short\tmixbuffer[MIXBUFFERSIZE];\n\n// file descriptor of sfx device\nint\t\tsfxdevice;\t\t\t\n\n// file descriptor of music device\nint \t\tmusdevice;\t\t\t\n\n// the channel data pointers\nunsigned char*\tchannels[8];\n\n// the channel step amount\nunsigned int\tchannelstep[8];\n\n// 0.16 bit remainder of last step\nunsigned int\tchannelstepremainder[8];\n\n// the channel data end pointers\nunsigned char*\tchannelsend[8];\n\n// time that the channel started playing\nint\t\tchannelstart[8];\n\n// the channel handles\nint \t\tchannelhandles[8];\n\n// the channel left volume lookup\nint*\t\tchannelleftvol_lookup[8];\n\n// the channel right volume lookup\nint*\t\tchannelrightvol_lookup[8];\n\n// sfx id of the playing sound effect\nint\t\tchannelids[8];\t\t\t\n\nint\t\tsnd_verbose=1;\n\nint\t\tsteptable[256];\n\nint\t\tvol_lookup[128*256];\n\nstatic void derror(char* msg)\n{\n    fprintf(stderr, \"error: %s\\n\", msg);\n    exit(-1);\n}\n\nint mix(void)\n{\n\n    register int\t\tdl;\n    register int\t\tdr;\n    register unsigned int\tsample;\n    \n    signed short*\t\tleftout;\n    signed short*\t\trightout;\n    signed short*\t\tleftend;\n    \n    int\t\t\t\tstep;\n\n    leftout = mixbuffer;\n    rightout = mixbuffer+1;\n    step = 2;\n\n    leftend = mixbuffer + SAMPLECOUNT*step;\n\n    // mix into the mixing buffer\n    while (leftout != leftend)\n    {\n\n\tdl = 0;\n\tdr = 0;\n\n\tif (channels[0])\n\t{\n\t    sample = *channels[0];\n\t    dl += channelleftvol_lookup[0][sample];\n\t    dr += channelrightvol_lookup[0][sample];\n\t    channelstepremainder[0] += channelstep[0];\n\t    channels[0] += channelstepremainder[0] >> 16;\n\t    channelstepremainder[0] &= 65536-1;\n\n\t    if (channels[0] >= channelsend[0])\n\t\tchannels[0] = 0;\n\t}\n\n\tif (channels[1])\n\t{\n\t    sample = *channels[1];\n\t    dl += channelleftvol_lookup[1][sample];\n\t    dr += channelrightvol_lookup[1][sample];\n\t    channelstepremainder[1] += channelstep[1];\n\t    channels[1] += channelstepremainder[1] >> 16;\n\t    channelstepremainder[1] &= 65536-1;\n\n\t    if (channels[1] >= channelsend[1])\n\t\tchannels[1] = 0;\n\t}\n\n\tif (channels[2])\n\t{\n\t    sample = *channels[2];\n\t    dl += channelleftvol_lookup[2][sample];\n\t    dr += channelrightvol_lookup[2][sample];\n\t    channelstepremainder[2] += channelstep[2];\n\t    channels[2] += channelstepremainder[2] >> 16;\n\t    channelstepremainder[2] &= 65536-1;\n\n\t    if (channels[2] >= channelsend[2])\n\t\tchannels[2] = 0;\n\t}\n\t\n\tif (channels[3])\n\t{\n\t    sample = *channels[3];\n\t    dl += channelleftvol_lookup[3][sample];\n\t    dr += channelrightvol_lookup[3][sample];\n\t    channelstepremainder[3] += channelstep[3];\n\t    channels[3] += channelstepremainder[3] >> 16;\n\t    channelstepremainder[3] &= 65536-1;\n\n\t    if (channels[3] >= channelsend[3])\n\t\tchannels[3] = 0;\n\t}\n\t\n\tif (channels[4])\n\t{\n\t    sample = *channels[4];\n\t    dl += channelleftvol_lookup[4][sample];\n\t    dr += channelrightvol_lookup[4][sample];\n\t    channelstepremainder[4] += channelstep[4];\n\t    channels[4] += channelstepremainder[4] >> 16;\n\t    channelstepremainder[4] &= 65536-1;\n\n\t    if (channels[4] >= channelsend[4])\n\t\tchannels[4] = 0;\n\t}\n\t\n\tif (channels[5])\n\t{\n\t    sample = *channels[5];\n\t    dl += channelleftvol_lookup[5][sample];\n\t    dr += channelrightvol_lookup[5][sample];\n\t    channelstepremainder[5] += channelstep[5];\n\t    channels[5] += channelstepremainder[5] >> 16;\n\t    channelstepremainder[5] &= 65536-1;\n\n\t    if (channels[5] >= channelsend[5])\n\t\tchannels[5] = 0;\n\t}\n\t\n\tif (channels[6])\n\t{\n\t    sample = *channels[6];\n\t    dl += channelleftvol_lookup[6][sample];\n\t    dr += channelrightvol_lookup[6][sample];\n\t    channelstepremainder[6] += channelstep[6];\n\t    channels[6] += channelstepremainder[6] >> 16;\n\t    channelstepremainder[6] &= 65536-1;\n\n\t    if (channels[6] >= channelsend[6])\n\t\tchannels[6] = 0;\n\t}\n\tif (channels[7])\n\t{\n\t    sample = *channels[7];\n\t    dl += channelleftvol_lookup[7][sample];\n\t    dr += channelrightvol_lookup[7][sample];\n\t    channelstepremainder[7] += channelstep[7];\n\t    channels[7] += channelstepremainder[7] >> 16;\n\t    channelstepremainder[7] &= 65536-1;\n\n\t    if (channels[7] >= channelsend[7])\n\t\tchannels[7] = 0;\n\t}\n\n\t// Has been char instead of short.\n\t// if (dl > 127) *leftout = 127;\n\t// else if (dl < -128) *leftout = -128;\n\t// else *leftout = dl;\n\n\t// if (dr > 127) *rightout = 127;\n\t// else if (dr < -128) *rightout = -128;\n\t// else *rightout = dr;\n\t\n\tif (dl > 0x7fff)\n\t    *leftout = 0x7fff;\n\telse if (dl < -0x8000)\n\t    *leftout = -0x8000;\n\telse\n\t    *leftout = dl;\n\n\tif (dr > 0x7fff)\n\t    *rightout = 0x7fff;\n\telse if (dr < -0x8000)\n\t    *rightout = -0x8000;\n\telse\n\t    *rightout = dr;\n\n\tleftout += step;\n\trightout += step;\n\n    }\n    return 1;\n}\n\n\n\nvoid\ngrabdata\n( int\t\tc,\n  char**\tv )\n{\n    int\t\ti;\n    char*\tname;\n    char*\tdoom1wad;\n    char*\tdoomwad;\n    char*\tdoomuwad;\n    char*\tdoom2wad;\n    char*\tdoom2fwad;\n    // Now where are TNT and Plutonia. Yuck.\n    \n    //\tchar *home;\n    char*\tdoomwaddir;\n\n    doomwaddir = getenv(\"DOOMWADDIR\");\n\n    if (!doomwaddir)\n\tdoomwaddir = \".\";\n\n    doom1wad = malloc(strlen(doomwaddir)+1+9+1);\n    sprintf(doom1wad, \"%s/doom1.wad\", doomwaddir);\n\n    doom2wad = malloc(strlen(doomwaddir)+1+9+1);\n    sprintf(doom2wad, \"%s/doom2.wad\", doomwaddir);\n\n    doom2fwad = malloc(strlen(doomwaddir)+1+10+1);\n    sprintf(doom2fwad, \"%s/doom2f.wad\", doomwaddir);\n    \n    doomuwad = malloc(strlen(doomwaddir)+1+8+1);\n    sprintf(doomuwad, \"%s/doomu.wad\", doomwaddir);\n    \n    doomwad = malloc(strlen(doomwaddir)+1+8+1);\n    sprintf(doomwad, \"%s/doom.wad\", doomwaddir);\n\n    //\thome = getenv(\"HOME\");\n    //\tif (!home)\n    //\t  derror(\"Please set $HOME to your home directory\");\n    //\tsprintf(basedefault, \"%s/.doomrc\", home);\n\n\n    for (i=1 ; i<c ; i++)\n    {\n\tif (!strcmp(v[i], \"-quiet\"))\n\t{\n\t    snd_verbose = 0;\n\t}\n    }\n\n    numsounds = NUMSFX;\n    longsound = 0;\n\n    if (! access(doom2fwad, R_OK) )\n\tname = doom2fwad;\n    else if (! access(doom2wad, R_OK) )\n\tname = doom2wad;\n    else if (! access(doomuwad, R_OK) )\n\tname = doomuwad;\n    else if (! access(doomwad, R_OK) )\n\tname = doomwad;\n    else if (! access(doom1wad, R_OK) )\n\tname = doom1wad;\n    // else if (! access(DEVDATA \"doom2.wad\", R_OK) )\n    //   name = DEVDATA \"doom2.wad\";\n    //   else if (! access(DEVDATA \"doom.wad\", R_OK) )\n    //   name = DEVDATA \"doom.wad\";\n    else\n    {\n\tfprintf(stderr, \"Could not find wadfile anywhere\\n\");\n\texit(-1);\n    }\n\n    \n    openwad(name);\n    if (snd_verbose)\n\tfprintf(stderr, \"loading from [%s]\\n\", name);\n\n    for (i=1 ; i<NUMSFX ; i++)\n    {\n\tif (!S_sfx[i].link)\n\t{\n\t    S_sfx[i].data = getsfx(S_sfx[i].name, &lengths[i]);\n\t    if (longsound < lengths[i]) longsound = lengths[i];\n\t} else {\n\t    S_sfx[i].data = S_sfx[i].link->data;\n\t    lengths[i] = lengths[(S_sfx[i].link - S_sfx)/sizeof(sfxinfo_t)];\n\t}\n\t// test only\n\t//  {\n\t//  int fd;\n\t//  char name[10];\n\t//  sprintf(name, \"sfx%d\", i);\n\t//  fd = open(name, O_WRONLY|O_CREAT, 0644);\n\t//  write(fd, S_sfx[i].data, lengths[i]);\n\t//  close(fd);\n\t//  }\n    }\n\n}\n\nstatic struct timeval\t\tlast={0,0};\n//static struct timeval\t\tnow;\n\nstatic struct timezone\t\twhocares;\n\nvoid updatesounds(void)\n{\n\n    mix();\n    I_SubmitOutputBuffer(mixbuffer, SAMPLECOUNT);\n\n}\n\nint\naddsfx\n( int\t\tsfxid,\n  int\t\tvolume,\n  int\t\tstep,\n  int\t\tseperation )\n{\n    static unsigned short\thandlenums = 0;\n \n    int\t\ti;\n    int\t\trc = -1;\n    \n    int\t\toldest = mytime;\n    int\t\toldestnum = 0;\n    int\t\tslot;\n    int\t\trightvol;\n    int\t\tleftvol;\n\n    // play these sound effects\n    //  only one at a time\n    if ( sfxid == sfx_sawup\n\t || sfxid == sfx_sawidl\n\t || sfxid == sfx_sawful\n\t || sfxid == sfx_sawhit\n\t || sfxid == sfx_stnmov\n\t || sfxid == sfx_pistol )\n    {\n\tfor (i=0 ; i<8 ; i++)\n\t{\n\t    if (channels[i] && channelids[i] == sfxid)\n\t    {\n\t\tchannels[i] = 0;\n\t\tbreak;\n\t    }\n\t}\n    }\n\n    for (i=0 ; i<8 && channels[i] ; i++)\n    {\n\tif (channelstart[i] < oldest)\n\t{\n\t    oldestnum = i;\n\t    oldest = channelstart[i];\n\t}\n    }\n\n    if (i == 8)\n\tslot = oldestnum;\n    else\n\tslot = i;\n\n    channels[slot] = (unsigned char *) S_sfx[sfxid].data;\n    channelsend[slot] = channels[slot] + lengths[sfxid];\n\n    if (!handlenums)\n\thandlenums = 100;\n    \n    channelhandles[slot] = rc = handlenums++;\n    channelstep[slot] = step;\n    channelstepremainder[slot] = 0;\n    channelstart[slot] = mytime;\n\n    // (range: 1 - 256)\n    seperation += 1;\n\n    // (x^2 seperation)\n    leftvol =\n\tvolume - (volume*seperation*seperation)/(256*256);\n\n    seperation = seperation - 257;\n\n    // (x^2 seperation)\n    rightvol =\n\tvolume - (volume*seperation*seperation)/(256*256);\t\n\n    // sanity check\n    if (rightvol < 0 || rightvol > 127)\n\tderror(\"rightvol out of bounds\");\n    \n    if (leftvol < 0 || leftvol > 127)\n\tderror(\"leftvol out of bounds\");\n    \n    // get the proper lookup table piece\n    //  for this volume level\n    channelleftvol_lookup[slot] = &vol_lookup[leftvol*256];\n    channelrightvol_lookup[slot] = &vol_lookup[rightvol*256];\n\n    channelids[slot] = sfxid;\n\n    return rc;\n\n}\n\n\nvoid outputushort(int num)\n{\n\n    static unsigned char\tbuff[5] = { 0, 0, 0, 0, '\\n' };\n    static char*\t\tbadbuff = \"xxxx\\n\";\n\n    // outputs a 16-bit # in hex or \"xxxx\" if -1.\n    if (num < 0)\n    {\n\twrite(1, badbuff, 5);\n    }\n    else\n    {\n\tbuff[0] = num>>12;\n\tbuff[0] += buff[0] > 9 ? 'a'-10 : '0';\n\tbuff[1] = (num>>8) & 0xf;\n\tbuff[1] += buff[1] > 9 ? 'a'-10 : '0';\n\tbuff[2] = (num>>4) & 0xf;\n\tbuff[2] += buff[2] > 9 ? 'a'-10 : '0';\n\tbuff[3] = num & 0xf;\n\tbuff[3] += buff[3] > 9 ? 'a'-10 : '0';\n\twrite(1, buff, 5);\n    }\n}\n\nvoid initdata(void)\n{\n\n    int\t\ti;\n    int\t\tj;\n    \n    int*\tsteptablemid = steptable + 128;\n\n    for (i=0 ;\n\t i<sizeof(channels)/sizeof(unsigned char *) ;\n\t i++)\n    {\n\tchannels[i] = 0;\n    }\n    \n    gettimeofday(&last, &whocares);\n\n    for (i=-128 ; i<128 ; i++)\n\tsteptablemid[i] = pow(2.0, (i/64.0))*65536.0;\n\n    // generates volume lookup tables\n    //  which also turn the unsigned samples\n    //  into signed samples\n    // for (i=0 ; i<128 ; i++)\n    // for (j=0 ; j<256 ; j++)\n    // vol_lookup[i*256+j] = (i*(j-128))/127;\n    \n    for (i=0 ; i<128 ; i++)\n\tfor (j=0 ; j<256 ; j++)\n\t    vol_lookup[i*256+j] = (i*(j-128)*256)/127;\n\n}\n\n\n\n\nvoid quit(void)\n{\n    I_ShutdownMusic();\n    I_ShutdownSound();\n    exit(0);\n}\n\n\n\nfd_set\t\tfdset;\nfd_set\t\tscratchset;\n\n\n\nint\nmain\n( int\t\tc,\n  char**\tv )\n{\n\n    int\t\tdone = 0;\n    int\t\trc;\n    int\t\tnrc;\n    int\t\tsndnum;\n    int\t\thandle = 0;\n    \n    unsigned char\tcommandbuf[10];\n    struct timeval\tzerowait = { 0, 0 };\n\n    \n    int \tstep;\n    int \tvol;\n    int\t\tsep;\n    \n    int\t\ti;\n    int\t\twaitingtofinish=0;\n\n    // get sound data\n    grabdata(c, v);\n\n    // init any data\n    initdata();\t\t\n\n    I_InitSound(11025, 16);\n\n    I_InitMusic();\n\n    if (snd_verbose)\n\tfprintf(stderr, \"ready\\n\");\n    \n    // parse commands and play sounds until done\n    FD_ZERO(&fdset);\n    FD_SET(0, &fdset);\n\n    while (!done)\n    {\n\tmytime++;\n\n\tif (!waitingtofinish)\n\t{\n\t    do {\n\t\tscratchset = fdset;\n\t\trc = select(FD_SETSIZE, &scratchset, 0, 0, &zerowait);\n\n\t\tif (rc > 0)\n\t\t{\n\t\t    //\tfprintf(stderr, \"select is true\\n\");\n\t\t    // got a command\n\t\t    nrc = read(0, commandbuf, 1);\n\n\t\t    if (!nrc)\n\t\t    {\n\t\t\tdone = 1;\n\t\t\trc = 0;\n\t\t    }\n\t\t    else\n\t\t    {\n\t\t\tif (snd_verbose)\n\t\t\t    fprintf(stderr, \"cmd: %c\", commandbuf[0]);\n\n\t\t\tswitch (commandbuf[0])\n\t\t\t{\n\t\t\t  case 'p':\n\t\t\t    // play a new sound effect\n\t\t\t    read(0, commandbuf, 9);\n\n\t\t\t    if (snd_verbose)\n\t\t\t    {\n\t\t\t\tcommandbuf[9]=0;\n\t\t\t\tfprintf(stderr, \"%s\\n\", commandbuf);\n\t\t\t    }\n\n\t\t\t    commandbuf[0] -=\n\t\t\t\tcommandbuf[0]>='a' ? 'a'-10 : '0';\n\t\t\t    commandbuf[1] -=\n\t\t\t\tcommandbuf[1]>='a' ? 'a'-10 : '0';\n\t\t\t    commandbuf[2] -=\n\t\t\t\tcommandbuf[2]>='a' ? 'a'-10 : '0';\n\t\t\t    commandbuf[3] -=\n\t\t\t\tcommandbuf[3]>='a' ? 'a'-10 : '0';\n\t\t\t    commandbuf[4] -=\n\t\t\t\tcommandbuf[4]>='a' ? 'a'-10 : '0';\n\t\t\t    commandbuf[5] -=\n\t\t\t\tcommandbuf[5]>='a' ? 'a'-10 : '0';\n\t\t\t    commandbuf[6] -=\n\t\t\t\tcommandbuf[6]>='a' ? 'a'-10 : '0';\n\t\t\t    commandbuf[7] -=\n\t\t\t\tcommandbuf[7]>='a' ? 'a'-10 : '0';\n\n\t\t\t    //\tp<snd#><step><vol><sep>\n\t\t\t    sndnum = (commandbuf[0]<<4) + commandbuf[1];\n\t\t\t    step = (commandbuf[2]<<4) + commandbuf[3];\n\t\t\t    step = steptable[step];\n\t\t\t    vol = (commandbuf[4]<<4) + commandbuf[5];\n\t\t\t    sep = (commandbuf[6]<<4) + commandbuf[7];\n\n\t\t\t    handle = addsfx(sndnum, vol, step, sep);\n\t\t\t    // returns the handle\n\t\t\t    //\toutputushort(handle);\n\t\t\t    break;\n\t\t\t    \n\t\t\t  case 'q':\n\t\t\t    read(0, commandbuf, 1);\n\t\t\t    waitingtofinish = 1; rc = 0;\n\t\t\t    break;\n\t\t\t    \n\t\t\t  case 's':\n\t\t\t  {\n\t\t\t      int fd;\n\t\t\t      read(0, commandbuf, 3);\n\t\t\t      commandbuf[2] = 0;\n\t\t\t      fd = open((char*)commandbuf, O_CREAT|O_WRONLY, 0644);\n\t\t\t      commandbuf[0] -= commandbuf[0]>='a' ? 'a'-10 : '0';\n\t\t\t      commandbuf[1] -= commandbuf[1]>='a' ? 'a'-10 : '0';\n\t\t\t      sndnum = (commandbuf[0]<<4) + commandbuf[1];\n\t\t\t      write(fd, S_sfx[sndnum].data, lengths[sndnum]);\n\t\t\t      close(fd);\n\t\t\t  }\n\t\t\t  break;\n\t\t\t  \n\t\t\t  default:\n\t\t\t    fprintf(stderr, \"Did not recognize command\\n\");\n\t\t\t    break;\n\t\t\t}\n\t\t    }\n\t\t}\n\t\telse if (rc < 0)\n\t\t{\n\t\t    quit();\n\t\t}\n\t    } while (rc > 0);\n\t}\n\n\tupdatesounds();\n\n\tif (waitingtofinish)\n\t{\n\t    for(i=0 ; i<8 && !channels[i] ; i++);\n\t    \n\t    if (i==8)\n\t\tdone=1;\n\t}\n\n    }\n\n    quit();\n    return 0;\n}\n"
  },
  {
    "path": "sndserv/soundsrv.h",
    "content": "// Emacs style mode select   -*- C++ -*- \n//-----------------------------------------------------------------------------\n//\n// $Id: soundsrv.h,v 1.3 1997/01/29 22:40:44 b1 Exp $\n//\n// Copyright (C) 1993-1996 by id Software, Inc.\n//\n// This source is available for distribution and/or modification\n// only under the terms of the DOOM Source Code License as\n// published by id Software. All rights reserved.\n//\n// The source is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License\n// for more details.\n//\n//\n// $Log: soundsrv.h,v $\n// Revision 1.3  1997/01/29 22:40:44  b1\n// Reformatting, S (sound) module files.\n//\n// Revision 1.2  1997/01/21 19:00:07  b1\n// First formatting run:\n//  using Emacs cc-mode.el indentation for C++ now.\n//\n// Revision 1.1  1997/01/19 17:22:50  b1\n// Initial check in DOOM sources as of Jan. 10th, 1997\n//\n//\n// DESCRIPTION:\n//\tUNIX soundserver, separate process. \n//\n//-----------------------------------------------------------------------------\n\n#ifndef __SNDSERVER_H__\n#define __SNDSERVER_H__\n\n#define SAMPLECOUNT\t\t512\n#define MIXBUFFERSIZE\t(SAMPLECOUNT*2*2)\n#define SPEED\t\t\t11025\n\n\nvoid I_InitMusic(void);\n\nvoid\nI_InitSound\n( int\t\tsamplerate,\n  int\t\tsamplesound );\n\nvoid\nI_SubmitOutputBuffer\n( void*\t\tsamples,\n  int\t\tsamplecount );\n\nvoid I_ShutdownSound(void);\nvoid I_ShutdownMusic(void);\n\n#endif\n"
  },
  {
    "path": "sndserv/soundst.h",
    "content": "// Emacs style mode select   -*- C++ -*- \n//-----------------------------------------------------------------------------\n//\n// $Id: soundst.h,v 1.3 1997/01/29 22:40:45 b1 Exp $\n//\n// Copyright (C) 1993-1996 by id Software, Inc.\n//\n// This source is available for distribution and/or modification\n// only under the terms of the DOOM Source Code License as\n// published by id Software. All rights reserved.\n//\n// The source is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License\n// for more details.\n//\n//\n// $Log: soundst.h,v $\n// Revision 1.3  1997/01/29 22:40:45  b1\n// Reformatting, S (sound) module files.\n//\n// Revision 1.2  1997/01/21 19:00:07  b1\n// First formatting run:\n//  using Emacs cc-mode.el indentation for C++ now.\n//\n// Revision 1.1  1997/01/19 17:22:50  b1\n// Initial check in DOOM sources as of Jan. 10th, 1997\n//\n//\n// DESCRIPTION:\n//\tSound (utility) related. Hang on.\n//\tSee gensounds.h and gensounds.c for what soundst.h is made of.\n//\n//-----------------------------------------------------------------------------\n\n#ifndef __SOUNDSTH__\n#define __SOUNDSTH__\n\n#define S_MAX_VOLUME\t\t127\n\n// when to clip out sounds\n// Doesn't fit the large outdoor areas.\n#define S_CLIPPING_DIST\t\t(1200*0x10000)\n\n// when sounds should be max'd out\n#define S_CLOSE_DIST\t\t(200*0x10000)\n\n\n#define S_ATTENUATOR\t\t((S_CLIPPING_DIST-S_CLOSE_DIST)>>FRACBITS)\n\n#define NORM_PITCH     \t\t128\n#define NORM_PRIORITY\t\t64\n#define NORM_VOLUME    \t\tsnd_MaxVolume\n\n#define S_PITCH_PERTURB\t\t1\n#define NORM_SEP\t\t\t128\n#define S_STEREO_SWING\t\t(96*0x10000)\n\n// % attenuation from front to back\n#define S_IFRACVOL\t\t\t30\n\n#define NA\t\t\t\t0\n#define S_NUMCHANNELS\t\t2\n\n\n\n\n//\n// MusicInfo struct.\n//\ntypedef struct\n{\n    // up to 6-character name\n    char*\tname;\n\n    // lump number of music\n    int\t\tlumpnum;\n    \n    // music data\n    void*\tdata;\n\n    // music handle once registered\n    int handle;\n    \n} musicinfo_t;\n\n\n\n//\n// SoundFX struct.\n//\ntypedef struct sfxinfo_struct\tsfxinfo_t;\n\nstruct sfxinfo_struct\n{\n    // up to 6-character name\n    char*\tname;\n\n    // Sfx singularity (only one at a time)\n    int\t\tsingularity;\n\n    // Sfx priority\n    int\t\tpriority;\n\n    // referenced sound if a link\n    sfxinfo_t*\tlink;\n\n    // pitch if a link\n    int\t\tpitch;\n\n    // volume if a link\n    int\t\tvolume;\n\n    // sound data\n    void*\tdata;\n\n    // this is checked every second to see if sound\n    // can be thrown out (if 0, then decrement, if -1,\n    // then throw out, if > 0, then it's in use)\n    int\t\tusefulness;\n\n    // lump number of sfx\n    int\t\tlumpnum;\t\t\n};\n\n\n\ntypedef struct\n{\n    // sound information (if null, channel avail.)\n    sfxinfo_t*\tsfxinfo;\n\n    // origin of sound\n    void*\torigin;\n\n    // handle of the sound being played\n    int\t\thandle;\n    \n} channel_t;\n\n\n\nenum\n{\n    Music,\n    Sfx,\n    SfxLink\n};\n\nenum\n{\n    PC=1,\n    Adlib=2,\n    SB=4,\n    Midi=8\n}; // cards available\n\nenum\n{\n    sfxThrowOut=-1,\n    sfxNotUsed=0\n};\n\n\n//\n// Initialize the sound code at start of level\n//\nvoid S_Start(void);\n\n//\n// Start sound for thing at <origin>\n//  using <sound_id> from sounds.h\n//\nextern void\nS_StartSound\n( void*\t\torigin,\n  int\t\tsound_id );\n\n\n\n// Will start a sound at a given volume.\nextern void\nS_StartSoundAtVolume\n( void*\t\torigin,\n  int\t\tsound_id,\n  int\t\tvolume );\n\n\n// Stop sound for thing at <origin>\nextern void S_StopSound(void* origin);\n\n// Start music using <music_id> from sounds.h\nextern void S_StartMusic(int music_id);\n\n// Start music using <music_id> from sounds.h,\n//  and set whether looping\nextern void\nS_ChangeMusic\n( int\t\tmusic_id,\n  int\t\tlooping );\n\n\n// Stops the music\nextern void S_StopMusic(void);\n\nvoid S_PauseSound(void);\nvoid S_ResumeSound(void);\n\n\n//\n// Updates music & sounds\n//\nextern void S_UpdateSounds(void* listener);\n\nvoid S_SetMusicVolume(int volume);\nvoid S_SetSfxVolume(int volume);\n\n//\n// Initializes sound stuff, including volume\n//\nvoid\nS_Init\n( int \t,\n  int    );\n\n\n\n//\n// SOUND IO\n//\n#define FREQ_LOW\t\t0x40\n#define FREQ_NORM\t\t0x80\n#define FREQ_HIGH\t\t0xff\n\n\nvoid I_SetMusicVolume(int volume);\nvoid I_SetSfxVolume(int volume);\n\n//\n//  MUSIC I/O\n//\nvoid I_PauseSong(int handle);\nvoid I_ResumeSong(int handle);\n\n//\n// Called by anything that wishes to start music.\n//  plays a song, and when the song is done,\n//  starts playing it again in an endless loop.\n// Horrible thing to do, considering.\nvoid\nI_PlaySong\n( int\t\thandle,\n  int\t\tlooping );\n\n\n// stops a song over 3 seconds.\nvoid I_StopSong(int handle);\n\n// registers a song handle to song data\nint I_RegisterSong(void *data);\n\n// see above then think backwards\nvoid I_UnRegisterSong(int handle);\n\n// is the song playing?\nint I_QrySongPlaying(int handle);\n\n\n//\n//  SFX I/O\n//\nvoid I_SetChannels(int channels);\n\nint I_GetSfxLumpNum (sfxinfo_t*);\n\n\n// Starts a sound in a particular sound channel.\nint\nI_StartSound\n( int\t\tid,\n  void*\t\tdata,\n  int\t\tvol,\n  int\t\tsep,\n  int\t\tpitch,\n  int\t\tpriority );\n\n\n// Updates the volume, separation,\n//  and pitch of a sound channel.\nvoid\nI_UpdateSoundParams\n( int\t\thandle,\n  int\t\tvol,\n  int\t\tsep,\n  int\t\tpitch );\n\n\n// Stops a sound channel.\nvoid I_StopSound(int handle);\n\n// Called by S_*()'s to see if a channel is still playing.\n// Returns 0 if no longer playing, 1 if playing.\nint I_SoundIsPlaying(int handle);\n\n\n// the complete set of sound effects\nextern sfxinfo_t\tS_sfx[];\n\n// the complete set of music\nextern musicinfo_t\tS_music[];\n\n#endif\n"
  },
  {
    "path": "sndserv/wadread.c",
    "content": "// Emacs style mode select   -*- C++ -*- \n//-----------------------------------------------------------------------------\n//\n// $Id: wadread.c,v 1.3 1997/01/30 19:54:23 b1 Exp $\n//\n// Copyright (C) 1993-1996 by id Software, Inc.\n//\n// This source is available for distribution and/or modification\n// only under the terms of the DOOM Source Code License as\n// published by id Software. All rights reserved.\n//\n// The source is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License\n// for more details.\n//\n//\n// $Log: wadread.c,v $\n// Revision 1.3  1997/01/30 19:54:23  b1\n// Final reformatting run. All the remains (ST, W, WI, Z).\n//\n// Revision 1.2  1997/01/21 19:00:10  b1\n// First formatting run:\n//  using Emacs cc-mode.el indentation for C++ now.\n//\n// Revision 1.1  1997/01/19 17:22:51  b1\n// Initial check in DOOM sources as of Jan. 10th, 1997\n//\n//\n// DESCRIPTION:\n//\tWAD and Lump I/O, the second.\n//\tThis time for soundserver only.\n//\tWelcome to Department of Redundancy Department. Again :-).\n//\n//-----------------------------------------------------------------------------\n\n\nstatic const char rcsid[] = \"$Id: wadread.c,v 1.3 1997/01/30 19:54:23 b1 Exp $\";\n\n\n\n#include <malloc.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <unistd.h>\n\n#include \"soundsrv.h\"\n#include \"wadread.h\"\n\n\nint*\t\tsfxlengths;\n\ntypedef struct wadinfo_struct\n{\n    char\tidentification[4];\t\t                 \n    int\t\tnumlumps;\n    int\t\tinfotableofs;\n\n} wadinfo_t;\n\ntypedef struct filelump_struct\n{\n    int\t\tfilepos;\n    int\t\tsize;\n    char\tname[8];\n\n} filelump_t;\n\ntypedef struct lumpinfo_struct\n{\n    int\t\thandle;\n    int\t\tfilepos;\n    int\t\tsize;\n    char\tname[8];\n\n} lumpinfo_t;\n\n\n\nlumpinfo_t*\tlumpinfo;\t\t                                \nint\t\tnumlumps;\n\nvoid**\t\tlumpcache;\n\n\n#define strcmpi strcasecmp\n\n\n//\n// Something new.\n// This version of w_wad.c does handle endianess.\n//\n#ifndef __BIG_ENDIAN__\n\n#define LONG(x) (x)\n#define SHORT(x) (x)\n\n#else\n\n#define LONG(x) ((long)SwapLONG((unsigned long) (x)))\n#define SHORT(x) ((short)SwapSHORT((unsigned short) (x)))\n\nunsigned long SwapLONG(unsigned long x)\n{\n    return\n\t(x>>24)\n\t| ((x>>8) & 0xff00)\n\t| ((x<<8) & 0xff0000)\n\t| (x<<24);\n}\n\nunsigned short SwapSHORT(unsigned short x)\n{\n    return\n\t(x>>8) | (x<<8);\n}\n\n#endif\n\n\n\n// Way too many of those...\nstatic void derror(char* msg)\n{\n    fprintf(stderr, \"\\nwadread error: %s\\n\", msg);\n    exit(-1);\n}\n\n\nvoid strupr (char *s)\n{\n    while (*s)\n\t*s++ = toupper(*s);\n}\n\nint filelength (int handle)\n{\n    struct stat\tfileinfo;\n  \n    if (fstat (handle,&fileinfo) == -1)\n\tfprintf (stderr, \"Error fstating\\n\");\n\n    return fileinfo.st_size;\n}\n\n\n\nvoid openwad(char* wadname)\n{\n\n    int\t\twadfile;\n    int\t\ttableoffset;\n    int\t\ttablelength;\n    int\t\ttablefilelength;\n    int\t\ti;\n    wadinfo_t\theader;\n    filelump_t*\tfiletable;\n\n    // open and read the wadfile header\n    wadfile = open(wadname, O_RDONLY);\n\n    if (wadfile < 0)\n\tderror(\"Could not open wadfile\");\n\n    read(wadfile, &header, sizeof header);\n\n    if (strncmp(header.identification, \"IWAD\", 4))\n\tderror(\"wadfile has weirdo header\");\n\n    numlumps = LONG(header.numlumps);\n    tableoffset = LONG(header.infotableofs);\n    tablelength = numlumps * sizeof(lumpinfo_t);\n    tablefilelength = numlumps * sizeof(filelump_t);\n    lumpinfo = (lumpinfo_t *) malloc(tablelength);\n    filetable = (filelump_t *) ((char*)lumpinfo + tablelength - tablefilelength);\n\n    // get the lumpinfo table\n    lseek(wadfile, tableoffset, SEEK_SET);\n    read(wadfile, filetable, tablefilelength);\n\n    // process the table to make the endianness right and shift it down\n    for (i=0 ; i<numlumps ; i++)\n    {\n\tstrncpy(lumpinfo[i].name, filetable[i].name, 8);\n\tlumpinfo[i].handle = wadfile;\n\tlumpinfo[i].filepos = LONG(filetable[i].filepos);\n\tlumpinfo[i].size = LONG(filetable[i].size);\n\t// fprintf(stderr, \"lump [%.8s] exists\\n\", lumpinfo[i].name);\n    }\n\n}\n\nvoid*\nloadlump\n( char*\t\tlumpname,\n  int*\t\tsize )\n{\n\n    int\t\ti;\n    void*\tlump;\n\n    for (i=0 ; i<numlumps ; i++)\n    {\n\tif (!strncasecmp(lumpinfo[i].name, lumpname, 8))\n\t    break;\n    }\n\n    if (i == numlumps)\n    {\n\t// fprintf(stderr,\n\t//   \"Could not find lumpname [%s]\\n\", lumpname);\n\tlump = 0;\n    }\n    else\n    {\n\tlump = (void *) malloc(lumpinfo[i].size);\n\tlseek(lumpinfo[i].handle, lumpinfo[i].filepos, SEEK_SET);\n\tread(lumpinfo[i].handle, lump, lumpinfo[i].size);\n\t*size = lumpinfo[i].size;\n    }\n\n    return lump;\n\n}\n\nvoid*\ngetsfx\n( char*\t\tsfxname,\n  int*\t\tlen )\n{\n\n    unsigned char*\tsfx;\n    unsigned char*\tpaddedsfx;\n    int\t\t\ti;\n    int\t\t\tsize;\n    int\t\t\tpaddedsize;\n    char\t\tname[20];\n\n    sprintf(name, \"ds%s\", sfxname);\n\n    sfx = (unsigned char *) loadlump(name, &size);\n\n    // pad the sound effect out to the mixing buffer size\n    paddedsize = ((size-8 + (SAMPLECOUNT-1)) / SAMPLECOUNT) * SAMPLECOUNT;\n    paddedsfx = (unsigned char *) realloc(sfx, paddedsize+8);\n    for (i=size ; i<paddedsize+8 ; i++)\n\tpaddedsfx[i] = 128;\n\n    *len = paddedsize;\n    return (void *) (paddedsfx + 8);\n\n}\n"
  },
  {
    "path": "sndserv/wadread.h",
    "content": "// Emacs style mode select   -*- C++ -*- \n//-----------------------------------------------------------------------------\n//\n// $Id: wadread.h,v 1.3 1997/01/30 19:54:23 b1 Exp $\n//\n// Copyright (C) 1993-1996 by id Software, Inc.\n//\n// This source is available for distribution and/or modification\n// only under the terms of the DOOM Source Code License as\n// published by id Software. All rights reserved.\n//\n// The source is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License\n// for more details.\n//\n//\n// $Log: wadread.h,v $\n// Revision 1.3  1997/01/30 19:54:23  b1\n// Final reformatting run. All the remains (ST, W, WI, Z).\n//\n// Revision 1.2  1997/01/21 19:00:10  b1\n// First formatting run:\n//  using Emacs cc-mode.el indentation for C++ now.\n//\n// Revision 1.1  1997/01/19 17:22:52  b1\n// Initial check in DOOM sources as of Jan. 10th, 1997\n//\n//\n// DESCRIPTION:\n//\tWAD and Lump I/O, the second.\n//\tThis time for soundserver only.\n//\tWelcome to Department of Redundancy Department.\n//\t (Yeah, I said that elsewhere already).\n//\tNote: makes up for a nice w_wad.h.\n//\n//-----------------------------------------------------------------------------\n\n#ifndef __WADREAD_H__\n#define __WADREAD_H__\n\n//\n//  Opens the wadfile specified.\n// Must be called before any calls to  loadlump() or getsfx().\n//\n\nvoid openwad(char* wadname);\n\n//\n//  Gets a sound effect from the wad file.  The pointer points to the\n//  start of the data.  Returns a 0 if the sfx was not\n//  found.  Sfx names should be no longer than 6 characters.  All data is\n//  rounded up in size to the nearest MIXBUFFERSIZE and is padded out with\n//  0x80's.  Returns the data length in len.\n//\n\nvoid*\ngetsfx\n( char*\t\tsfxname,\n  int*\t\tlen );\n\n#endif\n"
  }
]