home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Doom I/II Collection
/
DM12.ISO
/
edit
/
dhtk100
/
dfe.txt
< prev
next >
Wrap
Text File
|
1994-05-26
|
28KB
|
783 lines
*****************************************************************************
* The DOOM Hacker's Tool Kit v1.00 *
*****************************************************************************
* Author: Joshua Jackson internet: joshjackson@delphi.com *
* Release Date: 5/25/94 *
* Language: Borland Pascal 7.0 (Mostly Object Oriented) *
* Destination Platform: DOS real mode -OR- DPMI DOS protected mode *
* *
* The DOOM Hacker's Tool Kit is a product of Jackson Software *
*****************************************************************************
Welcome fellow DOOM hacker!
I have released this source code in hopes that someone out there with a
bit more brains than I have can put it to good use. I am also making
a request that anyone willing to trade source code and ideas, please
email me at the above address. I am mostly interested in 3D rendering
code, but am looking for anything that is not contained in this tool kit,
or improves upon it.
I am also considering setting up a DOOM Hacker's BBS. If you are
interested in such a BBS, please let me know so. It will be setup on
a File Point system with no ratio required for Long Distance callers.
I would also accept UUENCODED source over the internet, which would
then be placed onto the BBS.
NOTE: This BBS would be for programs that include the SOURCE CODE only!
I will also have a section for technical FAQs and the like...
similiar to DMSPEC13.TXT
All Languages would be accepted.
This is a rather extensive set of utilities that I hacked up to access
just about everything in the WAD file. Resource utilities include:
Sprite Viewer
Map Viewer (With object recognition/sprite viewing)
Floor/Ceiling Texture Viewer
Wall Texture Viewier
Sound Player
Resource Extractor
Object Caching
In addition, I have also included the source code for the DOOM Front End
v1.31. The majority of the code here is written in Borland Pascal 7.0 and
is mostly geared toward OOP (Object Oriented Programming). There is also some
assembly language thrown into it here and there.
Comments, Suggestions, Bashing, and Questions are more than welcome!
(Please, don't write just to tell me that the code is not fully cleaned up!
I do realize that there are declared variables that aren't used and things
of that nature. This is because I wanted to get this program into your
hands as soon as possible, and that this code, even though its version 1.0,
has done some massive evolving since I first began construction of it. It
was originally written in staight pascal, no objects that is, and was VERY
pathetic! This tool kit has probably under gone several dozen revisions
since I begin the project about a month ago.)
Contacting me:
Internet: joshjackson@delphi.com
Snail Mail: Joshua Jackson
10506 Bayard Rd.
Minerva, OH 44657
Phone: (216) 868-1169
!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*
DISCLAIMER:
The author will not accept any resposibility for damage resulting from the
use of these programs, whether it be hardware, software, or mental.
COPYING/USE of this code:
All of the code in the DOOM Hacker's tool kit is placed in the public domain
with the exception of DFE. The DFE source has been released simply for
educational purposes only, I would appriciate if you would not modify and
release and versions of this program.
!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*
NOTE: If you plan to compile this tool kit under DPMI, you should use the
BGI256.BGI or BGI256.OBJ (for linking in the graphics driver). I
obtained these drivers off of the official Borland BBS. Most public
domain BGI drivers will cause a general protection fault under DPMI,
BGI256 is designed to be compatible.
Well, enough of that, lets get onto the part you probably are interested in
reading.
The following are descriptions of all of the Objects/Routines that are
contained in the WAD Hacker's Toolkit.
------------------------------------------------------------------------------
Unit: WADDECL.PAS
Purpose: Declarations Unit
There is not much to this unit, it is simply the type declarations for
many of the different structured variables in the tool kit... If you can't
understand this unit, you may want to stop here! :)
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: WAD.PAS
Purpose: Accessing a WAD file's directory
This unit is the center of the tool kit. It contains an object that
loads a given WAD file's directory into memory. Almost all of the other units
rely on this unit for their access into the WAD files.
The object contained in this unit (TWadDirectory) MUST be initialized before
any other units can be initialized.
The following is a listing of the TWadDirectory object declaration:
type PWadDirectory=^TWadDirectory;
TWadDirectory=object
WadName :array[1..9] of char;
WadFile :file;
WadID :array[1..4] of char;
DirEntries :longint;
DirStart :longint;
DirEntry :PWADDirList;
Constructor Init(WadFileName:String);
Procedure DisplayWadDir;
Function FindObject(ObjName:ObjNameStr):word;
Procedure SetWadPalette(PlayPalNum:integer);
Procedure RestorePalette;
Destructor Done;
end;
When calling the init constructor, the only needed parameter is the path and
name of the WAD file that you wish to load. The following is an example
program that will load a WAD file directory:
uses Wad;
var WDir:PWadDirectory;
begin
{Initialize WAD file directory for DOOM.WAD}
WDir:=New(PWadDirectory, Init('C:\DOOM\DOOM.WAD'));
{Your code goes here}
{Dispose of the WAD file directory}
Dispose(WDir, Done);
end.
You will also notice that I included the call to the object's done destructor
when disposing of the object. This is mandatory unless you want a ton of
garbage left on the heap.
The other methods besides init and done are use mainly by the other units in
the tool kit. However, if you would like you implement them in your own
programs here is the syntax for them:
Function FindObject(ObjName:ObjNameStr):word
Returns the position in the directory array of the given object's name.
NOTE: you may use use the WildCard value of '?' for any or all letters in
the name if you want to do a "find first that matches" type search.
Procedure DisplayWadDir
displays the given WAD file's directory to the screen
Procedure SetWadPalette(PlayPalNum:integer);
Sets the current graphics palette to one of the 17 palettes available in
the WAD file. The main play palette is 0.
Procedure RestorePalette
Restores the current graphics palette to the original palette before the
first call to SetWadPalette
Finally, the variable fields in this object are defined as follows:
WadName :array[1..9] of char;
The ASCIIZ name of the WAD file that this object contains the directory to.
(NOTE: the .WAD extension is not included)
WadFile :file;
This file identifier used when accessing the WAD file. This field should
NEVER be altered.
WadID :array[1..4] of char;
Indicated if the loaded file is a 'PWAD' or an 'IWAD'
DirEntries :longint;
The number of directory entires contained in the WAD file.
DirStart :longint;
The file offset of the begining of the directory structure.
DirEntry :PWADDirList;
The directory entries themselves. The number of entries will vary
depending on the size of the WAD file.
NOTE: The number of entries in this array is always equal to DirEntries.
The structure for PWADDirList is contained in the WADDECL unit and has the
following structure:
PWADDirList=^TWADDirList;
TWADDirList=array[1..MaxEntries] of TWADDirEntry;
PWADDirEntry=^TWADDirEntry;
TWADDirEntry=record
ObjStart :longint;
ObjLength:longint;
ObjName :array[1..8] of char;
end;
Another NOTE: I used a little trickery to make the size of the directory
array variable. Therefor, it is possible to address entries outside of
the directory array itself. Be careful not to do this, especially when
in DPMI (You will more than likely cause a segment overrun error)
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: OBJCACHE.PAS
Purpose: WAD Object Cache
This unit is use to optimize the speed at which object are loaded from the
WAD file. I had originally written routines for loading sprites and the like
based on the code contained in DEU. Their code works good, but is slow as
a dog! When using the code based on DEU, I was able to load around one sprite
per second of so. When using the Object Cache, the speed of loading sprites
increased to around 10-15 sprites/sec!
The following is a listing of the TObjectCache declaration:
type PObjectCache=^TObjectCache;
TObjectCache=Object
Constructor Init(WDir:PWadDirectory;ObjNum:word);
Procedure SetPos(NewPos:longint);
Function CurPos:Longint;
Procedure IncPos(IncVal:longint);
Procedure CacheRead(var Dest;Count:word);
Function Size:Longint;
Destructor Done;
private
NumLumps:byte;
Lump:array[1..MaxLumps] of PCacheLump;
CachePos:longint;
LumpPos:word;
CurLump:byte;
end;
Basically, the object cache is simply a way to allocate more that 64k for
loading an object into memory.
When initializing the TObjectCache object, a WAD file directory (see WAD.PAS)
and an object number are needed. The object number is obtained through a call
to TWadDirectory.FindObject.
Once you have initialized the object cache, you may read blocks from it using
calls to CacheRead. CacheRead requires a viariable destination and the number
of bytes that you wish to transfer. After the read, the cache pointer will
move to the end of the transfered area (kind of like a file pointer).
You may move the cache pointer around in the cache by making calls to IncPos
and SetPos. IncPos moves the pointer IncVal bytes from the current pointer,
where SetPos moves the pointer relative to the beginning of the cache.
CurPos returns the current value of the cache pointer.
Size returns the number of bytes allocated for the cache.
Sample Code:
uses Wad,ObjCache;
var WDir:PWadDirectory;
OC:PObjectCache;
begin
{Initialize WAD file directory}
WDir:=New(PWadDirectory, Init('C:\DOOM\DOOM.WAD'));
{Initialize Object Cache for Object TROOA1 (an imp)}
OC:=New(PObjectCache, Init(WDir, WDir^.FindObject('TROOA1 ')));
{Your Code Goes Here}
{Clean Up the Heap}
Dispose(OC, Done);
Dispose(WDir, Done);
end.
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: FLOORS.PAS
Purpose: Loading and Displaying Floor Texures
Cached: no
This unit will load and display a floor texture out of the wad file.
The following is a listing of the TFloorTexture object declaration:
type PFloorTexture=^TFloorTexture;
TFloorTexture=object
Constructor Init(WDir:PWadDirectory;FloorName:ObjNameStr);
Procedure Draw(Scale,XOffset,YOffset:word);
Destructor Done;
private
FBuff:PFloorBuff;
end;
You may notice that this is one of the least complex objects in the tool kit.
To Initialize the object, you will need an initialized WAD file directory
and the texture name for the floor.
Once initialized, you may call Draw to display the texture on any part of the
screen at any scale. The scale factor used is percentage based (100 = 1:1).
NOTE: this unit requires you to initialize the GRAPH unit in Borland or Turbo
Pascal. I had orginally written the screen output routines to directly write
to screen memory, however, I switched over to the graph unit's PutPixel, that
way I could maintain compatability with SVGA screen modes. If you would like
to use this routine in a High Speed application, feel free to remove the
graph unit oriented code.
Sample Code:
uses Graph,Wad,Floors;
var WDir:PWadDirectory;
FT:PFloorTexture;
gd,gm:integer;
begin
{Initalize WAD directory}
WDir:=New(PWadDirectory, Init('C:\DOOM\DOOM.WAD'));
{Initalize Floor Texture}
FT:=New(PFloorTexture, Init(WDir, 'NUKAGE1 '));
gd:=InstallUserDriver('BGI256',Nil);
{640x480x256}
gm:=2;
{Initialize Graphics}
InitGraph(gd,gm,'');
{Set the Play Palette}
WDir^.SetWadPalette(0);
{Draw the texture 2:1 scale at coords 5,5}
FT^.Draw(200,5,5);
{Wait for the ENTER key}
readln;
{Restore the palette}
WDir^.RestorePalette;
{Shut down graphics}
CloseGraph;
{Clean up the heap}
Dispose(FT, Done);
Dispose(WDir, Done);
end.
--------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: THINGS.PAS
Purpose: Loading and Displaying Sprites
Cached: Yes
This unit will load and display a sprite out of the wad file. You can also
use this routine to load and display the title pictures and fonts.
The following is a listing of the TWadThing declaration:
Type PWadThing=^TWadThing;
TWadThing=object
SBuff:PPictureBuff;
Constructor Init(WDir:PWadDirectory;ThingName:ObjNameStr);
Procedure Draw(Scale,XOffset,YOffset:word);
Function Height:word;
Function Width:word;
Destructor Done;
end;
To Initialize the object, you will need an initialized WAD file directory
and the thing name for the sprite/picture.
Once initialized, you may call Draw to display the thing on any part of the
screen at any scale. The scale factor used is percentage based (100 = 1:1).
NOTE: this unit requires you to initialize the GRAPH unit in Borland or Turbo
Pascal. I had orginally written the screen output routines to directly write
to screen memory, however, I switched over to the graph unit's PutPixel, that
way I could maintain compatability with SVGA screen modes. If you would like
to use this routine in a High Speed application, feel free to remove the
graph unit oriented code.
Sample Code:
uses Graph,Wad,Things;
var WDir:PWadDirectory;
T:PWadThing;
gd,gm:integer;
begin
{Initalize WAD directory}
WDir:=New(PWadDirectory, Init('C:\DOOM\DOOM.WAD'));
{Initalize WadThing (an imp)}
T:=New(PWadThing, Init(WDir, 'TROOA1 '));
gd:=InstallUserDriver('BGI256',Nil);
{640x480x256}
gm:=2;
{Initialize Graphics}
InitGraph(gd,gm,'');
{Set the Play Palette}
WDir^.SetWadPalette(0);
{Draw the thing 2:1 scale at coords 5,5}
T^.Draw(200,5,5);
{Wait for the ENTER key}
readln;
{Restore the palette}
WDir^.RestorePalette;
{Shut down graphics}
CloseGraph;
{Clean up the heap}
Dispose(T, Done);
Dispose(WDir, Done);
end.
I think the the Height and Width functions should be pretty much self
explanatory!
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: WALLS.PAS
Purpose: loading and displaying Wall Textures
Cached: Yes
This unit will load and display a Wall texture out of the wad file. This is
the only unit in this tool kit that I am still not real happy with. It still
requires anywhere from 2-4 seconds to load complex textures. The code that
I have written has the living daylights cached out of it, but I still can't
get it to fly. If you have any ideas, let me know.
NOTE: It may look as though I am going through a lot of unecessary crap in
this routine, but it's not. The reason that I dispose of the texture
cache and reload it is to avoid a "gap" in the heap. The routine
might be a touch faster if I didn't do this, but the memory managment
would REALLY hoot!
The following is a listing of the TWallPatch object declaration:
type PWallTexture=^TWallTexture;
TWallTexture=object
Name :objnamestr;
Patches :word;
Image :^BA;
Width :word;
Height :word;
Constructor Init(WDir:PWadDirectory;TextName:ObjNameStr);
Procedure Draw(Scale,XOfs,YOfs:integer);
Destructor Done;
end;
To Initialize the object, you will need an initialized WAD file directory
and the texture name for the Wall.
Once initialized, you may call Draw to display the texture on any part of the
screen at any scale. The scale factor used is percentage based (100 = 1:1).
NOTE: this unit requires you to initialize the GRAPH unit in Borland or Turbo
Pascal. I had orginally written the screen output routines to directly write
to screen memory, however, I switched over to the graph unit's PutPixel, that
way I could maintain compatability with SVGA screen modes. If you would like
to use this routine in a High Speed application, feel free to remove the
graph unit oriented code.
Sample Code:
uses Graph,Wad,Walls;
var WDir:PWadDirectory;
WT:PWallTexture;
gd,gm:integer;
begin
{Initalize WAD directory}
WDir:=New(PWadDirectory, Init('C:\DOOM\DOOM.WAD'));
{Initalize Floor Texture}
WT:=New(PWallTexture, Init(WDir, 'AASTINKY'));
gd:=InstallUserDriver('BGI256',Nil);
{640x480x256}
gm:=2;
{Initialize Graphics}
InitGraph(gd,gm,'');
{Set the Play Palette}
WDir^.SetWadPalette(0);
{Draw the texture 2:1 scale at coords 5,5}
WT^.Draw(200,5,5);
{Wait for the ENTER key}
readln;
{Restore the palette}
WDir^.RestorePalette;
{Shut down graphics}
CloseGraph;
{Clean up the heap}
Dispose(WT, Done);
Dispose(WDir, Done);
end.
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: SOUNDS.PAS
Purpose: Declarations Unit
Cached: Yes
Type PSoundDef=^TSoundDef;
TSoundDef=object
Constructor Init(WDir:PWadDirectory;SndName:ObjNameStr);
Procedure PlaySound;
Funciton IsComplete:boolean;
Procedure EndSound;
Destructor Done;
Private
SoundBuff:PSoundBuff;
IsPlaying:Boolean;
sbBuff:word;
end;
This units will play any sound (NOT SONG) from the WAD file. The sounds
unit will work under protected mode!! This may not sound all that impressive
to those of you who have never tried to access the DMA whe in protected mode,
but its a little bit tricky!
When the conditional symbol DPMI in Borland Pascal is defined (automatic when
you compile for a Protected Mode Destination Platform) the unit automatically
includes the unit DPMI. DPMI has routines for allocating memory in low, DOS
memory. This makes DMA transfer a whole lot simpler.
To intialize a sound, you will need an open Wad File Directory and the Sound
name. Next, a call to PlaySound will play the sound and a call to EndSound
terminates it. The Boolean function IsComplete will indicate true when the
sound blaster card signals that it has completed the DMA transfer.
Sample Code:
uses Wad,Sounds;
var WDir:PWadDirectory;
S:PSoundDef;
begin
{Initialize the WAD directory}
WDir:=New(PWadDirectory, Init('C:\DOOM\DOOM.WAD'));
{Intialize the Sound Player}
S:=New(PSoundDef, Init(WDir, 'DSSLOP '));
{Play the sound}
S^.PlaySound;
{Wait for completion}
while not S^.IsComplete do;
{Terminate Sound}
S^.EndSound;
{Clean Up Heap}
Dispose(S, Done);
Dispose(WDir, Done);
end.
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: DOOMGUI.PAS, DOOMGUI.PAS, DOOMOBJ.PAS
Purpose: Map Viewer Graphical User Interface Units
This unit is used by MAPREAD (the map viewing unit for DFE)
Since this unit is so far from complete and optimized, I will not go into
a detailed description of it.
At this point, the only relevant Object in the DOOMGUI unit is the
TGraphGroup object (a descendant of the TGraphView object). The acutal
map viewing object TMapViewer is of type TGraphGroup and the Map routines
are of type TGraphView.
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: MAPS.PAS
Purpose: Map Loading/drawing routines
This object is by far the most complex of all the objects. It loads and
displays a map and any offset and scale. It also places selected objects
at their locations. The map viewer is currently designed to display the
map to a 640x480 screen. Even though you can display it in any screen mode,
the scaling parameters will be off.
type PWadMap=^TWadMap;
TWadMap=Object(TGraphView)
LevelEntries:PLevelEntries;
ThingList :PThingList;
VertexList :PVertexList;
LineDefList :PLineDefList;
ViewerMask :word;
ThingMask :word;
ScaleInc,XOffset,YOffset:word;
Constructor Init(WDir:PWadDirectory;LevelName:ObjNameStr);
Procedure Draw; virtual;
Procedure SetScale(NewScaleInc,NewXOffset,NewYOffset:word);
Function GetThingInArea(x1,y1,x2,y2:word):word;
Destructor Done; virtual;
end;
To initialize this object you will need an initialized WAD directory and the
level name (eg E1M1).
Once the Map is initialized, you should set the ViewerMask and ThingMask
variables to indicate which objects you want the map viewer to diaplay. The
format for these masks are as follows:
Viewer Mask Bits:
15-8 7 6 5 4 3 2 1 0
| | | | | | | | |
| | | | | | | | `--> Display Monsters
| | | | | | | `----> Display Goodies (Bonuses & collectables)
| | | | | | `------> Display Weapons
| | | | | `--------> Display Objects on Skills 1&2 \
| | | | `----------> Display Objects on Skill 3 > Can be set together
| | | `------------> Display Objects on Skills 4&5 /
| | `--------------> Display Objects Only Avail in Multi Player Games
| `----------------> Display Secret Linedefs as White
`---------------------> Unused at this time
Thing Mask Word:
This word contains a thing ID number to display on map. The thing ID #'s
are defined by DOOM. See the file DMSPEC13.TXT for a listing of these
numbers. When the Thing Mask is set, it will only display Objects that
have the same thing ID # as the thing mask.
The next step is to set the scale and offset factors with a call to SetScale.
the ScaleInc value is ratio based (1 = 1:1, 2= 2:2, etc). The scale factor
is actually based on a full screen view, scale factor 1 is a full screen map.
The offset factors are only useful when you have "ZOOMED IN" on the map, they
allow you to move the map around on the screen.
NOTE: See MAPREAD.PAS for a full example of creating a MapViewer.
Now we are ready to draw the map. Simply call draw.
NOTE: this unit requires you to initialize the GRAPH unit in Borland or Turbo
Pascal. I had orginally written the screen output routines to directly write
to screen memory, however, I switched over to the graph unit's PutPixel, that
way I could maintain compatability with SVGA screen modes. If you would like
to use this routine in a High Speed application, feel free to remove the
graph unit oriented code.
Sample Code:
uses Graph,Wad,Maps;
var WDir:PWadDirectory;
WMap:PWadMap;
gd,gm:integer;
begin
{Initalize WAD directory}
WDir:=New(PWadDirectory, Init('C:\DOOM\DOOM.WAD'));
{Initalize the map}
WMap:=New(PWadMap, Init(WDir, 'E2M7'));
gd:=InstallUserDriver('BGI256',Nil);
{640x480x256}
gm:=2;
{Initialize Graphics}
InitGraph(gd,gm,'');
{Set the Play Palette}
WDir^.SetWadPalette(0);
{Viewer Mask = Monsters + Skill Level 3}
WMap^.ViewerMask:=9;
{Thing Mask = 0 (Display all)}
WMap^.ThingMask:=0;
{Set Scale factor 1 (Full Screen) and 0 the offsets}
WMap^.SetScale(1,0,0);
{Draw the Map);
WMap^.Draw(200,5,5);
{Wait for the ENTER key}
readln;
{Restore the palette}
WDir^.RestorePalette;
{Shut down graphics}
CloseGraph;
{Clean up the heap}
Dispose(WMap, Done);
Dispose(WDir, Done);
end.
The Method GetThingInArea can be used to determine if a given area of a 640x
480 screen contains an object. If the given are does contian an object, it
will return the Sprite ID number.
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: MAPREAD.PAS
Purpose: Map Viewer unit for DFE
This unit is part of DFE, therefor it is not documented.
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: SPRITEVI.PAS
Purpose: Sprite Viewer unit for DFE
This unit is part of DFE, therefor it is not documented.
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: SWAP.PAS
Purpose: Memory Swapping Routines
This unit is part of DFE, therefor it is not documented.
Note: This is a hacked routine from a source listing found in Turbo Pascal
Internals by Michael Tischer
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: SWAPA.ASM
Purpose: Assembly part of Swapping Routines
This unit is part of DFE, therefor it is not documented.
Note: This is a hacked routine from a source listing found in Turbo Pascal
Internals by Michael Tischer
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: EMS.PAS
Purpose: EMS Accessing Part of SWAP
This unit is part of DFE, therefor it is not documented.
Note: This is a hacked routine from a source listing found in Turbo Pascal
Internals by Michael Tischer
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Unit: SWAPA.ASM
Purpose: Assembly part of Swapping Routines
This unit is part of DFE, therefor it is not documented.
Note: This is a hacked routine from a source listing found in Turbo Pascal
Internals by Michael Tischer
------------------------------------------------------------------------------
------------------------------------------------------------------------------
Program: DFE.PAS
Purpose: DOOM Front End Main Program
This is the Pascal Source Code for the DOOM Front End (written in mostly
Turbo Vision).
If you compile DFE, please follow the following steps:
1. Select the Options Menu from Borland/Turbo Pascal
2. Select the Compiler Option
3. Enter the letters DFE in the Conditional Defines area
4. Select the Compile Menu
5. Select Build
6. It should now be ready to go!
------------------------------------------------------------------------------