******************** DESCRIPT: HEXEN Script Decompiler *********************
V1.40 (c) R.A.Coward 1996
DESCRIPT is a fully fledged and user-friendly HEXEN script decompiler, that
generates highly readable source code from any compiled script. This may
take the form of a self contained object file, or the BEHAVIOR resource
in a main or patch WAD file.
Being able to decompile the existing scripts inside HEXEN is a real boon
for anyone wishing to design their own levels for the game, as it allows
them to learn the ACS language and experiment freely, without having to
do a lot of work up front. Since the source files that DESCRIPT produces
can be readily recompiled, it's very easy to try out modifications to the
existing levels, and find out what works, and what doesn't.
To use DESCRIPT to decompile an object file, or the first BEHAVIOR resource
in a WAD, type:
DESCRIPT <objectfile> <sourcefile>
For example, to decompile the first script in HEXEN.WAD, into a source
file called HEXLEV1.ACS, type:
DESCRIPT HEXEN.WAD HEXLEV1.ACS
If you have a multi-level WAD, such as HEXEN.WAD, you can extract any
script into a source file, by directly specifying the map number on the
end of the line:
DESCRIPT <wadfile> <sourcefile> <map>
For example, to decompile level MAP08 in HEXEN.WAD, into file HEXLEV8.ACS,
DESCRIPT HEXEN.WAD HEXLEV8.ACS 8
You can find out what levels are present in a WAD file by typing:
DESCRIPT <wadfile> /l
DESCRIPT contains on-line help, accessed by just typing its name with no
parameters, or by specifying the /? or ? switch. Additional debugging info
can be printed out by adding the /v switch, which includes a list of all
the strings and scripts in the file.
The source files generated by DESCRIPT are designed to be highly readable:
not only are all string parameters decoded, but also any variables passed
as string parameters are automatically shown as type "str" (unless they are
script arguments), and any strings they are assigned to are also shown.
Most of the enumerated types (in header file defs.acs) are also fully
decoded, again with variable assignments correctly shown. Even the return
values from functions such as gametype() and gameskill() are decoded, when
used directly in conditional/switch statements.
The net result is that the generated script files are probably fairly close
in appearence to those that Raven used when making the game. However, like
all compiled languages, some information in the source is not translated
into the object file, with the result that it is permanently lost. For ACS,
this applies to:
1) Variable names. DESCRIPT now names variables according to their usage,
so for example, a variable used as a "thing ID" will be called "tid". This
makes the code even more readable, especially where parameters are being
passed into scripts. The full list of decoded types are:
tid - thing ID on map
tag - sector tag on map
line - line identification
poly - polyobject identification
thing - thing type (assignments will be decoded)
str - all strings (assignments will be decoded)
spare - the variable is not used anywhere
var - default type, if not decoded
Whenever DESCRIPT names a variable, it prefixes it with "world" for
world-scope variables, "map" for map scope variables, "a" for arguments
and nothing for local variables. In all cases, it adds a suffix number,
which is the number of the variable in the object code. Note that all map
and script scope variables are listed in ascending numerical order, even
if not all are used. For example, if only the 4th map variable is actually
used, the first three will be displayed as "mapspare0" thru "mapspare2".
If this was not done, the recompiled script would differ from the original.
2) Variables declared but not used. Unless a script or map variable is
numerically below one that is used, or forms a script parameter, it will
not show up on the decompiled source. This is because only variable usage
is compiled; variable declarations are not.
3) Strings assigned to a variable but not used. This case is slightly
different as the string WILL be compiled into the object code, but DESCRIPT
will not reproduce it because the variable is never passed to anything
that it recognises as taking a string parameter. This is because strings
are stored as HANDLES, with a numerical parameter listing an index
of strings stored at the end of the script. The only difference between a
string and a number is where it is used.
3) User defined macros. If the user originally used his own macros (using
the #define directive), these will not be reproduced, as they are not
present in the object code.
4) Comments and spacing. These are not compiled, so do not show up.
DESCRIPT now has the capability to display extended definitions for even
greater readability, but because these are non-standard, you also need
an extended definitions file. This can be generated with the /g option,
and has a fixed name, DSEDEFS.ACS (DeScript Extended DEFinitionS). Then,
when decompiling scripts, use the /u option to use extended definitions.
The extended definitions currently provided are:
Line Specials - The setlinespecial() function takes in a special number
as its second parameter, which is the same number as the compiled code uses
when calling one of these functions directly. Since DESCRIPT already
decompiles these names, it is a simple matter to decode them in this context,
too. All the names are prefixed by "LS_", to distinguish them from the
actual special function names. Note that there are a few, usch as
UsePuzzleItem, which are not listed in SPECIALS.ACS, as they are not
suitable for being called directly from within scripts (or it is pointless
to do so).
Sector Sounds - The special Sector_ChangeSound takes in a parameter defining
the sound to be selected, as listed in the technical specs. DESCRIPT will
decode this, prefixing it with type "SS_".
Keys and Locks - Two specials, Door_LockedRaise and ACS_LockedExecute take in
a parameter specifying the key number that the player must hold for the
action to take place. These are listed in the technical specs, and prefixed
by type "KEY_".
Puzzle Items - The first parameter of UsePuzzleItem() is decoded to a sensible
name, as listed in the HEXEN technical specs. However, since this special
is unlikely to crop up, because it is not in SPECIALS.ACS, you may think
that it is pointless decoding this parameter. Actually, this is quite
useful, as DESCRIPT also decodes the parms when passed to setlinespecial().
If DESCRIPT finds a setlinespecial() function, with a numerical parameter
for the special type, it decodes all subsequent numerical parameters as
if they were being passed to the special function directly. This extends
readability still further, especially as this is the only place that some
specials, such as UsePuzzleItem(), are used.
Note that you only need to generate the DSEDEFS.ACS file once, as it will
be available to all subsequent decompilations. You can generate it at the
same time as doing a decompilation, or on its own, and you don't have to
specify the /u option at the same time. The generation of this file takes
place before DESCRIPT opens any other files, so it will not affect operation
in any other way (though it will slow down execution slightly). Note that
you should check that the version of DESCRIPT you are using matches that
displayed in the DSEDEFS.ACS file, and re-generate it if it does not.
If you want a disassembly of the object module, rather than a decompilation,
use the /a switch. Thus, to get an assembly listing of MAP30 in HEXEN.WAD,
into file HEXLEV30.DIS, type:
DESCRIPT HEXEN.WAD HEXLEV30.DIS 30 /a
If you don't like the nesting being shown with the brackets at the inner
nesting level, you can use the /n option, where the brackets will be shown
at the outer nesting level.
You can prevent DESCRIPT from displaying context sensitive variable names
with the /c option, and from decoding variable assignments to enumerated
types with the /e option. If you don't want assignments to text strings
being displayed, use the /s option. All these are useful if you find that
false information is being displayed due to the same variables being used for
different purposes in the script.
1) DESCRIPT is only guaranteed to work on code produced using the ACC
compiler by Ben Gokey of Raven Software. It relies on the compiler following
a fixed set of rules, both in the compiled code and the layout of the ACS
resource (it expects all the scripts to be sequentially arranged, followed
by the strings, and finally the internal directory). Many changes which
would still result in correctly executing code will be fatal for DESCRIPT,
as its rules would be violated. This is not a design deficiency in DESCRIPT;
several high level statements can only be reproduced because the low
level opcodes appear in an exact sequence.
2) DESCRIPT relies on good programming practices having being used in the
original scripts. For example, if a variable is used as both a string and an
integer, this may result in DESCRIPT displaying false strings, and could
result in the code recompiling differently (if the string indexing changes).
The enumerated types and context-sensitive variable naming could also cause
a problem if the same variable is used for different purposes in different
parts of the script, though the code will always recompile the same. If you
find this a problem, you can disable variable assignments to enumerated
types with the /e option, and context-sensitive variable names with the /c
option. If strings assignments are being falsely displayed, you can turn
them off with the /s option, though beware that this will seriously affect
how the code will recompile. Note that use of the /s or /e switches will
also disable the relevant context-sensitive variable names, as they stop
variables from being assigned the appropriate types.
3) There appears to be a minor "feature" in the ACC compiler. If you create
two nested "do" loops without brackets, and put a "continue" statement in the
inner statement, the compiler will actually make this jump to the outer
loop. This doesn't result in corrupted source (in fact DESCRIPT fixes it),
but the decompiled source will compile differently to the original.
4) In a few rare cases, two different source code fragments will compile to
identical object code, with the result that DESCRIPT will choose one of the
source representations. In all the cases I've found so far, the two source
code fragments mean the same thing. A classic example is if you bury an if()
statement inside a "do" loop, and put a "continue" statement inside the if(),
with nothing following it. DESCRIPT will generate an "else" statement instead
of the "continue", but they mean the same thing in this context. Comparing
the two object modules show the code is identical.
5) Like all software (except the truly trivial), DESCRIPT cannot be
guaranteed to be bug free, but it has been rigorously tested. I've thrown
all sorts of wierd and wonderful constructs at it, many of them meaningless
such as switch() statements with expressions or "default" statements before
the first "case". I've also tried many combinations of nested loops and
conditionals, and different combinations of pre- and post- increment/decrement
operators (which are especially difficult to decompile). For what it's
worth, I've also decompiled all the original HEXEN scripts, and checked
that they recompiled into object modules which were identical to the
6) If you attempt to decompile a level that does not exist in the WAD,
DESCRIPT will report an error. However, if you specify the /o option, you
can specify an "order number" which is the ORDER of the level in the WAD,
and DESCRIPT will always decompile a level, if it can find one. If the
order number is greater than the number of levels in the file, the last
level will be decompiled. Also note that when just listing levels, DESCRIPT
does NOT look for BEHAVIOR resources - it just looks for level names. Thus
you can list out levels in DOOM files without a problem, but you will get
an error as soon as you attempt to decompile a script.
7) Descript can simply dump its output to the console (screen), though this
cannot be piped or redirected. For object files, or the first level in a WAD,
simply miss out the output (source) file name. To decompile a higher WAD
level, simply call the output file "con", and you will get the same result.
8) The ACC compiler appears to get upset at the unary minus operator cropping
up in expressions containing a multiplication or division. Thus the expression
"var0 = var1 * -5" would not compile, but "var0 = var1 * (-5)" would. Thus
DESCRIPT will put brackets around unary operators in this situation. The
modulus (%) operator is also handled in the same manner.
9) ACC does not like variable type "str" as a script argument (probably
because this is a stupid idea, anyway). Therefore, DESCRIPT will always
display argumants as type "int", but if an argument is used as a string,
its NAME will be "astr".
id and Raven for HEXEN (and of course DOOM/DOOM2/HERETIC).
Ben Gokey and Raven for the ACC compiler (especially the -d option!).
Ben Morris for the Official Hexen Technical Specs.
Antony Burden for the excellent HETH and DETH editors.
V1.00 - Pre-release evaluation version.
V1.01 - First release (minor cosmetic changes).
V1.10 - Massive performance boost (fixed bug in file random access reads).
Fixed pre/post increment/decrement bug in switch() statements.
Now fully decodes enumerated types in switch() statements.
Accepts direct MAPxy numbers, rather than step nums (use /o for step).
New /n option for alt nesting display (preferred by some people).
Returned enumerated types work both sides of '!=' and '==' operators.
Returned enumerated types also work with '<', '>', '<=' and '>='.
File header now informs you of extracted WAD level.
Will now copy to console if you don't specify output file.
V1.20 - Bugfix to over-enthusiastic bracket removal with unary operators.
V1.30 - Advanced context-sensitive variable names for commonly used types.
New extended definitions with option to generate definitions file.
Advanced decoding of special parms passed into setlinespecial().
Command switch /s changed to /o, so that new /s option available.
New /e, /s and /c switches to disable variable assignments/naming.
Arguments distinguished from local vars by "a" prefix.
Prevent string arguments from displaying with type "str".
If map num not specified, now scans WAD for first level, not MAP01.
V1.40 - Minor cosmetic changes to help text and error messages.
Bugfix to extremely subtle problem with post inc/dec operators.
Minor tweaks to Floor_Waggle and Light_ForceLightning parm types.
The aim of this program is to allow you to examine the internal structure
of the HEXEN script resources, in the same way as a level editor allows
you to view other resources graphically. It does NOT mean that you are
allowed to copy these scripts and pass them off as your own work, just like
you are not allowed to copy the maps out of the original game.
I cannot be held responsible for any loss or injury which may result from
the use of this program. In particular, be careful not to name the source
file parameter to any valuable file you already have - it WILL be overwritten!
This program is FREEWARE. You can do absolutely anything you like with it,
as long as you distribute it with this file intact, and do not pass it off
as your own work.
If you have any comments or bug reports on this program, then feel free to
contact me at: