Difference between revisions of "Resource file format"
(New page: Stunts game data are stored in a common container format. Files of this format can hold multiple resources of any type, identified only by a 4-byte name. A resource file can be encapsulate...) |
(Update stressed repository link) |
||
(28 intermediate revisions by 6 users not shown) | |||
Line 1: | Line 1: | ||
− | Stunts game data are stored in a common container format. Files of this format can hold multiple resources of | + | Stunts game data are stored in a common container format. Files of this format can hold multiple ''resources'', blocks of data that may be of various types - text, bitmaps, sounds, etc. Resources are uniquely identified within each file solely by a 4-byte name. A resource container file can be encapsulated by [[Compression|compression]]. |
This document focuses on [[Game versions#PC versions|BB Stunts 1.1]], but details described here may still—partly or wholly—cover other versions, or even other games developed by DSI at the time. | This document focuses on [[Game versions#PC versions|BB Stunts 1.1]], but details described here may still—partly or wholly—cover other versions, or even other games developed by DSI at the time. | ||
Line 6: | Line 6: | ||
Different file name extensions are used to indicate the content of the files. Compressed files has ''"P"'' (packed) as the first letter of their extensions. Whether the game prefers raw or compressed files varies based on file type. | Different file name extensions are used to indicate the content of the files. Compressed files has ''"P"'' (packed) as the first letter of their extensions. Whether the game prefers raw or compressed files varies based on file type. | ||
− | {| | + | {| class="wikitable" |
− | + | ! File contents !! Raw !! Compressed !! Preferred | |
− | ! | ||
− | ! | ||
− | ! | ||
− | ! | ||
|- | |- | ||
− | | Text and misc. settings | + | | Text and misc. settings || RES || PRE || Raw |
− | | RES | ||
− | | PRE | ||
− | | Raw | ||
|- | |- | ||
− | | Bitmap images | + | | Bitmap images || VSH || PVS || Compressed |
− | | VSH | ||
− | | PVS | ||
− | | Compressed | ||
|- | |- | ||
− | | Icons | + | | Icons || ESH || PES || Compressed |
− | | ESH | ||
− | | PES | ||
− | | Compressed | ||
|- | |- | ||
− | | 3d shapes | + | | 3d shapes || 3SH || P3S || Compressed |
− | | 3SH | ||
− | | P3S | ||
− | | Compressed | ||
|- | |- | ||
− | | Music tracks | + | | Music tracks || KMS || PKM || Raw |
− | | KMS | ||
− | | PKM | ||
− | | Raw | ||
|- | |- | ||
− | | Instrument samples | + | | Instrument samples || VCE || PVC || Raw |
− | | VCE | ||
− | | PVC | ||
− | | Raw | ||
|- | |- | ||
− | | Sound effects | + | | Sound effects || SFX || PSF || Raw |
− | | SFX | ||
− | | PSF | ||
− | | Raw | ||
|} | |} | ||
Line 53: | Line 28: | ||
The resource file header consists of two integer fields denoting the total length of the file and the number of resources contained. The following table of contents has a list of ids and a corresponding list of offsets into the remaining data section. | The resource file header consists of two integer fields denoting the total length of the file and the number of resources contained. The following table of contents has a list of ids and a corresponding list of offsets into the remaining data section. | ||
− | < | + | <span class="hlC">// Header</span> |
− | // Header | + | <span class="hlP">uint32</span> fileLength |
− | uint32 fileLength | + | <span class="hlP">uint16</span> numResources |
− | uint16 numResources | + | |
− | + | <span class="hlC">// Table of contents</span> | |
− | // Table of contents | + | <span class="hlP">char</span> ids<span class="hlB">[</span>numResources<span class="hlB">][</span><span class="hlN">4</span><span class="hlB">]</span> |
− | char ids[numResources][4] | + | <span class="hlP">uint32</span> offsets<span class="hlB">[</span>numResources<span class="hlB">]</span> |
− | uint32 offsets[numResources] | + | |
− | + | <span class="hlC">// Resource data</span> | |
− | // Resource data | + | <span class="hlP">char</span> data<span class="hlB">[]</span> |
− | char data[] | ||
− | </ | ||
==Types== | ==Types== | ||
Line 70: | Line 43: | ||
===Plain text=== | ===Plain text=== | ||
− | Text resources are null-terminated [http://en.wikipedia.org/wiki/C_string C strings] | + | Text resources are null-terminated [http://en.wikipedia.org/wiki/C_string C strings] found in <tt>RES</tt>/<tt>PRE</tt> files. Strings can contain non-alphanumeric codes used by the game to achieve certain effects. Most prominent is the ''"]"'' (right square bracket) used to represent [http://en.wikipedia.org/wiki/Newline newline] in multi-line text. |
===Bitmap images=== | ===Bitmap images=== | ||
Line 76: | Line 49: | ||
Bitmaps are images with 8-bit color depth using a [[:Image:Stunts-pal-vga.png|fixed palette]] in VGA mode. Special bitmaps using the naming scheme <tt>!cg_</tt> and <tt>!eg_</tt> provides color mapping for graphics modes with fewer available colors. | Bitmaps are images with 8-bit color depth using a [[:Image:Stunts-pal-vga.png|fixed palette]] in VGA mode. Special bitmaps using the naming scheme <tt>!cg_</tt> and <tt>!eg_</tt> provides color mapping for graphics modes with fewer available colors. | ||
− | < | + | <span class="hlP">uint16</span> width |
− | uint16 width | + | <span class="hlP">uint16</span> height |
− | uint16 height | + | <span class="hlP">uint16</span> centreX |
− | uint16 | + | <span class="hlP">uint16</span> centreY |
− | uint8 | + | <span class="hlP">uint16</span> positionX |
+ | <span class="hlP">uint16</span> positionY | ||
+ | <span class="hlP">uint8</span> unknown<span class="hlB">[</span><span class="hlN">4</span><span class="hlB">]</span> | ||
+ | |||
+ | <span class="hlP">uint8</span> image<span class="hlB">[</span>width <span class="hlB">*</span> height<span class="hlB">]</span> | ||
− | + | Width and height correspond, of course, to the image dimensions. CentreX and centreY refer to the coordinates of the axial centre for moving images, as is the case of the gear knob and the steering dot. These coordinates set the point in the image that will move along the path established for it to go along. | |
− | |||
− | Parts of this structure are not fully understood. <tt> | + | Parts of this structure are not fully understood. <tt>unknown</tt> seems to affect how image pixels are organized in compressed resource files, likely to gain more effective [http://en.wikipedia.org/wiki/Run-length_encoding run-length compression]. Images with pixel data stored as continuous horizontal lines has <tt>unknown</tt> set to <code>{ 0x1, 0x2, 0x4, 0x8 }</code> which is probabily [http://en.wikipedia.org/wiki/Planar_(computer_graphics) Planar] data for 16 color modes. Modifying these values randomly for all images in a car does not appear to have any effect, which suggest this might have importance only in non-car PVS/VSH files. |
+ | |||
+ | Due to the internal resolution of Stunts (which uses [http://en.wikipedia.org/wiki/Mode_13h VGA Mode 13h]), non-square pixels are used for screen display. [[Aspect ratio]] corrections should be accounted for to avoid unwanted results. | ||
+ | |||
+ | The VGA palette itself is encoded as the <tt>!pal</tt> bitmap resource of <tt>SDMAIN.PVS</tt>. The pixel data of that resource is interpreted as 256 triplets of [https://moddingwiki.shikadi.net/wiki/VGA_Palette 6-bit RGB]. | ||
+ | |||
+ | See [[Car files]] for specific information on the different image resources. | ||
{{sectstub}} | {{sectstub}} | ||
Line 93: | Line 75: | ||
===3d shapes=== | ===3d shapes=== | ||
+ | Shapes are composed of a vertex list and a list of primitives. Primitives are basic shapes that can have different types, such as polygons or wheels. Depending on the type it contains a number of indices into the vertex list needed to draw the shape, color variations and some rendering hints. Stunts' coordinate system use 16-bit signed integers, making the resolution relative to the scale of the model. Shapes presented in the car selection screen are more detailed than in-game shapes, thus requiring larger scaling. Experiment has shown that 1 foot in-game corresponds to 5 units, making each track tile roughly 205 feet (or 62.5 meters) wide. | ||
+ | |||
+ | Due to the counter header fields being stored in single bytes, the total amount of vertices and primitives are limited to 255 per shape. | ||
+ | |||
+ | '''Main strcture''' | ||
+ | <span class="hlP">uint8</span> numVertices | ||
+ | <span class="hlP">uint8</span> numPrimitives | ||
+ | <span class="hlP">uint8</span> numPaintJobs | ||
+ | <span class="hlP">uint8</span> reserved <span class="hlC">// Always == 0</span> | ||
+ | |||
+ | VERTEX vertices<span class="hlB">[</span>numVertices<span class="hlB">]</span> | ||
+ | <span class="hlP">uint32</span> cullFront<span class="hlB">[</span>numPrimitives<span class="hlB">]</span> | ||
+ | <span class="hlP">uint32</span> cullBack<span class="hlB">[</span>numPrimitives<span class="hlB">]</span> | ||
+ | PRIMITIVE primitives<span class="hlB">[</span>numPrimitives<span class="hlB">]</span> | ||
+ | |||
+ | <span class="hlP">uint8</span> terminate<span class="hlB">[</span><span class="hlC">...</span><span class="hlB">]</span> <span class="hlC">// 1-3 NULL-bytes for data alignment</span> | ||
+ | |||
+ | '''Additional structures''' | ||
+ | <span class="hlP">struct</span> VERTEX <span class="hlB">{</span> | ||
+ | <span class="hlP">int16</span> x | ||
+ | <span class="hlP">int16</span> y | ||
+ | <span class="hlP">int16</span> z | ||
+ | <span class="hlB">}</span> | ||
+ | |||
+ | <span class="hlP">struct</span> PRIMITIVE <span class="hlB">{</span> | ||
+ | <span class="hlP">uint8</span> type | ||
+ | <span class="hlP">uint8</span> flags | ||
+ | <span class="hlP">uint8</span> materials<span class="hlB">[</span>numPaintJobs<span class="hlB">]</span> | ||
+ | <span class="hlP">uint8</span> indices<span class="hlB">[</span><span class="hlC">...</span><span class="hlB">]</span> <span class="hlC">// Size depends on type.</span> | ||
+ | <span class="hlB">}</span> | ||
+ | |||
+ | '''Primitive types''' | ||
+ | {| class="wikitable" | ||
+ | ! Primitive type !! Vertex indices needed !! Description | ||
+ | |- | ||
+ | | 1 || 1 || Particle, 1 pixel | ||
+ | |- | ||
+ | | 2 || 2 || Line segment, 1 pixel width | ||
+ | |- | ||
+ | | 3–10 || 3–10 || Polygon, ''n'' sides | ||
+ | |- | ||
+ | | 11 || 2 || Sphere (center + ~(radius / 2) | ||
+ | |- | ||
+ | | 12 || 6 || Wheel | ||
+ | |- | ||
+ | | * || 0 || Ignored | ||
+ | |} | ||
+ | |||
+ | '''Primitive flags''' | ||
+ | {| class="wikitable" | ||
+ | ! Flag !! Name | ||
+ | |- | ||
+ | | <tt>0x01</tt> || Two-sided | ||
+ | |- | ||
+ | | <tt>0x02</tt> || Z-Bias | ||
+ | |- | ||
+ | | * || Ignored | ||
+ | |} | ||
+ | |||
+ | '''Culling data masks''' | ||
+ | {| class="wikitable" | ||
+ | ! Flag !! Description | ||
+ | |- | ||
+ | | <tt>0xFFFE0000</tt> || Horizontal angle ranges, positive direction | ||
+ | |- | ||
+ | | <tt>0x0001FFFC</tt> || Horizontal angle ranges, negative direction | ||
+ | |- | ||
+ | | <tt>0x00000001</tt> || High vertical angle flag, positive direction | ||
+ | |- | ||
+ | | <tt>0x00000002</tt> || High vertical angle flag, negative direction | ||
+ | |} | ||
+ | |||
+ | For car body and track element shapes the first eight vertices are reserved for the corner vertices of the shape's bound box, used to cull entire shapes located outside the view frustum. | ||
+ | |||
+ | [[Image:Wheel_Primitive_Anatomy.png|200px|right|thumb|Anatomy of a wheel shape primitive.]] | ||
+ | The first three vertices in a wheel primitive marks the center, tire radius and rim radius for the inner wheel-half facing the vehicle. The last three vertices does the same, in the same order, for the outer half of the wheel. Directions may not matter, the vertex locations are used to measure distances. This rule applies to the sphere primitive as well. | ||
+ | |||
+ | Wheel transformations are performed on fixed vertex positions. Since the first eight vertices of <tt>car[0-2]</tt> shapes are occupied by the bound box, vertices 9-14 and 15-20 are front wheels. Misplaced wheel vertices will lead to corrupted shape rendering. | ||
+ | |||
+ | The <tt>flags</tt> in the <tt>PRIMITIVE</tt> structure are used for two-sided polygons (disable [http://en.wikipedia.org/wiki/Back-face_culling back-face culling]) and to enable Z-bias in order to resolve [http://en.wikipedia.org/wiki/Z-fighting Z-fighting]. | ||
+ | |||
+ | [[Shape materials|Materials]] are indices into a permanent, internal game structure and are assigned per-primitive. Cars can have multiple paint jobs, every primitive in the shape must set a material for each color scheme. | ||
+ | |||
+ | Each primitive has two 32-bit culling data fields to determine visibility. A culling data field holds angular visibility in positive and negative direction using 15 bits each. One bit corresponds to 1/15th of a full circle and the bits can be set in any combination. The two least significant bits are used as flags for visibility at near-vertical viewing angles. | ||
+ | |||
+ | A shape resource must be terminated with NULL-bytes. The number of bytes needed is not known, but padding three NULL-bytes has proven to work regardless of resource size or byte alignment. Failing to meet this requirement will lead to obscure rendering artifacts. | ||
+ | |||
+ | As for bitmaps, [[aspect ratio]] issues in Stunts should be considering when setting Y coordinates of 3D models. | ||
+ | |||
{{sectstub}} | {{sectstub}} | ||
===Car parameters=== | ===Car parameters=== | ||
{{main|Car parameters}} | {{main|Car parameters}} | ||
+ | Numerical car*.res parameters are stored in a 776 bytes sized resource identified as ''simd''. Most data in simd is stored as signed 16-bit integers. Noteworthy exceptions include number of gears, gear ratios, torque curve data and bitmap coordinates for dashboard instruments - which are unsigned 8-bit integers. Organization and function of the parameters are outside the scope of this article; the reader should consult the main article on the topic for further information. | ||
{{sectstub}} | {{sectstub}} | ||
===Opponent parameters=== | ===Opponent parameters=== | ||
+ | {{main|Opponent files}} | ||
+ | The opp?.res files contain numerical data resources which control opponent behaviour and victory/defeat animations. The contents and function of these resources are better understood within the context of the main article. | ||
{{sectstub}} | {{sectstub}} | ||
Line 112: | Line 186: | ||
==Tools== | ==Tools== | ||
− | * [[stressed]] - Stunts/4D [Sports] Driving resource editor [ | + | * [[stressed]] - Stunts/4D [Sports] Driving resource editor [https://github.com/dstien/gameformats/]. |
− | [[Category: | + | [[Category:Modding]] |
+ | [[Category:Internals]] |
Latest revision as of 02:22, 9 March 2023
Stunts game data are stored in a common container format. Files of this format can hold multiple resources, blocks of data that may be of various types - text, bitmaps, sounds, etc. Resources are uniquely identified within each file solely by a 4-byte name. A resource container file can be encapsulated by compression.
This document focuses on BB Stunts 1.1, but details described here may still—partly or wholly—cover other versions, or even other games developed by DSI at the time.
File names
Different file name extensions are used to indicate the content of the files. Compressed files has "P" (packed) as the first letter of their extensions. Whether the game prefers raw or compressed files varies based on file type.
File contents | Raw | Compressed | Preferred |
---|---|---|---|
Text and misc. settings | RES | PRE | Raw |
Bitmap images | VSH | PVS | Compressed |
Icons | ESH | PES | Compressed |
3d shapes | 3SH | P3S | Compressed |
Music tracks | KMS | PKM | Raw |
Instrument samples | VCE | PVC | Raw |
Sound effects | SFX | PSF | Raw |
Structure
The resource file header consists of two integer fields denoting the total length of the file and the number of resources contained. The following table of contents has a list of ids and a corresponding list of offsets into the remaining data section.
// Header uint32 fileLength uint16 numResources // Table of contents char ids[numResources][4] uint32 offsets[numResources] // Resource data char data[]
Types
Resource type can be determined by looking at the resource's id string and/or the source file name.
Plain text
Text resources are null-terminated C strings found in RES/PRE files. Strings can contain non-alphanumeric codes used by the game to achieve certain effects. Most prominent is the "]" (right square bracket) used to represent newline in multi-line text.
Bitmap images
Bitmaps are images with 8-bit color depth using a fixed palette in VGA mode. Special bitmaps using the naming scheme !cg_ and !eg_ provides color mapping for graphics modes with fewer available colors.
uint16 width uint16 height uint16 centreX uint16 centreY uint16 positionX uint16 positionY uint8 unknown[4] uint8 image[width * height]
Width and height correspond, of course, to the image dimensions. CentreX and centreY refer to the coordinates of the axial centre for moving images, as is the case of the gear knob and the steering dot. These coordinates set the point in the image that will move along the path established for it to go along.
Parts of this structure are not fully understood. unknown seems to affect how image pixels are organized in compressed resource files, likely to gain more effective run-length compression. Images with pixel data stored as continuous horizontal lines has unknown set to { 0x1, 0x2, 0x4, 0x8 }
which is probabily Planar data for 16 color modes. Modifying these values randomly for all images in a car does not appear to have any effect, which suggest this might have importance only in non-car PVS/VSH files.
Due to the internal resolution of Stunts (which uses VGA Mode 13h), non-square pixels are used for screen display. Aspect ratio corrections should be accounted for to avoid unwanted results.
The VGA palette itself is encoded as the !pal bitmap resource of SDMAIN.PVS. The pixel data of that resource is interpreted as 256 triplets of 6-bit RGB.
See Car files for specific information on the different image resources.
Icons
3d shapes
Shapes are composed of a vertex list and a list of primitives. Primitives are basic shapes that can have different types, such as polygons or wheels. Depending on the type it contains a number of indices into the vertex list needed to draw the shape, color variations and some rendering hints. Stunts' coordinate system use 16-bit signed integers, making the resolution relative to the scale of the model. Shapes presented in the car selection screen are more detailed than in-game shapes, thus requiring larger scaling. Experiment has shown that 1 foot in-game corresponds to 5 units, making each track tile roughly 205 feet (or 62.5 meters) wide.
Due to the counter header fields being stored in single bytes, the total amount of vertices and primitives are limited to 255 per shape.
Main strcture
uint8 numVertices uint8 numPrimitives uint8 numPaintJobs uint8 reserved // Always == 0 VERTEX vertices[numVertices] uint32 cullFront[numPrimitives] uint32 cullBack[numPrimitives] PRIMITIVE primitives[numPrimitives] uint8 terminate[...] // 1-3 NULL-bytes for data alignment
Additional structures
struct VERTEX { int16 x int16 y int16 z } struct PRIMITIVE { uint8 type uint8 flags uint8 materials[numPaintJobs] uint8 indices[...] // Size depends on type. }
Primitive types
Primitive type | Vertex indices needed | Description |
---|---|---|
1 | 1 | Particle, 1 pixel |
2 | 2 | Line segment, 1 pixel width |
3–10 | 3–10 | Polygon, n sides |
11 | 2 | Sphere (center + ~(radius / 2) |
12 | 6 | Wheel |
* | 0 | Ignored |
Primitive flags
Flag | Name |
---|---|
0x01 | Two-sided |
0x02 | Z-Bias |
* | Ignored |
Culling data masks
Flag | Description |
---|---|
0xFFFE0000 | Horizontal angle ranges, positive direction |
0x0001FFFC | Horizontal angle ranges, negative direction |
0x00000001 | High vertical angle flag, positive direction |
0x00000002 | High vertical angle flag, negative direction |
For car body and track element shapes the first eight vertices are reserved for the corner vertices of the shape's bound box, used to cull entire shapes located outside the view frustum.
The first three vertices in a wheel primitive marks the center, tire radius and rim radius for the inner wheel-half facing the vehicle. The last three vertices does the same, in the same order, for the outer half of the wheel. Directions may not matter, the vertex locations are used to measure distances. This rule applies to the sphere primitive as well.
Wheel transformations are performed on fixed vertex positions. Since the first eight vertices of car[0-2] shapes are occupied by the bound box, vertices 9-14 and 15-20 are front wheels. Misplaced wheel vertices will lead to corrupted shape rendering.
The flags in the PRIMITIVE structure are used for two-sided polygons (disable back-face culling) and to enable Z-bias in order to resolve Z-fighting.
Materials are indices into a permanent, internal game structure and are assigned per-primitive. Cars can have multiple paint jobs, every primitive in the shape must set a material for each color scheme.
Each primitive has two 32-bit culling data fields to determine visibility. A culling data field holds angular visibility in positive and negative direction using 15 bits each. One bit corresponds to 1/15th of a full circle and the bits can be set in any combination. The two least significant bits are used as flags for visibility at near-vertical viewing angles.
A shape resource must be terminated with NULL-bytes. The number of bytes needed is not known, but padding three NULL-bytes has proven to work regardless of resource size or byte alignment. Failing to meet this requirement will lead to obscure rendering artifacts.
As for bitmaps, aspect ratio issues in Stunts should be considering when setting Y coordinates of 3D models.
Car parameters
Numerical car*.res parameters are stored in a 776 bytes sized resource identified as simd. Most data in simd is stored as signed 16-bit integers. Noteworthy exceptions include number of gears, gear ratios, torque curve data and bitmap coordinates for dashboard instruments - which are unsigned 8-bit integers. Organization and function of the parameters are outside the scope of this article; the reader should consult the main article on the topic for further information.
Opponent parameters
The opp?.res files contain numerical data resources which control opponent behaviour and victory/defeat animations. The contents and function of these resources are better understood within the context of the main article.