There are many things currently not known about the PSDL file format. To encourage further investigation I publish my own findings here.
The acronym PSDL means Processed Scene Description Language and the format describes the terrain of the city blocks in a city of Midtown Madness II.
Here follows a pseudo C-style declaration of the PSDL file format:
Note: "float" is an IEEE single precision floating point value, "char", "short" and "long" are 8, 16 and 32 bit integers respectively. All integers have LSB first.
struct PSDL
{
char[4] fileIdentifier; // = "PSD0" (0x50534430)
long targetSize; // = 0x2 Unknown.
long nVertices; // Number of vertices in the list.
Vertex[nVertices] vertices; // List of vertices.
long nFloats; // Number of floats in the list.
float[nFloats] unknown; // List of floats of unkown function.
long nTextureNames; // Number of texture names + 1.
TextureName[nTextureNames - 1] textureNames; // List of texture names.
long nBlocks; // Number of city blocks + 1.
long unknown;
Block[nBlocks - 1] blocks; // List of city blocks.
char[rest of file] unknown;
}
Where:
struct Vector
{
float x;
float y;
float z;
}
struct TextureName
{
char length; // Length of name.
char[length] name; // File name without .tex extension.
char terminator; // String terminator = 0x00.
}
struct Block
{
long nVertices; // Number of points in the
// perimeter polygon.
long attributeLength; // Number of shorts for block
// attributes.
PerimeterVertex[nVertices] perimeter; // List of vertex references
// with connecting city block.
short[attributeLength] blockAttributes; // See below.
}
struct PerimeterVertex
{
short vertex; // Index in vertex list.
short connectingBlock; // Index + 1 of a city block connecting this block
// at this vertex.
}
Each city block has a number of block attributes, these are defined after the perimeter definition. Most of these attributes are unknown, for some the length is known and for a few the complete function is clear.
This attribute assigns a material to a city block. The texture name that is referenced here is in mapped to a material in the file city/materials.csv. And the materials themselves are defined in city/materials.mtl, here parameters as elasticity, friction, etc. are given to each material. (Make a pinball game by adjusting elasticity?)
struct BlockAttribute_50
{
short id; // 0x0050
short index; // Texture index + 1
}
Road section attributes are used to build roads. Most of these attributes seems to use a similar approach to defining a road. They use cross-sections to define where the sidewalk starts and ends. Such a cross-section is defined by:
struct RoadSection
{
short outer1; // The outermost coordinate of the road crossection.
// The sidewalk starts here.
short inner1; // The sidewalk stops here and the road begins.
short inner2; // The road ends here and the sidewalk on the other side
// begins.
short outer2; // The outermost coordinate of the road crossection.
// The second sidewalk ends here.
}
To be perfectly honest, I have not tested any of these yet. This is just what I guess from the use of them in city.psdl. Also I don't know the difference between them yet.
Attribute 0x80 can be used to create road sections. The data associated with this attribute is organized in groups of four vertex references. The four vertices define a single cross section of the road. At least two of these cross sections are needed to define a road segment.
struct BlockAttribute_0080
{
short id; // 0x0080
RoadSection[2 * n] crossection;
}
Attribute 0x82 can be used to create road sections. The data associated with this attribute is organized in groups of four vertex references. The four vertices define a single cross section of the road. At least two of these cross sections are needed to define a road segment.
struct BlockAttribute_0082
{
short id; // 0x0082
RoadSection[2 * n] crossection;
}
Attribute 0x84 can be used to create road sections. The data associated with this attribute is organized in groups of four vertex references. The four vertices define a single cross section of the road. At least two of these cross sections are needed to define a road segment.
struct BlockAttribute_0084
{
short id; // 0x0084
RoadSection[2 * n] crossection;
}
Attribute 0x85 can be used to create road sections. The data associated with this attribute is organized in groups of four vertex references. The four vertices define a single cross section of the road. At least two of these cross sections are needed to define a road segment.
struct BlockAttribute_0085
{
short id; // 0x0085
RoadSection[2 * n] crossection;
}
Triangle fan geometry definition. This attribute defines a clockwise triangle fan rendered within this city block. The first vertex in the list is the pivot point of the fan. To guarantee that there is at least one complete triangle, the last two vertices are defined after the list. Ususally these two vertices should be the last vertex and the pivot.
struct BlockAttribute_b0
{
short id; // 0x00b0
short nVertices; // Number of vertices
short[nVertices] vertices; // Indices in the vertex list defining the fan.
short lastVertex;// Index of the last vertex
short pivot; // Index of the pivot of the fan - same as
// the first vertex in "vertices".
}
Unknown
struct BlockAttribute_000b
{
short id; // 0x000b
short[6] unknown;
}
Unknown
struct BlockAttribute_0024
{
short id; // 0x0024
short[4] unknown;
}
Unknown
struct BlockAttribute_002a
{
short id; // 0x002a
short[4] unknown;
}
Unknown
struct BlockAttribute_0031
{
short id; // 0x0031
short[3] unknown;
}
Unknown
struct BlockAttribute_008c
{
short id; // 0x008c
short[8] unknown;
}
Check the project history page for a time line of events regarding my research on the PSDL file format and development of the PSDL Viewer.