Release Notes for JFShadowWarrior

Release date: 31 May 2005
Official website

New Features and Improvements

Known Issues

Save Help

I have written a new compiler and system-independent framework for saving and restoring the pointer information contained in Shadow Warrior's internal objects, and this has required a large amount of manual processing of the source code to pick out the data and functions in the game that need to be saved to disk. Because of this, I have quite likely missed some functions or data that are on a rare occasion required to properly save a game. If this happens, you will see a message after the save operation that reads:

There was a problem saving. See "Save Help" section of release notes.

When this message is displayed, the game creates a file named savegame symbols missing.txt in the game directory containing information I require to be able to add the missing symbols into the game code for future saves. If you see the message, please send the file to me at so I may update the game.

Customisation File Format

Historically, the modifications and total conversions for Shadow Warrior have been forced to resort to hacking the game executable with a hex editor to replace the names of maps and the episode title information. For this port I have introduced a customisation script file that is loaded on startup which achieves the same purpose as the EXE hacks once did. Customisation of weapon ammunition counts and other similar things will be added in the future as the need arises.

The customisation file should be named SWCUSTOM.TXT for the game to find it. The contents of the file may consist of these directives. Comments can be used by prefixing the text with a double forward-slash (C++ style), //, or surrounding the text with /* (comment here) */ (C style).

map number { ... }
level number { ... }
Selects the map to change the information of. number is a positive value in the range of 1 to 29. Levels 1 to 4 are the shareware maps. level is a synonym for map. The brace-enclosed block may contain these directives:
title title
name title
description title
Sets the new title of the level to title. name and description are synonyms for title.
filename filename
file filename
fn filename
levelname filename
Sets the new map file name to filename. file, fn, and levelname are synonyms for filename.
song filename
music filename
songname filename
Sets the new MIDI file name to filename. music and songname are synonyms for song.
cdatrack tracknum
cdtrack tracknum
Sets the Red-Book CD track number to tracknum. cdtrack is a synonym for cdatrack.
besttime seconds
partime seconds
Sets the "best" and "par" times of the level respectively. seconds is the new par time given as a count of seconds.
episode number { ... }
Selects the episode to change the information of. number is a positive value in the range of 1 to 2. The brace-enclosed block may contain these directives:
title title
name title
description title
Sets the new title of the episode to title. name and description are synonyms for title.
subtitle subtitle
Sets the new subtitle of the episode to subtitle.

Here is a fictitious example:

level 1 {
    title    "My Neat Map"
    filename "" 
    song     "coolsong.mid"
    cdatrack 4
    besttime 1800    // an hour
    partime  3600    // half an hour

episode 1 {
    title    "My Neat Episode"
    subtitle "One level of lameness"

DEF-file Language

These are the legal instructions that may be used in the SW.DEF file. Comments can be used by prefixing the text with a double forward-slash (C++ style), //, or surrounding the text with /* (comment here) */ (C style).

include filename
#include filename
Processes the script commands in filename at the point of the "include" call.
define label integer-value
#define label integer-value
Declares label to represent the numeric value integer-value. integer-value can be a label, in which case the value of the label given is used.
NOTE: You may find it convenient to include DEFS.CON or NAMES.H to predefine many of the tile names in the art file.
model filename { ... }
Defines an MD2/3-format model file to replace certain sprites in the game when running in OpenGL Polymost mode. The brace-enclosed block may contain these directives:
scale scale
scale is a (possibly fractional) value specifying a scaling factor for the model when it is rendered, eg. 1.5 for one-and-a-half times as big.
shade shade-offset
shade-offset is an integer value specifying how much to bias the sprite's shade value by. A negative value for this makes the model brighter. Conversely, a positive value makes it darker.
zadd offset
offset is a (possibly fractional) value specifying a height offset for the model. Quake models are aligned in the center while Build models are aligned at the floor. Using this command will allow Build to use Quake models without modification to the MD2/3 file itself.
skin { ... }
Defines a skin to be used on the model for all frames declared after this command. The brace-enclosed block may contain these directives:
pal palnum
Specifies which palette this skin maps to.
file filename
IMPORTANT: If your model exists in a subdirectory (ie. the model filename includes a path to the .md2/3) you will need to give the same path to filename if the skin is in the same directory.
anim { ... }
Defines an animation from a group of frames in the model. The brace-enclosed block may contain these directives:
frame0 framename
frame1 framename
Specifies the names of the start (frame0) and end (frame1) frames of the animation.
fps fps
Specifies the framerate at which the animation should play. This value may be fractional.
flags flags
Specifies any special properties the animation should have, the values of which should be added together to combine multiple options. Valid options are: 0 = none (looping animation), 1 = one-shot (plays beginning to end once and stops on the last frame).
frame { ... }
Defines a range of ART-file tiles to correspond with the given frame of the model. The brace-enclosed block may contain these directives:
frame framename
name framename
If framename is identical to the starting frame of an animation, the engine will play that animation, otherwise the replacement will be static. You can choose to use the 'frame' or 'name' versions of this directive as both are identical.
tile tilenum
tile0 tilenum
tile1 tilenum
Use the tile directive to specify an ART-file tile which this model should replace. Use the tile0 and tile1 directives together to specify a range of ART-file tiles. If you use tile0, you must also have a tile1. You may not use the same directive twice to specify multiple ranges.
hud { ... }
Defines a range of ART-file tiles to use with a heads-up-display. The brace-enclosed block may contain these directives:
tile tilenum
tile0 tilenum
tile1 tilenum
Tile0 and tile1 together specify a range of ART-file tiles which this model frame should replace when rendered as part of the HUD. You can specify individual tiles using a single tile command.
xadd offset
yadd offset
zadd offset
angadd offset
Use these offsets to fine-tune the location of the model placement. Xadd, yadd, and zadd are position offsets relative to the viewer's orienation. You can use floating point values with them. Angadd is a Build angle offset. (512 = 90 degrees, 1024 = 180 degrees, etc...).
Some weapons use multiple ART tiles for constructing the gun or animation. Use this option to hide parts that you don't need in your replacement.
By default, the HUD model offset is affected by the player bobbing offset when Lo Wang is walking. Use this option to disable that.
Use this option to apply the settings inside the hud block only if the object is normally rendered x-flipped (mirror image). Some weapons, such as the devastator, are rendered in 2 pieces, the left devastator is actually a mirror image of the right.
Use this to render a HUD model without the use of the depth buffer. Normally, you should avoid this. The one exception where this is useful is for the spinning nuke menu pointer because it should always be in front - and it just happens to be convex ... which is the one case that is safe with the depth buffer disabled ... a rather fortunate coincidence :)
Here are some HUD examples:

   // A pistol model that was posted a long time ago...
model "models/m9.md2"
   scale 50 shade 4
   anim { frame0 "idle1" frame1 "idle12" fps 5 flags 0 }
   frame { frame "idle1" tile 2524 }
   anim { frame0 "frame13" frame1 "frame45" fps 24 flags 0 }
   frame { frame "frame13" tile0 2525 tile1 2526 }
   anim { frame0 "reload1" frame1 "reload34" fps 24 flags 0 }
   frame { frame "reload1" tile0 2528 tile1 2529 }

   hud { tile0 2524 tile1 2529 xadd -1.2 yadd -1.0 zadd -1.0 angadd 1536 }

   frame { frame "reload1" tile0 2530 tile1 2532 } //consume tiles with dummy frames
   hud { tile0 2530 tile1 2532 hide } //Make it not draw 2D hands during reload

   // A devastator weapon model that was posted a long time ago...
model "models/hud_devastator.md2"
   scale 12
   skin { pal 0 file "models/hud_devastator.pcx" }
   frame { frame "Frame 1" tile0 2510 tile1 2511 }
   hud { tile0 2510 tile1 2511 xadd -8.5 yadd +1.83 zadd +10.0 angadd 1024 flipped } //left devastator
   hud { tile0 2510 tile1 2511 xadd +8.5 yadd +5.90 zadd +10.0 angadd 1024 } //right devastator

   // fearpi's activated tripbomb (wall mine)
model "models/a_tripbomb.md2"
   scale 8
   anim { frame0 "frame1" frame1 "frame1" fps 1 flags 0 }
   frame { frame "frame1" tile 2566 }
   hud { tile 2566 yadd +0.35 zadd 1.2 angadd 1024 }

   // spinning icon (by Killd a ton)
model "models/spinnuke.md2"
   scale 0.25 shade 0

      //when rendered as respawn timer:
   skin { pal 0 file "models/spinnuke.png" }
   anim { frame0 "spin00" frame1 "spin19" fps 17 flags 0 }
   frame { frame "spin00" tile0 2813 tile1 2819 }
   frame { frame "spin00" tile0 3190 tile1 3196 }
   skin { pal 0 file "models/spinnukeyellow.png" }
   frame { frame "spin00" tile0 3200 tile1 3206 }
   skin { pal 0 file "models/spinnukegreen.png" }
   frame { frame "spin00" tile0 3210 tile1 3216 }

      //when rendered as menu icon:
   hud { tile0 2813 tile1 2819 xadd 0.01 yadd -.02 zadd 1.05 nodepth }
voxel filename { ... }
Defines a voxel to replace sprites in the game when running in 8-bit classic rendering mode. filename is the name of the .KVX file containing the voxel. The brace-enclosed block may contain these directives:
tile tilenum
Maps the single tile tilenum to be rendered as a voxel.
tile0 tilenum
tile1 tilenum
These two directives together define a range of tiles to be rendered as voxels. The 'tile0' directive should appear before the 'tile1' directive to define a correct range.
scale scale
scale is a (possibly fractional) value specifying a scaling factor for the voxel when it is rendered, eg. 1.5 for one-and-a-half times as big.
skybox { ... }
Defines a skybox that overrides a parallaxing floor or ceiling in OpenGL Polymost rendering mode. The brace-enclosed block may contain these directives:
tile tilenum
Specifies the ART file tile to override.
pal palnum
Specifies the palette number the skybox should happen for.
*facename* filename
Defines a single face of the skybox where facename may be any of these keywords appropriate for the face in question:
FrontRight BackLeft TopBottom
frontrightbacklefttop bottom
forwardltceiling floor
NOTE: All six faces are required to be specified.
tint { ... }
Defines a Hightile texture tint to simulate palette effects normally used on ART-file tiles. The brace-enclosed block may contain these directives:
pal palnum
The palette number the tint applies to.
red value
green value
blue value
r value
g value
b value
Specifies a colour component value, in the range of 0 to 255. Unspecified components are assumed to be 255 and any out of range values are clamped to the maximum or minimum as appropriate.
flags flags
Specifies any special processing effects to use for the tint. The value of flags may be the sum of any of these values:
1convert to greyscale
2invert colours

A note about skyboxes

Ken has some utilities on his website that can assist you in converting your favourite skybox format to Polymost format. ( If your skybox is in Mercator projection, then you'll need to run MERC2KUB.EXE first. Once you have your 6 face textures, you can use KUBE.EXE to convert it to Polymost format using the /split option. The face textures can be in almost any configuration - KUBE has fancy autodetection code. For example, typing "kube /split" will generate the 6 face textures in the order and orientation that Polymost expects. The output files will be called SPLITCAP0.PNG, SPLITCAP1.PNG, ..., SPLITCAP5.PNG. After conversion, I suggest you rename the files and compress them using PNGOUT or similar utility.

Map Hack scripts

"Map Hack" scripts are files that override certain aspects of a map file when it is rendered in OpenGL Polymost mode. Currently they allow for angle adjustment on sprites, and the ability to prevent particular sprites from being drawn as a model. These are useful for making small corrections to ornamental sprites in a way that doesn't require modifying the original map.

The game will automatically load a map hack script whenever a map is loaded. The script should have the same base name as the original .MAP file, but with an .MHK extension. The map hack language is described below. It uses the same parser as DEF files, so you can use comments in the same way.

sprite number
Begins a sprite definition. number is the sprite number to affect. You can find this in the Build editor by highlighting the sprite in 2D mode and pressing Control+Tab. The next group of commands describe the changes to make to the sprite.
Prevents the sprite from being drawn as a model. It gets drawn as a regular sprite instead. notmd2 and notmd3 are synonyms for notmd.
Prevents model animation from playing if the sprite is being drawn as a model. nomd2anim and nomd3anim are synonyms for nomdanim.
angleoff angle
angoff angle
Adds angle to the angle of the sprite just before it is rendered. This is good for fixing up things like toilet sprites that are facing the wrong way. angoff is a synonym for angleoff.

Here is an example map hack script:

// Map hack file for JFDuke3D
// Level: E1L2.MAP (Original Atomic Edition version)
// Prepared by

// Invisible switch behind hand dryer in toilet of porn shop
sprite 191 notmd2

// Invisible switches on telephones near billiards room in club
sprite 254 notmd2
sprite 517 notmd2

// Toilet in restroom in club
sprite 478 angoff -512


This release features the "Hightile" texturing improvements to Polymost. Hightile allows Polymost to use true-colour textures instead of the artwork in the game's usual .ART file.

Replacement textures can be saved as JPEG, PNG (alpha channel supported), TGA, BMP, CEL, GIF, and PCX formats. Hightile uses Ken Silverman's picture library to provide rapid picture file loading.

Hightile textures are defined in the SW.DEF file. See the DEF-file language reference for information on how to specify Hightile textures.

Limitations to Hightile in this release

Extra GRP and ZIP file support

JFShadowWarrior (and JFBuild games in general) can load extra game resources from a GRP or ZIP file.

Use the "/g" command-line switch to specify the ZIP or GRP to load. eg. SW.EXE /gMYFILE.ZIP


Polymost is a full 3D implementation of the Build engine renderer, with hardware acceleration capability, and perspective in six degrees of freedom. In Ken's own words (copied from POLYMOST.C in my Build engine source distribution):

"POLYMOST" code written by Ken Silverman
Ken Silverman's official web site:

When 3D Realms released the Duke Nukem 3D source code, I thought somebody would do a OpenGL or
Direct3D port. Well, after a few months passed, I saw no sign of somebody working on a true
hardware-accelerated port of Build, just people saying it wasn't possible. Eventually, I realized
the only way this was going to happen was for me to do it myself. First, I needed to port Build to
Windows. I could have done it myself, but instead I thought I'd ask my Australian buddy, Jonathon
Fowler, if he would upgrade his Windows port to my favorite compiler (MSVC) - which he did. Once
that was done, I was ready to start the "POLYMOST" project.

This source file is basically a complete rewrite of the entire rendering part of the Build engine.
There are small pieces in ENGINE.C to activate this code, and other minor hacks in other source
files, but most of it is in here. If you're looking for polymost-related code in the other source
files, you should find most of them by searching for either "polymost" or "rendmode". Speaking of
rendmode, there are now 4 rendering modes in Build:

    rendmode 0: The original code I wrote from 1993-1997
    rendmode 1: Solid-color rendering: my debug code before I did texture mapping
    rendmode 2: Software rendering before I started the OpenGL code (Note: this is just a quick
                hack to make testing easier - it's not optimized to my usual standards!)
    rendmode 3: The OpenGL code

The original Build engine did hidden surface removal by using a vertical span buffer on the tops
and bottoms of walls. This worked nice back in the day, but it it's not suitable for a polygon
engine. So I decided to write a brand new hidden surface removal algorithm - using the same idea
as the original Build - but one that worked with vectors instead of already rasterized data.

Polymost is the default renderer choice for any video mode with a colour depth greater than 256 colours.

NOTE: If your computer does not have an OpenGL graphics card, Polymost in OpenGL mode will most likely use the default Windows OpenGL rasterising facility which does all rendering in software. This may be extremely slow. If your Windows installation doesn't have any form of OpenGL rendering ability, Polymost will probably crash.

NOTE 2: OpenGL Polymost has been tested on an nVidia Riva TNT 16MB, an nVidia GeForce2 GTS 32MB, an nVidia GeForce4 Ti4600 128MB, an nVidia GeForce 6800GT 256MB, an ATi Radeon Mobility 9000 64MB, and a 3D-Labs Oxygen GVX420 128MB (minor texturing issues).

Console Commands

This is a list of console commands and variables and their purpose:

Displays the compilation information for the game when it was built.
echo <text...>
Displays to the console what is passed as parameters to the command.
fileinfo <filename>
Displays some information about a given file, eg. size, CRC-32 checksum.
Displays some information about the OpenGL driver.
gltextureanisotropy <level>
Sets the OpenGL anisotropic filtering level.
gltexturemode <mode-number>
Sets the OpenGL texturing mode. Valid values are:
0GL_NEAREST (looks rather like the original software renderer)
help <name>
Displays a help message for a particular console variable or command.
Displays the names of all commands and variables available in the console.
setrendermode <mode>
Sets the current Polymost render mode.
glusetexcompr <0 or 1>
Enables or disables the use of OpenGL texture compression for hightile textures. You need to use 'restartvid' to apply any changes to this value.
novoxmips <0 or 1>
Disables or enables the use of voxel mipmaps to improve voxel visual quality.
osdrows <num>
Sets the number of visible lines of the console when it is open.
screencaptureformat <0 or 1>
0 = Targa, 1 = PCX
usemodels <0 or 1>
Disables or enables the use of 3D models in GL Polymost mode if any are defined.
usevoxels <0 or 1>
Disables or enables the use of voxels in the classic renderer if any are defined.
usehightile <0 or 1>
Disables or enables the use of Hightile textures in GL Polymost mode if any are defined.
usegoodalpha <0 or 1>
If 1, a lower alpha cutoff value is used when rendering textures with transparency, which gives better looking transparent textures at the expense of some sprites behind the transparent item potentially being invisible in certain circumstances. 0 is a more compatible value (and is the default) for this option but slight visual degradation will result.
glredbluemode <0 or 1>
Enables or disables the red-blue stereovision mode in OpenGL. This mode is experimental at this time. We know the flicker is nasty and the menu background will mix into the screen. To avoid the menu bug, run the game in fullscreen mode and type "glredbluemode 1" AFTER beginning the game. Sorry, there is no way to change parallax or separation. Do not contact us about bugs with this mode... if you do, we'll think twice about documenting hidden features in future releases. :P
Have fun!
Jonathon Fowler (