3D Pinball for Windows - Space Cadet (better known as Space Cadet Pinball) is one of the most well-known games from the Windows XP era.
This game was developed by Cinematronics for Windows 95 (and later included in Windows XP). Another game called Full Tilt! Pinball was also developed afterwards, which features Space Cadet as one of the three tables.
This page will only be focusing on the version of Space Cadet Pinball included with Windows XP.
If you've found any errors in the documentation or want to suggest any changes or additions, please contact 겑衐诠芜蟭莼볿骵薖详鳔誷齛꒦諶鳓芗.
Table of Contents
Cheats
Prior to launching the ball out of the deployment chute, players can input any of the codes below to gain their associated effects.
Code
Effect
HIDDEN TEST
Enable cheat mode
GMAX
Activate the gravity well
RMAX
Increase rank
1MAX
Gain an extra ball
BMAX
Never lose a ball
When in cheat mode, the keys below trigger their associated functionality.
Key
Effect
B
Spawn an extra ball if the ball isn't on the table
H
Show the high score dialog and set a new high score
M
Show memory usage
R
Increase rank
Y
Display FPS in the title bar
F1
Progress the game by a single frame when paused
F11
Runs port_draw() for TEdgeManager (dummied out)
F12
Runs port_draw() for all components on the table
F15
Toggles single-step mode
If both Y and H are pressed when in cheat mode, it will trigger a "graphics render history" mode, which draws using a red palette for any changed areas of the screen.
Additionally, there are 2 other codes that are programmed into the game, but their effects and activations have been dummied out of the final game.
These codes are CINEMATRONICS. and QUOTES.. However, these codes are used in pre-release version of the game.
The following pages discuss the various cheats found within the pre-release versions of Space Cadet Pinball.
April 26th, 1995
Below are the codes used in the April 26th build of Space Cadet Pinball.
Code
Effect
TEST
Enable cheat mode
When in cheat mode, the keys below trigger their associated functionality.
Key
Effect
Up
Unknown (increases a counter by 1)
0
Detect whether the center lights are on (on/off in title bar)
1-9
Turn on the center lights
B
Spawn an extra ball if the ball isn't on the table
H
Set graphics render history flag
Y
Display FPS in the title bar
F1
Progress the game by a single frame when in single-step mode
F3
Enable REPLAY MODE
F4
Unknown (decrements counter 0..5000 by 1)
F5
Unknown (decrements counter 0..5000 by 2)
F6
Unknown (decrements counter 0..5000 by 100)
F7
Write to capture.log
F8
Read from capture.log
F11
Runs port_draw() for TEdgeManager (dummied out)
F12
Runs port_draw() for all components on the table
F15
Toggles single-step mode
May 22nd, 1995
Below are the codes used in the May 22nd build of Space Cadet Pinball.
Code
Effect
TEST
Enable cheat mode
When in cheat mode, the keys below trigger their associated functionality.
Key
Effect
Up
Unknown (increases a counter by 1)
0
Detect whether the center lights are on (on/off in title bar)
1-9
Turn on the center lights
B
Spawn an extra ball if the ball isn't on the table
H
Set graphics render history flag
Y
Display FPS in the title bar
F1
Progress the game by a single frame when in single-step mode
F4
Unknown (decrements counter 0..5000 by 1)
F5
Unknown (decrements counter 0..5000 by 2)
F6
Unknown (decrements counter 0..5000 by 100)
F7
Write to capture.log
F8
Read from capture.log
F11
Runs port_draw() for TEdgeManager (dummied out)
F12
Runs port_draw() for all components on the table
F15
Toggles single-step mode
May 26th, 1995
Below are the codes used in the May 26th build of Space Cadet Pinball.
Code
Effect
TEST
Enable cheat mode
When in cheat mode, the keys below trigger their associated functionality.
Key
Effect
Up
Unknown (increases a counter by 1)
B
Spawn an extra ball if the ball isn't on the table
H
Set graphics render history flag
Y
Display FPS in the title bar
F1
Progress the game by a single frame when paused
F11
Runs port_draw() for TEdgeManager (dummied out)
F12
Runs port_draw() for all components on the table
F15
Toggles single-step mode
June 12th, 1995
Below are the codes used in the June 12th build of Space Cadet Pinball.
Code
Effect
TEST
Enable cheat mode
CINEMATRONICS
Display staff list
When in cheat mode, the keys below trigger their associated functionality.
Key
Effect
B
Spawn an extra ball if the ball isn't on the table
H
Show the high score dialog and set a new high score
R
Increase rank
Y
Display FPS in the title bar
F1
Progress the game by a single frame when paused
F11
Runs port_draw() for TEdgeManager (dummied out)
F12
Runs port_draw() for all components on the table
F15
Toggles single-step mode
Staff List
Below is the staff list displayed when entering the CINEMATRONICS code.
Programming by
Michael Sandige
-----
John Taylor
Greg Hospelhorn
-----
David Stafford
-----
Art by
John Frantz
Ryan Medeiros
-----
Design by
Kevin Gliner
-----
Audio by
Matt Ridgeway
Donald S. Griffin
-----
Executive Producer
David Stafford
-----
Producer
Kevin Gliner
-----
Special Thanks
Paula Sandige
-----
Jim Cooper
Jim Mischel
-----
Danny Thorpe
Jamie Risdon
June 16th, 1995
Below are the codes used in the June 16th build of Space Cadet Pinball.
Code
Effect
HIDDEN TEST
Enable cheat mode
CINEMATRONICS
Display staff list
QUOTES
Display quotes
When in cheat mode, the keys below trigger their associated functionality.
Key
Effect
B
Spawn an extra ball if the ball isn't on the table
H
Set graphics render history flag
R
Increase rank
Y
Display FPS in the title bar
F1
Progress the game by a single frame when paused
F11
Runs port_draw() for TEdgeManager (dummied out)
F12
Runs port_draw() for all components on the table
F15
Toggles single-step mode
Staff List
Below is the staff list displayed when entering the CINEMATRONICS code.
Programming by
Michael Sandige
-----
John Taylor
Greg Hospelhorn
-----
David Stafford
-----
Art by
John Frantz
Ryan Medeiros
-----
Design by
Kevin Gliner
-----
Audio by
Matt Ridgeway
Donald S. Griffin
-----
Executive Producer
David Stafford
-----
Producer
Kevin Gliner
-----
Special Thanks
Paula Sandige
-----
Jim Cooper
Jim Mischel
-----
Danny Thorpe
Jamie Risdon
Quotes
Below are the quotes displayed when entering the QUOTES code.
No matter where you go there you are.
-----
Tomorrow already sucks
-----
That worked to good to be right.
June 28th, 1995
Below are the codes used in the June 28th build of Space Cadet Pinball.
Code
Effect
HIDDEN TEST
Enable cheat mode
CINEMATRONICS.
Display staff list
QUOTES.
Display quotes
When in cheat mode, the keys below trigger their associated functionality.
Key
Effect
B
Spawn an extra ball if the ball isn't on the table
H
Show the high score dialog and set a new high score
M
Show memory usage
R
Increase rank
Y
Display FPS in the title bar
F1
Progress the game by a single frame when paused
F11
Runs port_draw() for TEdgeManager (dummied out)
F12
Runs port_draw() for all components on the table
F15
Toggles single-step mode
Staff List
Below is the staff list displayed when entering the CINEMATRONICS. code.
Programming by
Michael Sandige
-----
John Taylor
-----
Greg Hospelhorn
-----
David Stafford
-----
Art by
John Frantz
-----
Ryan Medeiros
-----
Design by
Kevin Gliner
-----
Audio by
Matt Ridgeway
-----
Donald S. Griffin
-----
Executive Producer
David Stafford
-----
Producer
Kevin Gliner
-----
Special Thanks
Paula Sandige
-----
Jim Cooper
-----
Jim Mischel
-----
Danny Thorpe
-----
Jamie Risdon
-----
Brad Silverberg
-----
Jeff Camp
-----
Alex St. John
Quotes
Below are the quotes displayed when entering the QUOTES. code.
Hey, is that a screen saver?
-----
I guess it has been a good week
-----
She may already be a glue bottle
-----
If you don't come in Saturday,
...
-----
don't even bother coming in Sunday.
-----
Tomorrow already sucks
-----
I knew it worked too good to be right.
-----
World's most expensive flippers
July 3rd, 1995
Below are the codes used in the July 3rd build of Space Cadet Pinball.
Code
Effect
HIDDEN TEST
Enable cheat mode
CINEMATRONICS.
Display staff list
QUOTES.
Display quotes
When in cheat mode, the keys below trigger their associated functionality.
Key
Effect
B
Spawn an extra ball if the ball isn't on the table
H
Show the high score dialog and set a new high score
M
Show memory usage
R
Increase rank
Y
Display FPS in the title bar
F1
Progress the game by a single frame when paused
F11
Runs port_draw() for TEdgeManager (dummied out)
F12
Runs port_draw() for all components on the table
F15
Toggles single-step mode
Staff List
Below is the staff list displayed when entering the CINEMATRONICS. code.
Programming by
Michael Sandige
-----
John Taylor
-----
Greg Hospelhorn
-----
David Stafford
-----
Art by
John Frantz
-----
Ryan Medeiros
-----
Design by
Kevin Gliner
-----
Audio by
Matt Ridgeway
-----
Donald S. Griffin
-----
Executive Producer
David Stafford
-----
Producer
Kevin Gliner
-----
Special Thanks
Paula Sandige
-----
Jim Cooper
-----
Jim Mischel
-----
Danny Thorpe
-----
Jamie Risdon
-----
Brad Silverberg
-----
Jeff Camp
-----
Alex St. John
Quotes
Below are the quotes displayed when entering the QUOTES. code.
Hey, is that a screen saver?
-----
I guess it has been a good week
-----
She may already be a glue bottle
-----
If you don't come in Saturday,
...
-----
don't even bother coming in Sunday.
-----
Tomorrow already sucks
-----
I knew it worked too good to be right.
-----
World's most expensive flippers
FONT.DAT
This file goes unused in the final game, but it could have possibly been an early version of the numbers used for the score display or used to display numbers as part of debug functionality.
Actual Font Data
The actual font glyphs used by the final game are stored within the PBMSG_FT resource, which can be found in the pinball.exe executable at offset 0x23FB0.
The data for the font glyphs themselves begins at offset 0x240D1 and ends at offset 0x29476.
The width of the glyph (in pixels) is defined by width, and the height of each glyph is always 22 pixels.
The number of pixels (bytes) that make up the glyph is width * 22.
Unused Font Data
There's an additional unused font that exists in the PINBALL2.MID file at offset 0x86. I'm not sure why it's stored with a MIDI extension, but the file only contains font glyph data.
Each glyph uses the same PinballFontGlyph format above, except the height of each glyph is 23 pixels.
PINBALL.DAT
This file contains a significant portion of what makes the game work, such as sprites, components, collision, physics, and more.
Overview
The file consists of a header, followed by a record table.
The record table is a collection of records, which are just groups of data.
Each record contains one or more fields (properties), which hold the actual data.
Think of it like an Excel spreadsheet:
Record Table: The entire spreadsheet
Record: A row in the spreadsheet
Field: A column within the row
struct DatFile
{
DatHeader header;
Record record_table[];
}
The size (in bytes) of record_table is determined by the table_size header value in the next section.
DAT Header
At the beginning of the file is a 183-byte header with the following format:
struct DatHeader
{
CHAR sig[21];
CHAR game_name[50];
CHAR table_name[100];
INT filesize;
SHORT num_records;
INT table_size;
SHORT extra_size;
}
The value of sig must be PARTOUT(4.0)RESOURCE with a null byte (00) at the end.
game_name is the name of the game (3D-Pinball), and table_name is the name of the current table.
filesize is the total size (in bytes) of the PINBALL.DAT file.
num_records is the number of records in the record table, and table_size is the total size (in bytes) of the record table.
It seems that extra_size may have been used to specify an extra part of the header. This is 0 in the final game, and any additional fields are simply skipped over.
Record Entries
Each record is a group of fields, and each record starts with a 1-byte value determining the number of fields it contains.
struct Record
{
BYTE num_fields;
Field fields[];
}
Take the string of bytes below as an example. (You'll learn all about the field types in the next sections)
02 00 CA 00 09 04 00 00 00 2E 2E 2E 00
Let's see what this translates to step-by-step:
02: This record contains 2 fields.
00: The 1st field is a Type field.
CA 00: This is 202 in little endian format, so this record's type is A_SOUND.
09: The 2nd field is a String field.
04 00 00 00: This next record is 4 bytes long.
2E 2E 2E 00: This is the ASCII string ....
Next let's take a look at the types of fields that can be stored in records.
Field Entries
Each field starts with a 1-byte field type identifier, followed by the field's data.
Every field type except for the Type field is always followed by a 4-byte length that determines the size (in bytes) of the field.
struct Field
{
BYTE type;
INT length;
...
}
There are 13 possible field types that a record can have.
00: Type
01: Image
02: XImage
03: DLabel
04: Construction
05: Palette
06: Color Cycle
07: Color Rotate
08: Integer
09: String
0A: Attributes
0B: Float Attributes
0C: Z Buffer
Each field type has its own rules for how it processes the data that follows, so we'll take a look at each one.
Field Type: Type
This field is always followed by a single 16-bit value that determines the type of the record.
Below are the possible record types.
Hex
Decimal
Type
C8 00
200
AN_OBJECT
C9 00
201
A_STATE
CA 00
202
A_SOUND
2C 01
300
MATERIAL
90 01
400
KICKER
Field Type: Image
This field defines an image (sprite) that will potentially be drawn on screen.
struct Image
{
BYTE resolution;
SHORT width;
SHORT height;
SHORT x;
SHORT y;
INT size;
BYTE type;
BYTE pixels[];
}
resolution is always 0 since this indicates a resolution of 640x480.
width and height are the width and height of the image in pixels.
x and y are the X and Y offsets of the image on the table.
size is the total size of the image in bytes.
type is always 1, indicating a device-independent bitmap.
pixels is an array of palette indices that compose the final bitmap.
Field Type: XImage
This field goes unused in the final game. Its usage remains shrouded in mystery.
Field Type: DLabel
This field identifies the name (or label) of the record.
struct DLabel
{
CHAR name[];
}
The length of the name string is determined by the field's length value.
Field Type: Construction
This field goes unused in the final game. Its usage remains shrouded in mystery.
Field Type: Palette
This field identifies the palette to be used for all Image types. This palette is also referenced by the font glyph data discussed on page 3.
Each entry in the palette is a color in BGRA format. This means the first byte is the blue value, then green, then red, and finally alpha.
The alpha value is unused in the final game, and the color black (00 00 00 00) is used for transparency.
struct Color
{
BYTE b;
BYTE g;
BYTE r;
BYTE a;
}
The palette itself is simply a collection of these color structures.
struct Palette
{
Color colors[];
}
The number of actual color entries in the palette can be determined by dividing the field's length value by 4.
Field Type: Color Cycle
This field goes unused in the final game. Its usage remains shrouded in mystery.
Field Type: Color Rotate
This field goes unused in the final game. Its usage remains shrouded in mystery.
Field Type: Integer
This field goes unused in the final game. Its usage remains shrouded in mystery.
Field Type: String
This field identifies a string to be used for later processing.
If the record Type is A_SOUND, then this is the filename of a sound.
struct String
{
CHAR str[];
}
The length of the str string is determined by the field's length value.
Field Type: Attributes
This field identifies attributes that affect the game in some way.
Each attribute is identified by a 2-byte identifier, followed by the attribute's data. Each attribute's data is a series of 2-byte values.
struct Attribute
{
SHORT type;
...
}
There are various different attribute types, as shown below.
Hex
Decimal
Type
05 00
5
Single-Line Text Box
64 00
100
Number of States
2C 01
300
Material Reference
30 01
304
Soft Hit Sound
90 01
400
Kicker Reference
96 01
406
Hard Hit Sound
58 02
600
Table Size *
5A 02
602
Collision Plane
01 04
1025
Table Object Array
03 04
1027
Image Array
4C 04
1100
Activation Sound
4D 04
1101
Deactivation Sound
DC 05
1500
Multi-Line Text Box
* The Table Size is actually determined by the record with label "table_size", but it's being noted here since it's the sole exception to the rule.
As with each field type, each attribute type has its own rules for how it processes the data that follows.
Single-Line Text Box
This attribute takes 4 2-byte values that determine the offset and size of the text box.
struct SingleLineTextBox
{
SHORT x;
SHORT y;
SHORT width;
SHORT height;
}
The x and y values determine the top-left offset of the text box.
The width and height values determine the size of the text box.
Number of States
This attribute determines the number of states that a component can have.
This also dictates that the next series of records will be states ("children") of the current record.
struct NumberOfStates
{
SHORT num_states;
}
The number of records to set as children of this record are determined by (num_states - 1).
For example, if num_states was 8, then the next 7 records will be child states of the current record.
Material Reference
This attribute determines the record to be used as a "Material", which controls the Elasticity and Smoothness properties.
The Elasticity and Smoothness properties are discussed more in the Float Attributes section.
struct MaterialReference
{
SHORT record_id;
}
The Material to be used is determined by the record_id value.
Soft Hit Sound
This attribute determines which A_SOUND record to be used when the ball rolls over the component.
struct SoftHitSound
{
SHORT record_id;
}
The A_SOUND record to be used is determined by the record_id value.
Kicker Reference
This attribute determines the record to be used as a "Kicker", which controls various properties about the Kicker.
The specific Kicker properties are discussed more in the Float Attributes section.
struct KickerReference
{
SHORT record_id;
}
The Kicker to be used is determined by the record_id value.
Hard Hit Sound
This attribute determines which A_SOUND record to be used when the ball bounces off the component.
struct HardHitSound
{
SHORT record_id;
}
The A_SOUND record to be used is determined by the record_id value.
Table Size
This attribute is a special case, and isn't really identified by the value 600.
Instead, the 600 is actually the table width, and the following 2-byte value is the table height.
struct TableSize
{
SHORT width = 600;
SHORT height;
}
The width and height values determine the total size of the playable area.
These values also determine the window size.
Collision Plane
This attribute determines which collision plane the component resides on.
Plane 2 indicates the topmost plane, e.g. where the ramp bumpers are.
Plane 1 resides between plane 0 (the ground plane) and plane 2, and functions as a "transition" plane of sorts.
struct CollisionPlane
{
SHORT plane;
}
If a component doesn't have a Collision Plane attribute, it resides on plane 0 (the ground plane).
Table Object Array
This attribute determines the components that are shown on the table during gameplay.
As the name implies, this defines an array of records alongside a component type for each record.
Each entry in the array has the following format:
struct TableObject
{
SHORT object_type;
SHORT record_id;
}
The object_type identifies the type of object that the record represents.
The record being associated with the object type is identified by the record_id value.
Below are the possible object types.
Hex
Decimal
Type
E8 03
1000
Table Wall (Polygon)
F2 03
1010
Table Wall (Circle)
E9 03
1001
Plunger
EA 03
1002
Light
EB 03
1003
Left Flipper
EC 03
1004
Right Flipper
ED 03
1005
Bumper
EE 03
1006
Target
EF 03
1007
Drain (Kill Plane)
F3 03
1011
Ball Blocker
F4 03
1012
Kickout (Hyperspace, Black Hole)
F5 03
1013
Gate (Blocks kicker access)
F6 03
1014
Kicker
F7 03
1015
Rollover
F8 03
1016
One-Way
F9 03
1017
Wormhole (Sink)
FA 03
1018
Flag
FB 03
1019
Target (Button)
FC 03
1020
Space Warp Rollover
FD 03
1021
Ramp
FE 03
1022
Ramp Hole
FF 03
1023
Demo
00 04
1024
Deployment Light (Skill Shot)
02 04
1026
Light Group
04 04
1028
Bumper Group
05 04
1029
Gravity Well
06 04
1030
Fuel Lights
07 04
1031
Sound
08 04
1032
Timer
09 04
1033
Text Box
Below is the structure of the actual Table Object Array.
The number of TableObject entries can be calculated by subtracting 2 from the field length, then dividing by 4.
Image Array
This attribute determines a group of images, with the record typically also having an associated DLabel field.
Each entry in the array is a record ID. Each target record must have an Image field.
struct ImageArray
{
SHORT record_ids[];
}
The number of record ID entries can be calculated by subtracting 2 from the field length, then dividing by 2.
Activation Sound
This attribute determines which A_SOUND record to be used when the component is activated.
struct ActivationSound
{
SHORT record_id;
}
The A_SOUND record to be used is determined by the record_id value.
Deactivation Sound
This attribute determines which A_SOUND record to be used when the component is deactivated.
struct DeactivationSound
{
SHORT record_id;
}
The A_SOUND record to be used is determined by the record_id value.
Multi-Line Text Box
This attribute takes 4 2-byte values that determine the offset and size of the text box.
struct MultiLineTextBox
{
SHORT x;
SHORT y;
SHORT width;
SHORT height;
}
The x and y values determine the top-left offset of the text box.
The width and height values determine the size of the text box.
Field Type: Float Attributes
Like the Attributes field, this field also identifies attributes that affect the game in some way.
The main difference is that these values are all float values, i.e. numbers like 3.14 instead of 3.
Each float attribute is identified by a 4-byte identifier, followed by the attribute's data. Each float attribute's data is a series of 4-byte values.
struct FloatAttribute
{
FLOAT type;
...
}
There are various different float attribute types, as shown below.
Hex
Decimal
Type
00 00 80 3F
1.0
Camera Matrix
00 80 96 43
301.0
Material: Smoothness
00 00 97 43
302.0
Material: Elasticity
00 80 98 43
305.0
Gravity
00 00 99 43
306.0
Activation Radius
00 80 C8 43
401.0
Kicker: Threshold
00 00 C9 43
402.0
Kicker: Boost
00 80 C9 43
403.0
Kicker: Eject Force
00 00 CA 43
404.0
Kicker: Eject Direction
00 80 CA 43
405.0
Kicker: Eject Angle
00 00 CB 43
406.0
Kicker: Hard Hit Sound
00 80 CB 43
407.0
Kicker: Deactivation Timer
00 00 CC 43
408.0
Kicker: Ball Position
00 00 FA 43
500.0
Ball: Collision Z Offset
00 80 FA 43
501.0
Ball: Sprite Distance
00 00 16 44
600.0
Collision
00 40 16 44
601.0
Ball: Spawn Position
00 C0 16 44
603.0
Rollover Collision
00 00 2F 44
700.0
Table Center
00 40 2F 44
701.0
Gravity Multiplier
00 00 48 44
800.0
Flipper: Base Position
00 40 48 44
801.0
Flipper: Tip Position (Idle)
00 80 48 44
802.0
Flipper: Tip Position (Active)
00 C0 48 44
803.0
Flipper: Force
00 00 49 44
804.0
Flipper: Activate Speed
00 40 49 44
805.0
Flipper: Deactivate Speed
00 00 61 44
900.0
Light On Duration
00 40 61 44
901.0
Light Off Duration
00 C0 61 44
903.0
Light Group Timer
00 00 62 44
904.0
Fuel Graph Light Timers
00 00 96 44
1200.0
Maximum Flag Speed
00 20 96 44
1201.0
Minimum Flag Speed
00 40 96 44
1202.0
Flag Friction
00 80 A2 44
1300.0
Ramp: Incline
00 A0 A2 44
1301.0
Ramp: Enter Line
00 C0 A2 44
1302.0
Ramp: Transition Line
00 E0 A2 44
1303.0
Ramp: Plane Transition
00 00 A3 44
1304.0
Ramp: Plane Change Flag
00 20 A3 44
1305.0
Ramp: Z Offset Flag
00 00 AF 44
1400.0
Demo: Left Flipper Trigger Line
00 20 AF 44
1401.0
Demo: Left Flipper Trigger Raycast
00 40 AF 44
1402.0
Demo: Right Flipper Trigger Line
00 60 AF 44
1403.0
Demo: Right Flipper Trigger Raycast
00 80 AF 44
1404.0
Demo: Plunger Trigger Line
* The Camera Matrix is actually determined by the record with label "camera_info", but it's being noted here since it's the sole exception to the rule.
As with each field type, each attribute type has its own rules for how it processes the data that follows.
Camera Matrix
This float attribute consists of 15 different float values that define the camera's 3D projection matrix.
The camera matrix is used for calculating the positioning of collision, the ball's current depth, and a few other things.
Like the Table Size attribute, it isn't really identified by the value 1.0.
Instead, the 1.0 is actually the first part of the structure, followed by 14 additional float values.