Rise of the Triads Source Code Released

Rise of the Triads Source Code Released

For those fo you that are more technically minded, 3D Realms announced that they have released the source code for Rise of the Triads. Like DOOM, Rise of the Triad is a high quality, fast scrolling first-person perspective 3D action game. Graphics are on par with DOOM. It has destructive enemies and lots of them, an arsenal of weapons from simple pistols to missile launchers, life-preserving armor, traps and ambushes galore, and the ability to play by modem or network. In all, there's a great degree of similarity between the two games.
The source is being released under the GPL license, and 3D Realms will retain the copyright on the game i.e. they will still sell Rise of the Triad, but they yielded to public demand and here it is.

The release of the Rise of the Triad Source code is a tribute to, Rise of the Triad programmer William Scarboro, who died tragically back in early August of 2002.
In order to download the file, which contains some thoughts by Tom Hall (the game's producer), and Scott Miller (Apogee/3DR CEO/Founder), just follow the download tab above.

Text file description:

=============================================================
| Apogee Expanded MIDI (EMIDI) API v1.0                     |
=============================================================
Specifications created by Lee Jackson and Jim Dos
Support coded by Jim Dos
FOR INTERNAL USE ONLY.  ACCESS OR USE WITHOUT EXPRESS WRITTEN
PERMISSION FROM APOGEE SOFTWARE, LTD., 3D REALMS ENTERTAINMENT, OR
ACTION ENTERTAINMENT, INC., IS STRICTLY PROHIBITED.
Copyright (c) 1995 Apogee Software Ltd.  All Rights Reserved.
=============================================================
| Contents                                                  |
=============================================================
    I.      Instrument Definitions
    II.     Controller Definitions
    III.    InitBeat Format
=============================================================
| I.  Instrument Definitions                                |
=============================================================
The following instruments are currently defined as valid for data
entry events:
        0 -     General MIDI
        1 -     Roland Sound Canvas (GM and GS)
        2 -     Sound Blaster AWE32
        3 -     Wave Blaster and Compatibles (SCD-10, etc.)
        4 -     Sound Blaster and Compatibles (OPL-2 and OPL-3)
        5 -     Media Vision Pro Audio series
        6 -     Logitech Sound Man 16
        7 -     Adlib and Compatibles
        8 -     Ensoniq Soundscape
        9 -     Gravis Ultrasound, Ultrasound Max, Ultrasound ACE
        127 -   All (see Controller Definitions)
=============================================================
| II.  Controller Definitions                                |
=============================================================
The following controllers are currently defined as valid:
110 -   Track Designation
        Required:               Yes
        Multiples allowed:      Yes
        Format:                 110 nn
                                (where nn is a defined instrument)
Controller 110 determines which instruments will receive data for
this track.  Multiple instances may be used to designate multiple
instruments.  If the track is designated for all instruments,
Instrument 127 may be used.  This instrument may also be used in
conjunction with Controller 111 to exclude a single instrument or
group of instruments.
111 -   Track Exclusion
        Required:               No
        Multiples allowed:      Yes
        Format:                 111 nn
                                (where nn is a defined instrument -
                                        instrument 127 is invalid)
Controller 111 excludes an instrument from receiving the data for
this track.  Multiple instances may be used to exclude multiple
instruments.  Instrument 127 is not valid for this controller.
112 -   Program Change
        Required:               No
        Multiples allowed:      Yes
        Format:                 112 nn
                                (where nn is a GM program change)
Controller 112 is the same as the standard MIDI program change
event.  It may be inserted at any point a program change is
required.  If it does not exist, standard MIDI program change events
will be recognized for this track.  If it does exist, standard MIDI
program change events will not be recognized for this track, and all
program changes for the track must be entered using Controller 112.
113 -   Volume
        Required:               No
        Multiples allowed:      Yes
        Format:                 113 nn
                                (where nn is between 0 and 127)
Controller 113 allows different volumes to be inserted in the same
manner as Controller 7.  It should be used only if
designation/exclusion groups exist on the same MIDI channel.  If
Controller 113 does not exist at the beginning of the track,
Controller 7 events will be recognized.  If Controller 113 does
exist at the beginning of the track, Controller 7 events will be
ignored.
116 -   Loop Begin
        Required:               Yes
        Multiples allowed:      Yes (see below)
        Format:                 116 nn
                                (see below for definition)
Controller 116 indicates the beginning of a sequence to be looped.
Values for this controller are as follows:
        0 - infinite loop
        1 - loop once
        2 - loop twice
        x - loop x times
A song should have a Master Start point and a Master End point.  The
start point should occur after the InitBeat (see section III) and
after any introduction you wish to have played only once.  Any
controller information (patch changes, pitch bend info, etc.)
required for the loop should be entered and/or repeated after the
loop's Master Start event (see also Section III, "InitBeat", below).
Likewise, any "reset" events required should be entered either just
after the loop's Master Start event or just before the loop's Master
End event.
Multiple non-infinite loop start/end pairs (see Controller 117
below) may occur at any point inside the Master Loop.  Only one
infinite loop may be defined per song.  Nested loops are not yet
supported.
IMPORTANT:  Any event to be included within the loop must fall after
            the Loop Begin point and end before the Loop End point.
            If you are using Cakewalk Pro for Windows, you can
            verify this by looking at the Event List view.
            SIMULTANEOUS TIMES FOR LOOP POINTS AND EVENT DATA DO NOT
            GUARANTEE THAT AN EVENT IS INSIDE THE LOOP.  If in
            doubt, set the event one tick after the Loop Begin or
            have it end one tick before the Loop End.
117 -   Loop End
        Required:               Yes
        Multiples allowed:      Yes (see below)
        Format:                 117 127
Controller 117 indicates the end of a looped segment of a song, or
in the case of a Master Loop, the end of the entire song.  This
controller signals that the Apogee Sound System should immediately
loop back to the nearest non-resolved loop begin event (i.e., the
nearest Controller 116 event that does not have a matching
Controller 117 event).
Loop End events should not occur at the exact time of a Loop Begin
event.
Note:  Loop Begin/End pairs only affect the track they are placed
       on.  If an entire song is to be looped through a segment, all
       tracks must contain Loop Begin events and Loop End events
       which occur on the same tick across all tracks.  Be careful
       how you use non-synchronous single track loops - the composer
       is responsible for making sure that everything stays in sync.
See Controller 116 above for important information regarding
placements of events to be included within loops.
=============================================================
| III.  InitBeat Format                                     |
=============================================================
Each song should begin with an InitBeat.  In terms of standard
musical notation, an example of this would be a single 1/4 measure
where the rest of the song is in 4/4, or a single 1/8 measure when
the rest of the song is in 6/8.  In MIDI terms, this is equivalent
to one full cycle of the current timebase (e.g., for timebase 120, a
single beat, 120 ticks long).  Each track used should have its own
InitBeat.  This beat should not contain any note on/off data, sysex
dump data, Loop Begin/End events (Controllers 116 and/or 117), or
other non-Controller events.  It may, however, contain MetaEvents
such as text, copyrights, markers, program changes (normal or
Controller 112), and the like.
The InitBeat should contain any needed Controller 110, 111, 112, or
113 events needed to set up each track for the song.  It may also
contain other Controllers (pitch wheel, modulation, RPN/NRPN data,
etc.) as desired.  Keep in mind that you will need to repeat these
controller values at some point within the Master Loop if you change
them at any point in the song (and you rely on the original values
when the song goes back to the Master Start point).
If no special handling is required for a track (i.e., the track is
to be played by all instruments and no Controller resets/setups are
desired), the InitBeat for that track may be left blank.  It is
advisable, however, to at least insert a single Controller 110 event
with value 127, indicating that the track is to be played by all
instruments.  This is more a matter of personal preference, but it
can come in handy for setting up templates with Cakewalk Pro for
Windows and other programs that support default song templates.
If none of the tracks in a song require any special handling, all
InitBeats may be left blank.  Do not delete the InitBeat measure -
just leave it blank.
Controller events may be spread throughout the InitBeat as desired.
You may place them all on a single tick, or on separate ticks.  Some
instruments will behave unpredictably if all events for a track are
placed on the same tick (e.g., the 5-6 events needed to adjust pitch
bend parameters, especially if controllers 100 and 101 are set with
value 0 and then reset with value 127 as recommended by Roland), so
composer discretion is advised.  At this time, simultaneous events
across multiple tracks are not known to cause a problem.
The InitBeat may be inserted in any way desired.  If you are using
Cakewalk Pro for Windows, it is simplest to make the first measure a
1/4 measure (one beat @ 4 per measure) and then switch the meter to
whatever you want to use for the remainder of the song right at
measure 2.  Meter changes within the song are not affected by this,
of course.
Apogee Sound System MIDI notes:
-------------------------------
FM Midi :
On OPL2 cards 9 voices are available.  OPL3 cards have 18 voices.  Melodic
mode is used for music.  For percussion (channel 10), each key is assigned
a timbre to play at a pitch specified by the patch.  This method was chosen
based on the advice of Bobby Prince, Rob Wallace, George Sanger, and Lee
Jackson, all of whom felt the 11 voice mode was too restricting for
musicians.
Channels 1-16 are now supported.
Controllers supported
6   data entry msb     |
38  data entry lsb     | - Only on Set Pitch Bend Range (RPN 0, 0)
96  data increment     |
97  data decrement     |
7   channel volume
10  pan (balance)
121 reset all controllers
123 all notes off
AWE32 :
All controllers supported by API.
Gravis Ultrasound:
1   modulation wheel
6   data entry msb     |
38  data entry lsb     | - Only on Set Pitch Bend Range (RPN 0, 0)
96  data increment     |
97  data decrement     |
7   channel volume
39  volume lsb ignored
10  pan (balance)
11  channel expression (volume)
43  expression lsb ignored
64  sustain (damper pedal)
100 set registered parameter number
101 set registered parameter number
121 reset all controllers
120 all sounds off     |
123 all notes off      |
124 omni off           | - Perform all notes off
125 omni on            |
126 mono               |
127 mono               |
General MIDI (all other cards):
All controllers sent.  Support depends on the card.
                             Apogee Sound System
                                Version 1.09
                                 by Jim Dos
                                Revision notes
When your game is in beta, you must fax me your beta reports with sound
problems since I will not see them otherwise.
Please be sure to include my name in the credits for your program.  I'm
not asking for a design or game programming credit, just credit for the
sound system.  This includes the credits screen, the manual, and the
text files accompanying the game (if the credits for the game are listed
in them).  Thank you.
NOTE: make sure that you unzipped the zip file using the "-d"
parameter.  This will create some directories that the demo needs.
                           Known problems:
- GUS may crash on systems with a VMM since the GUS code does not lock
  any of its allocated memory.
- GUS only supports IRQs below 8.  At higher IRQs, the dos extender seems
  to be too slow.
- PAS mixer does not have a linear sounding volume change.
                             To-do list:
- Add support for playback of stereo sounds.  If anyone needs this
  right away, let me know.
- Add permanant debugging code to various modules.
- If an error occurs loading a patch file, GUS code should return
  which file it is.
- Finish this doc file.  Music functions have to be documented using the
  new format.
- Change current limit of one upper IRQ.
- Get upper IRQ's working for GUS code.
- Lock memory in GUS code.
- Test code on OS/2 and Windows
- Lookup table for PAS mixer for more linear volume change.
- MOD music playback.
                          Revision History:
5/14/96
- Version 1.12
- Fixed a reverb bug where 8-bit fast reverb would round down to -infinity
  instead of 0.  This caused a bit of noise since the mix buffer would
  only reduce down to -1 and 0.
5/10/96
- Version 1.11
- added function FX_EndLooping to break a looping sound out of its loop.
1/16/96
- Modified Maketmb to chop off bits not relevant to OPL2 cards.
1/8/96
- Version 1.09
- ***** IMPORTANT *****
  Any code that is called by the sound system should be compiled with
  the -zu option.  This tells the compiler to assume that SS is not
  equal to DS.  This is required for any code that's called from the
  sound system while it's in an interrupt (I switch to a custom stack
  upon entering the interrupt).  Any function that is used with
  TS_ScheduleTask or FX_SetCallBack should be compiled this way (you may
  want to put them in a separate module.  I recommend doing this for
  USRHOOKS functions as well, in case you free a task from within a timer
  function (MUSIC_FadeVolume does this, so if you use it, USRHOOKS must
  be compiled with -zu).
- The sound system now uses the ULTRAMID.INI in the current directory if
  it exists, otherwise it uses the one pointed to by the ULTRADIR environment
  variable.
1/4/96
- Fixed a bug with fast 8-bit reverb.  Also made a few optimizations to
  it.
12/18/95
- Version 1.08
- I'm a dumbass.  I left a "strcat( InstrumentDirectory, name );"
  after locating the ULTRAMID.INI file for the Gravis Ultrasound.  So
  if you  haven't been able to initialize music in a while, you know
  who's to blame. :)
12/12/95
- New utility called PS.  Allows you to test a WAV, VOC, or raw sound
  file from the command line using the sound system.  Check in the
  PS directory for the source code.
12/8/95
- Version 1.07
- Added adjustable delay on the reverb.  The following functions allow
  you to manipulate the delay setting.
  int   FX_GetMaxReverbDelay( void );
  int   FX_GetReverbDelay( void );
  void  FX_SetReverbDelay( int delay );
  The delay is measured in the number of samples to delay before the
  sound is regenerated.  To calculate the delay in seconds, just do this:
  seconds = delay / mixingrate; (using floats, obviously).  The minimum
  delay is 256 samples.  The maximum depends on whether you're doing
  16 bit or 8 bit, and stereo or mono mixing.  Use FXGetMaxReverbDelay
  to get the maximum (after the FX_Init has been called).  In 8-bit mono,
  the delay is about 1/2 of a second.  The maximum delay is constrained
  by the size of the buffer I allocate to mix the sound in.  If anyone
  wants longer delays, let me know and I can increase the buffer size
  for you (I'd probably make it adjustable by you).
12/6/95
- 18 voice FM music on OPL3 cards.
- FM music is no longer limited to MIDI channels 1 through 10.  For
  backward compatability sake, the function MUSIC_SetMaxFMMidiChannel
  can be used to limit it, but why would anyone want to do that?
11/13/95
- Fixed a bug with Adlib music where the default pitch bend range was
  set too low.  Thanks Peter!
- 1.06a library upload.
11/7/95
- 1.06 update.  Time sure flies...
8/21/95
- 1.05 update.
- Full EMIDI support.
- Context sensitive music through EMIDI.
- Added the ability to fastforward to a specific beat/measure or time
  in a song.
- GUSMIDI.INI file no longer required (this may return, we're still debating).
- Probably a few other things that I can't think of right now.
7/24/95
- Changed GUS code to use software mixing instead of hardware mixing.
  GUS purists will hate me (if they found out), but it makes a lot more
  sense this way since I only have to maintain one playback method, plus
  it allows GUS owners to hear reverb and gives better control over panning.
  There may be other benefits, but I can't think of them right now.
5/31/95
- Playback of 16-bit digitized sound now supported.  Source data must
  be signed data.  Still need to do GUS version, but I'm uploading it for
  Jason Blochowiak.
- Reverb code rewritten in assembly.  Still needs to be fine-tuned.
5/25/95
- Using "option eliminate" with the sound library should no longer crash
  the linker.
5/8/95
- Added some new functions:
     void FX_SetReverseStereo( int setting );
     int  FX_GetReverseStereo( void );
     void FX_SetReverb( int reverb );
     void FX_SetFastReverb( int reverb );
  FX_SetReverseStereo and FX_GetReverseStereo allow you to swap the left
  and right channel volumes for people whose speakers are reversed.  Zero
  is no swap, non-zero swaps channels.
  FX_SetReverb and FX_SetFastReverb set the amount of echo to mix into
  the sound.  It's a very mechanical sounding echo, kind of like the spring
  reverb on guitar amps.  The effect is much like the echo effects in
  Super Mario World on the SNES.
  FX_SetReverb accepts values from 0 to 255. 0 means no reverb and 255
  is 100% reverb (sound repeats infinitely).  Values above 96 are probably
  overkill.  Speedwise, reverb costs about as much as one sound effect.
  FX_SetFastReverb uses a bitwise shift instead of table lookup to scale
  the volume of the echo.  A value of 0 is no echo, 1 is echo at half
  volume (equivalent to 128 with FX_SetReverb), 2 to 1/4 volume
  (equivalent to 64), etc.  If you're not doing dynamic changes in reverb,
  this may be preferable since it's a bit faster.
4/7/95
- void MUSIC_RegisterTimbreBank( unsigned char *timbres );
  Added the function MUSIC_RegisterTimbreBank to allow developers to use
  their own FM timbres on Sound Blaster and Adlib compatibles.  Use the
  supplied utility MAKETMB to create data files using a script and IBK
  files.  The resulting file can be loaded by the program at runtime and
  a pointer to it passed to MUSIC_RegisterTimbreBank.  Afterwards, the
  memory can be deallocated (the audio library copies the supplied timbres
  over the default timbres.
  If you decide to use your own timbres, I can recommend a good shareware
  program that helps you to create them.  Just give me a call.
3/17/95
- Finished looping functions.  Here is how to loop a sample:
  For a raw sample, use FX_PlayLoopedRaw.  Here is it's definition:
     int FX_PlayLoopedRaw
        (
        char          *ptr,
        unsigned long  length,
        char          *loopstart,
        char          *loopend,
        unsigned       rate,
        int            pitchoffset,
        int            vol,
        int            left,
        int            right,
        int            priority,
        unsigned long  callbackval
        );
     ptr is a pointer to the start of the sample.  It can actually be
  later in the sample than the start of the loop.  This is usefull for
  MOD players which may start a sample at an offset into the sound.
     length is the number of samples to play before the loop begins.  In
  most cases, this will be ( loopend - ptr ).  It may seem strange to be
  able to play past the end of the loop on the first time through the
  sound, but it's the most flexible.
     loopstart is a pointer to the start of the loop.  The can be before
  or after the start of the sample.
     loopend is a pointer to the end of the loop.  This points to the last
  sample to play in the loop.
  For a WAV file, use FX_PlayLoopedWAV.
     int FX_PlayLoopedWAV
        (
        char *ptr,
        long  loopstart,
        long  loopend,
        int   pitchoffset,
        int   vol,
        int   left,
        int   right,
        int   priority,
        unsigned long callbackval
        );
     ptr is the start of the WAV file.  WAV files currently do not have
  the flexibility that raw files do, in that the sound must start at the
  beginning of the WAV file.  All looping is based past the beginning of
  the WAV file.  The sound will loop after the sample pointed to by
  loopend.
     loopstart is the offset (not pointer) of the sample to begin the loop
  on.  This must be a positive value.  Any negative values or values past
  the end of the sound will cause the WAV to only play once.
     loopend is the offset (not pointer) of the sample to end the loop on
  (the sample will loop after this sample is played).  If the offset is
  past the end of the sample, the loop end will be set to the last sample
  in the file.
  For a VOC file, use FX_PlayLoopedVOC.
     int FX_PlayLoopedVOC
        (
        char *ptr,
        long  loopstart,
        long  loopend,
        int   pitchoffset,
        int   vol,
        int   left,
        int   right,
        int   priority,
        unsigned long callbackval
        );
     ptr is the start of the VOC file.  VOC files currently do not have
  the flexibility that raw files do, in that the sound must start at the
  beginning of the VOC file.  All looping is based past the beginning of
  the VOC file.  Since VOC files can contain multiple blocks of data,
  looping samples will only play the first block of data.
     loopstart is the offset (not pointer) of the sample to begin the loop
  on.  This must be a positive value.  Any negative values or values past
  the end of the sound will cause the VOC to play normally.
     loopend is the offset (not pointer) of the sample to end the loop on
  (the sample will loop after this sample is played).  If the offset is
  past the end of the sample, the loop end will be set to the last sample
  in the file.
3/16/95
- I have changed the names of the two functions for playing VOCs.  It just
  no longer makes sense to call them PlaySound when there are a variety of
  file formats supported.
  Here's an updated list of functions that play digitized sound:
  FX_PlayRaw       : Plays raw sampled data once
  FX_PlayLoopedRaw : Plays raw sampled data with looping
  FX_PlayWAV       : Plays a WAV file once
  FX_PlayLoopedWAV : Plays a WAV file with looping
  FX_PlayVOC3D     : Plays a VOC file once using '3D' panning
  FX_PlayVOC       : Plays a VOC file once
  FX_PlayLoopedVOC : Plays a VOC file with looping
  I'm thinking about moving the '3D' routines out of the library since
  they are really just panning tables.  This way, users could create their
  own method.
3/15/95
- Increased the reported maximum number of voices you can play on some cards
  to 32.  Since all voice tables are dynamically allocated, you could have
  more, but anything beyond 16 is a bit ridiculous.  The Gravis Ultrasound
  is limited to 24 currently, and any voices used for sound fx leaves fewer
  voices for music.  In any case, 8 voices is the recommended maximum, but
  if you have reason to use more, go ahead.
3/14/95
- Added special routines optimized to mix only 1 channel into stereo.
  Whenever the volume of the left or the right channel is zero for a
  voice, only one channel is mixed.  This is especially useful if you
  intend to play a sound with the left and right channels' frequencies are
  slightly different.  Peter Freese (from Q Studios, who are developing
  Blood) needs to do this for his Sonic Holography 3D sound library that
  he is developing.
- Fixed a problem on the Sound Source.  I noticed that pitches were higher
  than normal on the Sound Source, and discovered that it was possible to
  overflow the FIFO buffer without causing the overflow flag to be set.
  Since the Sound Source can only play sound at 7khz, I must poll the
  port to see if it's ready for sound.  The overflow flag sometimes doesn't
  register imediatly, so I changed my routine to write only 14 samples at
  a time to the port.  This causes the pitch to be nearly 7khz.
3/13/95
- Increased the resolution of the volume levels that digitized sounds can
  play from 16 to 64.  Since the volumes were passed in as 0 to 255, there
  is no need for users to change any code to take advantage of this.
  Although this requires more memory (in 16bit mode, 32k as opposed to 8k),
  it means smooth volume changes and allows for MOD playback.
- Added new function for changing pitch: FX_SetFrequency.  This allows you
  to change the rate that a sound is mixed at.  This isn't very usefull for
  VOC playback since the VOC file can override the rate, it is very usefull
  for playback of raw data since you will already know the sampling rate.
  This is also usefull for MOD playback.
3/10/95
- Added embedded looping commands for MIDI files.  To loop a specific
  track, use controller 116 on any MIDI channel on that track with a value
  of 0 for infinite, or > 0 for the number of times to loop.  To mark the
  end of a loop, use controller 117 with a value of 127.  NOTE: Currently,
  you must put loop info on all tracks to if you want all of them to loop.
  This may seem like a pain at first, but I thought that it would give
  the musician extra flexibility.  If no one likes this, I'll change it so
  that you only have to do one loop controller.
3/9/95
- Added support for .WAV files and raw samples.
- Added two new functions allowing the programmer to control the relative
  volume of each MIDI channel of a song.  Use MUSIC_SetMidiChannelVolume
  to set the volume of a MIDI channel and MUSIC_ResetMidiChannelVolumes
  to restore all channels to full volume.  I do not reset the volumes
  when you play a new song so that you can set the volume of all the
  channels before playing a song.
2/14/95
- Found a potential bug where the pitch scaling function could go outside
  of the array bounds given certain pitch offsets.
2/4/95
- Stereo FM temporarally commented out for ROTT.  Seems to take too long on
  some computers and causes the mouse driver to miss interrupts.
  Have to contact Creative Labs to find out which chips have fast timings.
2/3/95
- Added FX_VoiceAvailable.  Checks if a voice can be played at the
  specified priority.
1/30/95
- Version 1.02 uploaded for all current developers.
- Various initialization routines improved.
- Now performs checks to see if DMA and IRQ are working.
- Added the ability to do custom Sound Blaster setup.  Now users can enter
  the DMA, card address, and IRQ.
- Fixed problem with SB16 and Sound Canvas Daughterboard.  This was
  caused by an undocumented IRQ disable flag in the SB16.
- GUS initialization no longer bombs if all the patches in GUSMIDI.INI
  could not be loaded.  I was forced to do this due to the problems
  that can be caused by a slight error in the patch list.  Gravis has changed
  the patch sizes over several versions making it difficult to create a patch
  list that works on all cards.  Since the last patches loaded are percussion,
  these are the ones that will be affected.
1/11/95
- Additive timbres now respond to volume changes.
1/7/95
- Added better error checking and detection for AWE32.
1/4/95
- Version 1.01 uploaded for all current developers.
12/20/94 - Version 1.01
- Version built for Rise of the Triad.
- Fixed some problems with MIDI playback on SoundScape.  It was
  missing program changes (instruments).
- GUS uses GUSMIDI.INI in local directory instead of ULTRAMID.INI.
  This is so that a custom patch list could be included with your
  game.  If you own a GUS, copy the file ULTRAMIDI.INI from your
  ULTRASND\MIDI directory.  I will come up with something more
  convenient in the future.
- When command line parameter "ASSVER" used, the library returns
  an error when initialized.  The error string will contain the
  version text.  This was done so that your programs could perform
  their normal shutdown procedure before exiting to DOS.
12/15/94
- Optimized mixing routines.  50% increase in 8-bit mono and stereo
  playback and 20%-30% increase in 16-bit playback.
11/28/94
- Pitch tables are now linked in rather than generated at startup.
  This gets rid of any floating point code.
11/14/94
- Added function FX_SoundsPlaying to report the number of voices
  playing.
11/13/94
- Fixed problem with SoundScape where it wasn't reporting the MaxBits
  and MaxVoices properly.
11/6/94 - Version 1.00
- SoundScape now automatically gets MIDI port address from SNDSCAPE.INI.
- WaveBlaster now automatically gets MIDI port address from the BLASTER
  environment string.
- TaskMan now locks the link list manager's memory in case FX_Init
  and MUSIC_Init are not called.
- All developers must make sure that they ask for the midi port on the
  following cards: GenMidi, and SoundCanvas.
11/5/94
- Made the GUSWAVE play voice routine allocate voice with a priority of
  0, which tells gf1_play_digital to not attempt voice stealing.  This
  prevents the music playback from stealing sound fx voices and was
  the source of the bug in Boppin' where voices would be cutoff.
- VOC decoder now deals with block type 8 properly.  Bug caused it to be
  ignored.
- GUS set volume function now sets volume of all playing voices.  Before,
  it would only set the volume for new voices.
10/31/94
- Now keeping track of version number.  From any game that uses the
  sound system, you can type "ASSVER" to get version number.
- Added command line parameter "DEBUGGUS" to Gravis Ultrasound GUSWAVE
  to help track down a voice dropout problem.  I'll eventually add similar
  parameters to other modules.
10/26/94
- Added native support for the Ensoniq SoundScape.  This card is capable
  of playing 8 and 16 bit stereo or mono data and uses wavetable synthesis
  for music.  Please add this to your config.
10/23/94
- Did some optimizing and found that for 16-bit mixing it is slightly
  quicker to use code to do harsh clipping than to use tables.  This
  gets rid of a 64k lookup table I had.  With 8-bit sound, it is quicker
  to use a table, but it only takes up 512 bytes.
  Here is the various mixing modes in order of fastest to slowest:
      8-bit mono, 16-bit mono, 8-bit stereo, 16-bit stereo.
  Just for reference, 8-bit mono is only about 40% faster than 16-bit
  stereo.  And considering that at 11khz, mixing is only called about
  10 times a second, it's not an incredible difference.
10/20/94
- Fixed the PAS slowdown bug.  DMA channel was not being disabled after
  a PCM transfer.  On some computers with PAS-16s, when the DMA circuitry
  in the PAS was turned off, it left the DRQ line on the DMA in a floating
  state, causing the DMA to do a continuous burst transfer, which would
  slow down the computer.  This would not happen on other cards, since
  their DMA circuitry is always active.
- Fixed the WaveBlaster bug.  Bug caused by a problem with playing
  WaveBlaster music and Sound Blaster-16 that is completely undocumented
  in Creative Labs' developer kits.
10/4/94
- Added several new cards to enum list in SNDCARDS.H.  Unless you use all
  of these, you are not supporting all sound cards----PUT THEM IN!
- AWE32 support for General MIDI.
- Midi callback system for implimenting digital drums and other music-
  synced events.
9/7/94
- All interrupt service routines now allocate their own 1024 byte stack
  in case Windows or OS/2 don't leave enough stack space.  This was
  suggested by Steve Lepisto (Accursed Toys) who kindly supplied example
  code.
9/6/94
- PC speaker sound fx can now be turned off by setting the volume to 0.
- Turned off panning on the FM rhythm channel.
- Found and fixed another FM panning bug.
9/2/94
- Added ability to reroute data from a MIDI channel to a user function.
  Good for digital drums or animation synced to music.
- Fixed a problem on the GUS with instruments that use tremelo.  Only
  showed up when volume was set to 0.
9/1/94
- Fixed the high volume notes on GUS.
8/26/94
- Ummm, added revision notes!  What follows is what I've added in the
  past few weeks.
- Locked down memory on all cards except GUS.
- Support for higher IRQ's on PAS and Sound Blaster.  Requires Dos4gw Pro
  version 1.97.
- Internal support for PAS mixer control (using MVSOUND.SYS).  Requires
  Dos4gw Pro version 1.97.
- FM pan position defaulted to full left.  Only noticable if midi file
  didn't set its own pan positions.  Now defaults to center.
- Added stereo panning on FM music.
- Added stereo detuning on FM music.
- Added harsh clipping so that digitized sounds playback at same volume
  no matter how many voices are allocated.  Could distort when many sounds
  are playing, but that's the price you pay.
- Changed panning from sine tables to a linear attenuation ramp as you
  deviate from center.
- GUS can now choose number of voices to use (used to be 8 no matter what).
                            Header files:
There are four header files that you'll need: TASK_MAN.H, SNDCARDS.H,
MUSIC.H and FX_MAN.H.  MUSIC.H contains all functions that control music.
FX_MAN.H contains all functions for control of digitized and synthesized
sound.  SNDCARDS.H contains sound card definitions and is brought in by
MUSIC.H and FX_MAN.H.  TASK_MAN.H is a module used for timer management.
SNDCARDS.H defines the following enumerated type:
   typedef enum
      {
      SoundBlaster,
      ProAudioSpectrum,
      SoundMan16,
      Adlib,
      GenMidi,             data;
   ( *Count )++;
   }
void main
   (
   void
   )
   {
   task *TimerTask;
   int  Count;
   Count = 0;
   // Schedule our timing task
   TimerTask = TS_ScheduleTask( TimeFunction, 30, 1, &Count );
   // Make sure we start it
   TS_Dispatch();
   while( !kbhit() )
      {
      printf( "Count = %d\n", Count );
      }
   // clear the key that was hit
   getch();
   // Hey, let's change it so it runs at 1000 times a second!
   TS_SetTaskRate( TimerTask, 1000 );
   while( !kbhit() )
      {
      printf( "Count = %d\n", Count );
      }
   // clear the key that was hit
   getch();
   // Stop the clock before we leave
   TS_Terminate( TimerTask );
   }
Try out that program to see how it works.
task *TS_ScheduleTask( void ( *Function )( task * ), int rate,
                       int priority, void *data );
TS_ScheduleTask add your function to the list of routines that use the
timer.  You can it set to any rate you would normally be able to program
the PC's internal timer for.
rate is the number of times per second your routine should be called.
priority is used for when tasks occur simultaniously and must be called in
a specific order.
data allows you to pass variables into your timer.  You could set up
several tasks using one function, but sending in pointers to different
data.  Here's how to use it:
   int   Data1 = 0;
   int   Data2 = 100;
   task *Timer1;
   task *Timer2;
   Timer1 = TS_ScheduleTask( Function, 100, 1, &Data1 );
   Timer2 = TS_ScheduleTask( Function, 200, 1, &Data2 );
   TS_Dispatch();
   .
   .
   .
void Function
   (
   task *Task
   )
   {
   int *data;
   data = ( int * )Task->data;
   *data++;
   }
The task * that's passed in can also be used to control your task.
For example, to terminate your task after a certain period of time:
void Function
   (
   task *Task
   )
   {
   int *data;
   data = ( int * )Task->data;
   *data++;
   // After 100 times, this task commits suicide.
   if ( *data > 100 )
      {
      TS_Terminate( Task );
      }
   }
void TS_Dispatch( void );
TS_Dispatch starts the timer on all waiting scheduled events.  Several
events may be scheduled before calling TS_Dispatch.
int TS_Terminate( task *ptr );
TS_Terminate ends a task.  Use this when your done with a task.
void TS_SetTaskRate( task *Task, int rate );
TS_SetTaskRate allows you to change the rate a task runs at after you've
started it up.
PM is a quick and dirty file player for DOS using the Apogee Sound System.
If you type PM with no arguments you'll get the following information:
PM Version 1.10 by Jim Dos
Usage: PM [ midifile ] CARD=[ card number ] MPU=[ port address in hex ]
          TIMBRE=[ timbre file ]
   card number =
      0 : General Midi
      1 : Sound Canvas
      2 : Awe 32
      3 : WaveBlaster
      4 : SoundBlaster
      5 : Pro AudioSpectrum
      6 : Sound Man 16
      7 : Adlib
      8 : Ensoniq SoundScape
      9 : Gravis UltraSound
So, if you want to play FANFARE.MID on a Sound Canvas at port 330h, type:
   PM Fanfare.mid card=1 mpu=330
The TIMBRE parameter tells PM to use the MAKETMB generated timbre file
specified.  This is useful when you're testing new timbres and want to
see how they sound.
You can also set an environment variable that has the card settings you
want to use by default and PM will use it.  Just type "SET PM=,"
where  is the card number and  is the address of the MIDI card
(if any).  So for a Sound Canvas at port 330h, you would use "SET PM=1,330".
Then you can just type "PM Fanfare.mid" on the command line to play your
song without worrying about the card and mpu parameters.
If you have any problems or questions, call me at (214)-271-1365 ext. 221.
Jim Dos
Here's a quick explanation of the files included in this zip file:
  README.TXT : This file
      PM.TXT : A quick explanation of the PM.EXE midi player
      PM.EXE : PM - a command line midi file player
 MAKETMB.EXE : this generates TMB files for use by PM.  Read GMTIMBRE.INI
GMTIMBRE.TMB : A timbre file generated by MAKETMB
GMTIMBRE.INI : An example timbre script for MAKETMB.  Generates GMTIMBRE.TMB
  PERCUS.IBK : General MIDI percussion instruments.  Used by GMTIMBRE.INI
 GENMIDI.IBK : General MIDI percussion instruments.  Used by GMTIMBRE.INI
 TIMBRES.ZIP : A couple sets of instrument banks that can be used as
               source material for fm instruments.
MAKETMB is a simple utility to allow developers to develop and use their
own FM instrument banks with the Apogee Sound System.  It can generate
either a data file or source code (ask me if you'd prefer to use this
feature) containing the instruments and usable by the sound system.
The benefit of having the instruments in a data file is that multiple files
could be made, each with instruments specific to a particular song or set
of songs.  It also allows you to make quick changes to the instruments and
hear them in a song (using PM) very quickly, without having to recompile
the code (very useful for musicians).
I know I'm terrible at writing documentation, so if these files are a little
vague, give me a call and I'll walk you through it.
Jim Dos
(214)-271-1365 Ext. 221
( ) NME does not always exit level upon killing him
( ) Game locks when firing Darkstaff or Dog Mode at El Oscuro
( ) Network server locks up when exiting net game
( ) Stand( )alone server doesn't work consistently
( ) Break out with error message if ROTT is run with no sound.rot
( ) Quickload question should have (Y/N) on end
( ) Phone number length needs to be lengthed.
( ) Network packets need checksum word added to their packets
( ) VR gear new mouse sub functions
( ) Dog mode still locks up as well as some bat blast stuff
( ) Fix Live Remote Ridicule
( ) Wall can push you into platform so you fall down inside of it and be
    stuck.
( ) Hanging at menu after a few network/modem games
( ) Hanging at warp menu prompt
( ) Warp Menu: chose level, screen went black, some how still in level select
( ) Unexpect Interrupt
( ) Generat random levels and battle levels.  Choose alternate both.
    New game End Game New Game Kaboom
( ) Make snake heads hurt to touch (fixes snake trapping you)
( ) Fix missiles not hitting enemy when standing face to face
( ) lockup on snake with one head left
( ) gamepad enabled before joystick enabled
( ) bark blast under dog walls and stuff
( ) config files saved in alternat RTS directory
( ) people complaining about reconfiging joystick each time
( ) Panic mapping over modem glass in 8 megs not in 4 megs
( ) Voice credit for OverPatrol (CHuck) Lightning guard (william)
MAP Stuff
( ) level 1( )3 in random powerup crossroads room, two different wall types
( ) Krist, if led outside of room, goes up disks, but can't go down them.
  So he winds up running around sky.  Fix map to lock door so Krist cannot
  come out...
  DISPSTICK                // enable cheats
  \ECC                     // enable cheats
  SLACKER                  // three keys, more health
  \BUM                     // three keys, more health
  CHOJIN                   // normal god mode
  \WWW                     // normal god mode
  GOTO                     // warp
  \GTL                     // warp
  SIXTOYS                  // all keys, armor, 100% health
  \GAI                     // all keys, armor, 100% health
  TOOSAD                   // god mode powerup
  \GOD                     // god mode powerup
  // Register only
  WOOF                     // dog mode powerup
  \DOG                     // dog mode powerup
  // end
  FLYBOY                   // mercury mode powerup
  \MER                     // mercury mode powerup
  BADTRIP                  // shrooms mode powerup
  \SHR                     // shrooms mode powerup
  BOING                    // elasto mode powerup
  \ELA                     // elasto mode powerup
  GOOBERS                  // warp to level 1, start with pistol
  \GOO                     // warp to level 1, start with pistol
  WHACK                    // hurt player 10%
  \OOF                     // hurt player 10%
  SPEED                    // run fast all the time
  \RFA                     // run fast all the time
  PANIC                    // back to normal
  \PAN                     // back to normal
  DIMON                    // light diminishing on
  \DON                     // light diminishing on
  DIMOFF                   // light diminishing off
  \DOF                     // light diminishing off
  LONDON                   // fog on (0x00 - 0x80 minmax)
  \FON                     // fog on (0x00 - 0x80 minmax)
  NODNOL                   // fog off (0x80 - 0xFF minmax)
  \FOF                     // fog off (0x80 - 0xFF minmax)
  GOGATES                  // blow out of game
  \L8R                     // blow out of game
  GOARCH                   // end the current level
  \ECL                     // end the current level
  GOTA386                  // floor and ceiling off
  \CON                     // floor and ceiling off
  GOTA486                  // floor and ceiling on
  \COF                     // floor and ceiling on
  SHOOTME                  // bullet proof armor
  \BAR                     // bullet proof armor
  BURNME                   // fire proof armor
  \FAR                     // fire proof armor
  LUNGDUNG                 // gas mask
  \GAR                     // gas mask
  HUNTPACK                 // all keys, armor, 100% health, MP40,
  \OFP                     // all keys, armor, 100% health, MP40,
  86ME                     // kill player
  \DIE                     // kill player
  NEER                     // re-enter level
  \REL                     // re-enter level
  JOHNWOO                  // give double pistol
  \GW2                     // give double pistol
  PLUGME                   // give mp40
  \GW3                     // give mp40
  VANILLA                  // give bazooka
  \GW4                     // give bazooka
  HOTTIMES                 // give heatseeker
  \GW5                     // give heatseeker
  BOOZE                    // give drunk missile
  \GW6                     // give drunk missile
  FIREBOMB                 // give firebomb
  \GW7                     // give firebomb
  BONES                    // give firewall
  \GW8                     // give firewall
  SEEYA                    // give god hand
  \GW9                     // give god hand
  // Register Only
  SPLIT                    // give split missile
  \GWA                     // give split missile
  KESOFDEATH               // give kes
  \GWB                     // give kes
  HOMERUN                  // give bat
  \GWC                     // give bat
  CUJO                     // give dog weapon
  \GWD                     // give dog weapon
  // end
  RIDE                     // give MISSILE CAM
  \CAM                     // give Missile Cam
  WHERE                    // turn where am i on/off
  \HUD                     // give hud
  \FUN                     // Rotation fun
  RECORD                   // Demo RECORD
  STOP                     // Demo stop recording
  PLAY                     // Demo Playback
  \EKG                     // Engine Killing Gibs
  MAESTRO                  // JukeBox
  \LEE                     // JukeBox
  CARTIER                  // Map Cheat
  \MAP                     // Map Cheat
  \YOU                     // Secret Message
  HAVE                     // Secret Message
  \NO                      // Secret Message
  LIFE                     // Secret Message
ROTT.EXE
========
NOWAIT       go to menu, skip intro
NOSOUND      no music no sound
NOW          go immediately into game default difficulty default level 1
DOPEFISH     Scott Head intro, random sounds in credits, burping while
             caching, silly strings, eluder/deluder as scott's head,
             silly death cam, silly menu sounds
MAPSTATS     Print out map statistics to MAPDEBUG.TXT
TILESTATS    Print out tile statistics to MAPDEBUG.TXT
VER          Rott version number
PAUSE        Pause after printing startup info
SOUNDSETUP   Launch sound Setup WARP [level] Launch to any level (1 based)
LEVELSIZE    compute memory needed for level
SLOWDEATH    slow down death rotation
QUIET        get rid of all start up text except for errors
FILE
FILE1        add external wads for graphic replacement
FILE2
NOJOYS       disable joystick test
NOMOUSE      disable mouse test
SPACEBALL    enable spaceball avenger
CYBERMAN     enable cyberman
ASSASSIN     enable wingman assassin
NOECHO       turn off reverb effect
DEMOEXIT     exit the game when demo is interrupted
TIMELIMIT    play for a certain amount of time in seconds
             once time is gone game ends (unlimited lives)
             get more time by killing guards and picking up ankhs
             must use MAXTIMELIMIT as well
MAXTIMELIMIT the max time to count down from so if you keep killing
             guards you cannot get an infinite amount of time
             must use TIMELIMIT as well
ENABLEVR     enable Virtual Reality support for HMD's (Head mounted disp)
WARP         warp to a specific level 1 based level 1 is 1
NET          Used by ROTTSER and ROTTIPX
IS8250       Used by ROTTSER whether you have an 8250 or not
TEDLEVEL     Ted stuff
(DEV ONLY) MONO         enable mono-monitor support (Development only)
(DEV ONLY) TRANSPORT    Warp to any x,y,angle in level (Development only)
(DEV ONLY) SCREENSHOTS  get rid of screen title stuff (Development only)
ROTTSER.EXE
========
VECTOR       interrupt vector with which to communicate with ROTT
ANSWER       answer mode
DIAL         dial mode
PAUSE        pause before launching
STATS        print out run time stats after ROTT returns
PLAYER       0 - makes you the master
             1 - makes you not the master
             must be used on both systems with different values, to work
             properly
ROTTIPX.EXE
========
VECTOR       interrupt vector with which to communicate with ROTT
PAUSE        pause before launching
NODES        number of players in game
SOCKET       network socket to use
SERVER       specify this computer to be a server
STANDALONE   specify this server to be standalone
             else client on top of server
MASTER       used by client to make yourself the master
REMOTERIDICULE turn on remote ridicule support
                              Rise of the Triad
                                 Version 1.1
                                 Hacker Info
RTL & RTC File format:
----------------------
Rise of the Triad (ROTT) uses two file extensions for levels data, RTL
and RTC.  RTC indicates that the file is for Comm-bat (multiplayer) play
only and does not contain any enemies or exits.  RTL indicates the file
can can be used for both Comm-bat and standard game levels.  In Comm-bat,
the enemies in RTL maps in standard play are not present during Comm-bat
games and the exit and entrance arches behave like teleporters.  Other than
these differences, the two files are alike.
The RTL/RTC file format changed with the release of ROTT version 1.1.
Since the shareware version of ROTT cannot use alternate levels, this
should not be a problem for map designers.  The new format is much more
formal.  If any changes are made in the format in the future,  the first 8
bytes of the file will inform you if it is compatible with your editor/viewer.
The RTL/RTC file is broken into three sections:  Version info, Header block,
and Data block.
                            RTL/RTC version info
This 8 byte block of data indicates what type of file it is and which
version of the RTL/RTC file format it is.
  Offset   Size    Description
-------------------------------------------------------------
    0        4     Format signature
    4        4     Version number
Format signature :
This is used to indicate what type of levels are contained within the
file.  This is a null-terminated string containing either "RTL" or "RTC".
Version number :
0101h for version 1.1.  If this value is higher, it indicates that the file
format has changed.  This is NOT the ROTT version.
                            RTL/RTC Header block
The header block contains an array of 100 structures with the following
format:
  Offset   Size    Explanation
-------------------------------------------------------------
    0        4     Used flag
    4        4     CRC
    8        4     RLEWtag
   12        4     MapSpecials
   12        4     Offset in file of Wall plane
   16        4     Offset in file of Sprite plane
   20        4     Offset in file of Info plane
   24        4     Length of Wall plane
   28        4     Length of Sprite plane
   32        4     Length of Info plane
   36       24     Name of level
Used flag :
This is non-zero if a map exists at this position.
CRC :
This value is used to determine if all the players in a multiplayer game
are using the same maps.  You can use any method you like to calculate this
value.
RLEWtag :
This is the run-length encoding tag used for compressing and decompressing
the map data.  The use of this will be described below.
MapSpecials :
This is used for flags that describe special conditions for the level.
Currently only one flag is used.  If Bit 0 is set, then all the pushwalls
will be activated in Comm-bat mode.  This is done in case there are player
start locations within hidden areas and the player would be trapped until
a pushwall was activated.
Offsets :
The Wall, Sprite, and Info plane offsets are each absolute offsets of the
data from the beginning of the file.
Lengths :
The Wall, Sprite, and Info plane lengths are each lengths of the
uncompressed data.
Name of level :
This is a null-terminated string containing the name of the level.
Although there is 24 bytes available, level names should be at most 22
bytes long.
                             RTL/RTC Data block
When expanded, ROTT maps contain 3 planes of 128 by 128 word sized data.
They are stored in the RTL/RTC files as 3 blocks of run-length encoded
data.  The procedure for decompressing them is as follows:
    1) Allocate 128 * 128 words of memory (32768 bytes)
    2) Read one word from compressed block
    3) If word is equal to RLEWTag, then the next two words are a compressed
       run of data.  The first word is the number of words to write.
       The second word is the value to write map.
       If word was not equal to RLEWTag, then simply write that word
       to the map.
    4) Go back to 2 until all data is written.
Here's an example of the procedure in C.
/*---------------------------------------------------------------------
   Function: RLEW_Expand
   Run-length encoded word decompression.
---------------------------------------------------------------------*/
void RLEW_Expand
   (
   unsigned short *source,
   unsigned short *dest,
   long length,
   unsigned short rlewtag
   )
   {
   unsigned short value;
   unsigned short count;
   unsigned short *end;
   end = dest + length;
   while( dest < end );
      {
      value = *source;
      source++;
      if ( value != rlewtag )
         {
         //
         // uncompressed data
         //
         *dest = value;
         dest++;
         }
      else
         {
         //
         // compressed string
         //
         count = *source;
         source++;
         value = *source;
         source++;
         //
         // expand the data
         //
         while( count > 0 )
            {
            *dest = value;
            dest++;
            count--;
            }
         }
      }
   }
Here is sample code for loading a ROTT map.
#include 
#include 
#include 
#include 
/*---------------------------------------------------------------------
   Map constants
---------------------------------------------------------------------*/
#define MAXLEVELNAMELENGTH           23
#define ALLOCATEDLEVELNAMELENGTH     24
#define NUMPLANES                    3
#define NUMHEADEROFFSETS             100
#define MAPWIDTH                     128
#define MAPHEIGHT                    128
#define MAP_SPECIAL_TOGGLE_PUSHWALLS 0x0001
#define WALL_PLANE    0
#define SPRITE_PLANE  1
#define INFO_PLANE    2
/*---------------------------------------------------------------------
   Type definitions
---------------------------------------------------------------------*/
typedef struct
   {
   unsigned long used;
   unsigned long CRC;
   unsigned long RLEWtag;
   unsigned long MapSpecials;
   unsigned long planestart[ NUMPLANES ];
   unsigned long planelength[ NUMPLANES ];
   char          Name[ ALLOCATEDLEVELNAMELENGTH ];
   } RTLMAP;
/*---------------------------------------------------------------------
   Global variables
---------------------------------------------------------------------*/
unsigned short *mapplanes[ NUMPLANES ];
/*---------------------------------------------------------------------
   Macros
---------------------------------------------------------------------*/
#define MAPSPOT( x, y, plane ) \
   ( mapplanes[ plane ][ MAPWIDTH * ( y ) + ( x ) ] )
#define WALL_AT( x, y )   ( MAPSPOT( ( x ), ( y ), WALL_PLANE ) )
#define SPRITE_AT( x, y ) ( MAPSPOT( ( x ), ( y ), SPRITE_PLANE ) )
#define INFO_AT( x, y )   ( MAPSPOT( ( x ), ( y ), INFO_PLANE ) )
/*---------------------------------------------------------------------
   Function: ReadROTTMap
   Read a map from a RTL/RTC file.
---------------------------------------------------------------------*/
void ReadROTTMap
   (
   char *filename,
   int mapnum
   )
   {
   char            RTLSignature[ 4 ];
   unsigned long   RTLVersion;
   RTLMAP          RTLMap;
   int             filehandle;
   long            pos;
   long            compressed;
   long            expanded;
   int             plane;
   unsigned short *buffer;
   filehandle = open( filename, O_RDONLY | O_BINARY );
   //
   // Load RTL signature
   //
   read( filehandle, RTLSignature, sizeof( RTLSignature ) );
   //
   // Read the version number
   //
   read( filehandle, &RTLVersion, sizeof( RTLVersion ) );
   //
   // Load map header
   //
   lseek( filehandle, mapnum * sizeof( RTLMap ), SEEK_CUR );
   read( filehandle, &RTLMap, sizeof( RTLMap ) );
   if ( !RTLMap.used )
      {
      //
      // Exit on error
      //
      printf( "ReadROTTMap: Tried to load a non existent map!" );
      exit( 1 );
      }
   //
   // load the planes in
   //
   expanded = MAPWIDTH * MAPHEIGHT * 2;
	for( plane = 0; plane > 1, RTLMap.RLEWtag );
      free( buffer );
      }
   close( filehandle );
   }
MAP WEIRDNESS
-------------
You can pretty much figure out most of the map data easily, but there are
a few things in the map which are a little oddly set up.  Here's a few
helpful items.
THE UPPER CORNER
The first row of a map contains vital information to setting up a map.
In the first plane (WALLS) are these values:
0,0   FLOOR # (0xB4 through 0xC3, though we might cut some)
1,0   CEILING # (0xC6 through 0xD5, or skies: 0xEA to 0xEE)
2,0   BRIGHTNESS LEVEL (0xD8 to 0xDF, from dark to light)
3,0   RATE AT WHICH LIGHT FADES OUT WITH DISTANCE
        (0xFC to 0x010B, fast to slow)
In the second plane (SPRITES) are these:
0,0   Height of level
	(1-8 ranges from 0x5A to 0x61, 9-16 is from 0x01C2 to 0x01C9)
1,0   Height that sky is at relative to level (with same 1-16 arrangement)
        (not needed for level with a ceiling)
2,0   Icon for NO FOG (0x68) or FOG (0x69)
3,0   Light sourcing icon (0x8B: if present, lights illuminate walls)
Optional items in the upper corner are:
Second Plane
	Lightning icon (0x0179)
	Timer icon (0x79: third plane points 0xXXYY to X,Y location of
	  timed thing--time in minutes/seconds there is MMSS in decimal
	  digits, so 0130 is 1 minute thirty seconds--and to one side of
	  that timed thing is the end time in the same format. This, for
	  instance, would say when to shut the door that opened at the
	  start time)
Third Plane (INFO)
	Song number: 0xBAnn, where nn is song number.  If not present,
	 the game will choose song 0.  If greater than the number of
	 level songs (18 in shareware), the game will blow out.
DISKS
Gravitational Anomaly Disks (GADS) are set up with a GAD icon in the
second plane and a height in the third plane.  The actual graphic has a
disk in the top quarter, so to put one on the floor, you sort of have to
put the object IN the floor, so the disk will be at the right height.
Heights for objects start with 0xB0 and have that last byte as a
tiles-off-the-floor nybble and sixteenths-of-a-tile fraction.
So 0xB000 is, for normal sprites, resting on the floor.
For disks, that would be a disk you could stand on to be one story
(eight feet) in the air. The heights of disks usually go by sixes (that's
the maximum they can be apart and you can still climb them like stairs) or
fours (for a more gradual ascension).  Here are three sets of height
values.  The values of 0xB0F1-$B0FE are into the floor, and $B0F6 is right
about floor height.
by 6     by 4     by 2
B0F6     B0F6     B0F6
B0FC     B0FA     B0F8
B002     B0FE     B0FA
B008     B002     B0FC
B00E     B006     B0FE
B014     B00A     B010
B01A     B00E     B012
B020     B012     B014
B026     B016     ...
B02C     B01A
B032     B01E
B038     B022
B03E     B026
B044     B02A
B04A     B02E
B050     B032
B056     B036
B05C     B03A
B062     B03E
B068     B042
B06E     B046
B074     B04A
B07A     B04E
If you need higher ones, calculate them yourself, man.
SWITCHES AND TOUCHPLATES
Everything activated by a switch or touchplates points to the switch or
touchplate that activates it, with the standard 0xXXYY format.  This way
tons of things can be activated by one switch.  To make a door open with
multiple switches/touchplates, make it a few tiles wide and have different
parts of the door point to the different switches.
LOCKED DOORS
Locked doors are normal doors with a key sprite icon placed on them.
============================================================================
The ROTT WAD Format
-------------------
        Most of you out there are probably very familiar with the WAD file
format developed by Id Software.  We borrowed the format with their
consent and use it for all the data in Rise of the Triad.
        The WAD structure itself is identical to that of other WAD's,
where the WAD header is as follows:
typedef struct
{
	char	identification[4];
	long	numlumps;
	long	infotableofs;
} wadinfo_t;
and the WAD directory is made up of [numlumps] of:
typedef struct
{
	long		filepos;
	long		size;
	char		name[8];
} lumpinfo_t;
ROTT Specific Data
------------------
WALLS - Walls are stored in the WAD between the two labels "WALLSTRT" and
"WALLSTOP".  The format of each wall is a 4,096 block of data with no
header.  The bitmaps are grabbed in vertical posts so that drawing in
modex is more straight format.  All walls must be 64 x 64. The walls must
be the first few lumps in the WAD.
MASKED OBJECTS - Masked objects in the wad comprise all actors and
sprites.  They can be found as weapons, objects, actors etc.  They use the
following headers and structures:
typedef struct
{
   short          origsize;         // the orig size of "grabbed" gfx
   short          width;            // bounding box size
   short          height;
   short          leftoffset;       // pixels to the left of origin
   short          topoffset;        // pixels above the origin
   unsigned short collumnofs[320];  // only [width] used, the [0] is &collumnofs[width]
} patch_t;
These are extremely similar to the patches used in another game, except
for the addition of the origsize parameter.
typedef struct
{
   short origsize;         // the orig size of "grabbed" gfx
   short width;            // bounding box size
   short height;
   short leftoffset;       // pixels to the left of origin
   short topoffset;        // pixels above the origin
   short translevel;
   short collumnofs[320];  // only [width] used, the [0] is &collumnofs[width]
} transpatch_t;
Certain objects in the game like masked walls and touch plates will use
the second type of patch which acts like a translucent patch.
SKYS, FLOORS and CEILINGS - Skys are larger than the screen and are made
up of two 256X200 grabs in posts similar to the walls.  The first grab
represents the bottom part of the sky and the second part the top of the
sky.  The skys are denoted by the labels SKYSTRT and SKYSTOP.  Floors and
ceilings use the following structure:
typedef struct
{
   short     width,height;
   short     orgx,orgy;
   byte     data;
} lpic_t;
They can be found between the labels UPDNSTRT and UPDNSTOP.
Okay, enough hints!  Have fun figuring stuff out.
--THE DEVELOPERS OF INCREDIBLE POWER
The ROTT WAD Format
-------------------
        Most of you out there are probably very familiar with the WAD file
format developed by Id Software.  We borrowed the format with their
consent and use it for all the data in Rise of the Triad.
        The WAD structure itself is identical to that of other WAD's,
where the WAD header is as follows:
typedef struct
{
	char	identification[4];
	long	numlumps;
	long	infotableofs;
} wadinfo_t;
and the WAD directory is made up of [numlumps] of:
typedef struct
{
	long		filepos;
	long		size;
	char		name[8];
} lumpinfo_t;
ROTT Specific Data
------------------
WALLS - Walls are stored in the WAD between the two labels "WALLSTRT" and
"WALLSTOP".  The format of each wall is a 4,096 block of data with no
header.  The bitmaps are grabbed in vertical posts so that drawing in
modex is more straight format.  All walls must be 64 x 64. The walls must
be the first few lumps in the WAD.
MASKED OBJECTS - Masked objects in the wad comprise all actors and
sprites.  They can be found as weapons, objects, actors etc.  They use the
following headers and structures:
typedef struct
{
   short          origsize;         // the orig size of "grabbed" gfx
   short          width;            // bounding box size
   short          height;
   short          leftoffset;       // pixels to the left of origin
   short          topoffset;        // pixels above the origin
   unsigned short collumnofs[320];  // only [width] used, the [0] is &collumnofs[width]
} patch_t;
These are extremely similar to the patches used in another game, except
for the addition of the origsize parameter.
typedef struct
{
   short origsize;         // the orig size of "grabbed" gfx
   short width;            // bounding box size
   short height;
   short leftoffset;       // pixels to the left of origin
   short topoffset;        // pixels above the origin
   short translevel;
   short collumnofs[320];  // only [width] used, the [0] is &collumnofs[width]
} transpatch_t;
Certain objects in the game like masked walls and touch plates will use
the second type of patch which acts like a translucent patch.
SKYS, FLOORS and CEILINGS - Skys are larger than the screen and are made
up of two 256X200 grabs in posts similar to the walls.  The first grab
represents the bottom part of the sky and the second part the top of the
sky.  The skys are denoted by the labels SKYSTRT and SKYSTOP.  Floors and
ceilings use the following structure:
typedef struct
{
   short     width,height;
   short     orgx,orgy;
   byte     data;
} lpic_t;
They can be found between the labels UPDNSTRT and UPDNSTOP.
ZIP contents
ROTTIPX               IPX source BC 3.1 format
ROTTSER               SERIAL source BC 3.1 format
ROTTNET  C           4,861 ROTTDRIVER interface code
ROTTNET  H           1,955 "
RT_NET   H           7,008 ROTT packet headers with size info
README   TXT         3,366 This file
ROTT Network Specification:
ROTT does all of it's multi-player communication through a user interrupt
in the range of 0x60 - 0x66.  This is all very similar to DOOM.
Data is passed back in forth between ROTT and the real mode driver by
means of the ROTTNET structure defined in "ROTTNET.H".  "ROTTNET.H" should
not be changed, since it is compiled into ROTT itself.  Here is a
description of what each item in the ROTTNET structure does:
typedef struct
   {
   //
   // The interrupt on which ROTT and the caller are to communicate
   //
   short   intnum;  // ROTT executes an int to send commands
   // communication between ROTT and the driver
   //
   // what type of command we are sending to the real mode driver,
   // a get packet or send packet command
   //
   short   command;                // CMD_SEND or CMD_GET
   //
   // the destination node when we send packets, and the source node when
   // we receive packets.  In network games, the following convention is
   // used for player numbers:
   //
   // For Clients:
   //
   // address 0: their local address
   // address 1: the address of the server
   //
   // For the Packet Server:
   // address 0: local address
   // address 1: address of player 1, if standalone
   //            local address if client on top of server
   //            but still the address of player 1
   // address 2: address of player 2
   // address 3: address of player 3
   // address 4: address of player 4
   //  .
   //  .
   //  .
   //
   // In MODEM games the destination and source are pretty unused since
   // only two player are capable of playing.
   //
   // upon getting a packet, if remotenode = -1 there is no packet ready
   //
   short   remotenode;  // dest for send, set by get (-1 = no packet)
   //
   // length the packet to send
   // length of the packet received
   //
   short   datalength;  // bytes in rottdata to be sent / bytes read
   // info specific to this node
   //
   // The player number of this node
   // In MODEM games it is either 0 or 1
   //
   // IN NETWORK games
   //
   // player 1 = 1
   // player 2 = 2
   // player 3 = 3
   // etc.
   // passing in a consoleplayer value of 0 signifies standalone server
   //
   short   consoleplayer;  // 0-(numplayers-1) = player number
   //
   // numplayers in the game
   //
   short   numplayers;     // 1-11
   //
   // whether the current computer is a server or a client
   //
   short   client;         // 0 = server 1 = client
   //
   // whether we are playing a modem or network game
   //
   short   gametype;       // 0 = modem  1 = network
   //
   // space between packet times
   //
   // 1 = 35   FPS control update speed
   // 2 = 17.5 FPS control update speed
   // 3 = 11.7 FPS control update speed
   // 4 = 8.75 FPS control update speed
   //
   short   ticstep;        // 1 for every tic 2 for every other tic ...
   //
   // whether this station is capable of sending live remote ridicule
   // ticstep is set to 1 if this is enabled.
   // bandwith of comm device must be capable of 256*35 = 8960 bytes per
   // second.
   short   remoteridicule; // 0 = remote ridicule is off 1= rr is on
   // packet data to be sent
   char    data[MAXPACKETSIZE];
   } rottcom_t;
If you have any questions give me a call or drop me a note
Mark Dochtermann
(214) 271 - 1365 ext. 210
paradigm@metronet.com
;
; Sample RTS script file
; 3D Realms Entertainment
;
; The script file format is as follows
;
; The first name is the name of the RTS file.
; The next 10 names are filenames of valid VOC or WAV files.
; sound files have to be 8 or 16 bit mono sounds with no compression.
; sound files can be recorded anywhere from 7 Khz to 44 Khz.
;
; The name of the RTS file
;
SAMPLE.RTS
;
; sound file 1
;
SOUND1.VOC
;
; sound file 2
;
SOUND2.WAV
;
; sound file 3
;
SOUND3.WAV
;
; sound file 4
;
SOUND4.VOC
;
; sound file 5
;
SOUND5.VOC
;
; sound file 6
;
SOUND6.WAV
;
; sound file 7
;
SOUND7.VOC
;
; sound file 8
;
SOUND8.WAV
;
; sound file 9
;
SOUND9.VOC
;
; sound file 10
;
SOUND10.WAV
		    GNU GENERAL PUBLIC LICENSE
		       Version 2, June 1991
 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.
			    Preamble
  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.)  You can apply it to
your programs, too.
  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.
  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
  The precise terms and conditions for copying, distribution and
modification follow.
		    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.
    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.
    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,
    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,
    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.
  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
			    NO WARRANTY
  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
		     END OF TERMS AND CONDITIONS
	    How to Apply These Terms to Your New Programs
  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
    
    Copyright (C)   
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:
  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.
  , 1 April 1989
  Ty ****, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Library General
Public License instead of this License.
Rise of the Triad (v1.3 CD Version) Source Code Release - December 20, 2002.
(If you can't read this right in your editor, turn on word wrap)
Please note that this is being released without any kind of support from Apogee Software, Ltd / 3D Realms Entertainment.  We cannot and will not help in getting this running.  We do not guarantee that you will be able to get it to work, nor do we guarantee that it won't blow up your computer if you do try and use it.  Caveat Emptor - Use at your own risk.  Having said that, this source code release was compiled on December 7, 2002 using the materials in this archive.  Here's a note from one of the original ROTT programmers (Jim Dose) as to what will be needed to get this compiled:
------
You will need Watcom C v10.0b, which is what was used to compile the game originally.  This source code release was tested with that version.  Other versions are not guaranteed to work.  Later versions such as v11.0 are known to most likely not work because of changes in the way data types are handled.
You also need Borland's Turbo Assembler (TASM), but it is not required to build the full game if you use the .obj files that are already included.  To do a full rebuild of the C code without having TASM, delete all the .obj files except for the following files:
F_SCALE.OBJ
RT_DR_A.OBJ
RT_FC_A.OBJ
RT_SC_A.OBJ
RT_VH_A.OBJ
To compile ROTT, type "wmake all".  To compile the audio library, run "wmake.bat".
-----
Please note that while we are releasing the source code to Rise of the Triad, the game itself has not been released in the same manner (in other words, Rise of the Triad is still commercial software).  You can still buy the game from us by visiting ****://www.3drealms.com.
Thanks to all the fans who have hung in there and waited for us to do something like this, we hope you enjoy it.  If you produce something cool with this source code, drop us a line at rott@3drealms.com.   In honor of the source code release, we contacted Tom Hall to reminicse a bit about the game, and we have some history from Scott Miller, President of Apogee. Their thoughts are below.
Furthermore, the release of the Rise of the Triad source code is dedicated to our late friend and cohort, William Scarboro.  William was one of the original Rise of the Triad programmers, and he unfortunately died of an asthma attack on August 9, 2002.
William was born March 2, 1971 in El Paso, TX. He was a graduate of Texas A&M with a degree in Computer Science. He came to work here back in 1993, and was the first programmer we hired back then when we started doing in house development. He was mainly known for his work on Rise of the Triad where he worked on actor code, weapon stuff, and the gibs. In fact, William was directly responsible for the /EKG gib cheat in ROTT.
Joe Siegler
Apogee Software / 3D Realms
Dec 20, 2002
==============================================================================================
RISE OF THE TRIAD: The Source Code Release
A Note from Tom Hall
It was 1992.  I had just worked through half of DOOM, a creative guy in a technologically-oriented company.  We parted ways and I left to start up "In-House Development" at Apogee (later 3D Realms). We started accruing a programmer here, and artist there. Soon we had a team.  I came in with a memo about something (like bonus dough for library functions or something), and at the end I said something like, "And once we complete all these, we will be... THE DEVELOPERS OF INCREDIBLE POWER!" The guys laughed and liked this a lot.  And thus we became the DIPs. :)
With a game called ROTT, and a bunch of DIPs, you're heading for some derision. :) We had a fairly inexperienced crew: me, inexperienced at management; the rest of the guys, fairly new to the industry. It started as WOLFENSTEIN 3D, Part 2 -- a "transition project" my heart wasn't in, really.  It wasn't my idea to do it, but it was something to do. Once we parted ways with that idea and id's involvement, things got rewritten, changed, and we came up with s-Quake functionality crammed into a Wolfenstein Plus/sub-DOOM engine.  It was an example of pushing a technology to do what it really can't do well.  We had a fun engine, but one that looked ugly, especially in the masked platforms -- they were paper thin!  You could have had a fun game without doing what it couldn't do... but we were trying to beat the Joneses. And once we split ways with the id thing, I made three bad decisions: a) to keep the art we had already, b) to not redesign a new game and c) not to move over to the Build engine, where we could have had a Duke/DOOM level game going decently quickly.
Ah well. Hindsight's 20/20.  I am too nice, I guess. Yet, ROTT, for all its tortured technology, all its semi-justified graphics, did have some stuff that there was to be proud of. It was the first game with Rocket Jumping, the first game with Jumppads, the first game with parental password and Violence level adjustment, the first game with Capture the Flag...  Plus, I believe, the record for the most cheat codes ever in a game!  And a Random Level creator!  And what about all those cool Deathmatch options! And speaking of Deathmatch, I recall William's wonderful Corpseyard deathmatch map, and his "totally heinous" insane Drunk Missiles!  Mr. Scarboro, you, and your tuna, egg, salsa, and Omega 3 fatty acid bowls of goo will be missed.  Wah-bgsht! To Mark, Jim, Nolan, Steve, Tim, Chuck, Susan, JoeSke, Big Joe, Marianna, Lee, Bobby, Robert and all, thanks for sharing a strange time with me, and so many crazy memories!  "Uh, guys... um...."  The Disturbathon.  "50 kills!" "Ooooooooooh...  Mmmmmmm..."  Going to that crazy Bazaar to digitize old weapons.  "Check out what I got the boulders to do now."  "***!" "Use the fish." "Bowooooooooo!"  "I'm lookin' for some hot buns..."
And on and on... And thanks to Scott and George, for providing a place for us to be crazy together, and sticking with it even though it wasn't blockbuster stuff. I do owe you a debt in what was an odd, disorienting time for me. In the end, a couple hundred thousand folks seem to have enjoyed it, so there was something there that they liked. "See, Charlie Brown?  It wasn't such a bad tree after all."
======
A little history...
by Scott Miller
ROTT, as it quickly became known, marked a turning point for Apogee.  It was our first in-house game since I started the company in 1987 with my home grown Kroz games, the games that started the shareware revolution that resulted in the launch of three of the most successful independent PC developers, Epic, Id, and us -- all three still kicking after 12+ years (as of Dec. '02).  It's hard to believe we're among the oldest of all surviving independent PC developers in the world now.  Before ROTT, Apogee, as we were then known, solely worked with outside development teams, often funding and helping organize these teams, and helping guide their game designs using our experience.  But around 1993 it was evident that this method wasn't going to work out much longer because as games got larger and more complicated to make, team sizes had to grow, too.  So, in 1994 we started hiring developers to form our own internal team, with the first of those hires including William Scarboro, Nolan Martin, Mark Dochtermann, Jim Dos, and the ever creative Tom Hall to run the show.
As Tom notes above, ROTT was originally an Id-approved sequel to Wolfenstein 3-D, using the original Wolf engine.  The game was going to be called, Wolfenstein: Rise of the Triad, and explore what happened after Hitler's demise.  About 4-5 months into development, though, a surprise call from John Romero ended the project, and we were left with a lot of content specific to the Wolfenstein premise, which had to be rolled into a new game concept so that we didn't waste all that we'd done.  The result was a bit of a mish-mash, and as Tom says above, the project probably should've been restarted using our new Build engine, which our second internal team down the hall was using, making Duke Nukem 3D.
ROTT ended up selling several hundred thousand copies and making enough money to keep Duke 3D funded.  But it was at this time that we knew we  had to make a radical company change to shift with the times, and so we created the 3D Realms label, and began phasing out Apogee, which we were leaving behind with the glory days of shareware, and the arcade-style games Apogee was best known for.
We get a lot of requests to release the source code to many of the older Apogee games, but the problem is that Apogee does not own these games that were developed by external teams.
This source release is a long time coming, and hopefully it's not the last time we're able to do this.
Scott Miller, CEO / Founder
======
/*
Copyright (C) 1994-1995 Apogee Software, Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

File information

File name: rottsource.zip

File size: 3.85 MB

Mime type: text/plain; charset=us-ascii compressed-encoding=application/zip; charset=binary

File name: rottsource.zip

File size: 3.85 MB

Mime type: text/plain; charset=us-ascii compressed-encoding=application/zip; charset=binary