This is an alternate version of OPL2’s wiki page.
Overview
Binary specifications are written as C-like 010 Editor Templates. Most data types should be self-explanatory.
Text specifications are written as ABNF + RFC 7405.
| File | Type | Purpose |
|---|---|---|
| attribute | Text | Add-on definition |
| *.BIN | Binary | Font |
| *.OGG | Binary | Audio |
| *.PL2 | Binary | Archive |
| *.PSD | Binary | Image |
| *.TCM | Text | Camera animation |
| *.TMB | Binary | 3D model |
| *.TSB | Binary | 3D animation |
| *.TSM | Text | 3D model |
| *.TSS | Text | 3D animation |
| *.TXT | Text | Script |
BIN
Monochrome font image.
//--- 010 Binary Template: PL2_FONTBIN.bt
// Authors: Crazycatz00
// Version: 1.0
// Category: Game
// File Mask: font.bin
// ID Bytes: 46 4F 4E 54
RequiresVersion(3);
LittleEndian();
typedef struct {
char signature[4]; // "FONT"
int32 glyph_count;
int32 glyph_size;
BYTE padding0C__[4] <hidden=true>;
struct {
uint16 codepoint; // Shift-JIS
ubyte data[glyph_size * glyph_size];
} glyph[glyph_count] <optimize=true>;
} FONT;
FONT bin <open=true>;
OGG
Lossy audio in the FOSS Xiph.Org Ogg Vorbis format.
PL2
Binary file archive.
Compression uses the 1989-01-06 LZSS implementation
written by Haruhiko Okumura. Default parameters are kept (N = 4096, F = 18, THRESHOLD = 2, etc.), though the
buffer is initialized to NULL instead of space characters.
//--- 010 Binary Template: PL2_PL2.bt
// Authors: Crazycatz00
// Version: 1.0
// Category: Game
// File Mask: *.pl2
// ID Bytes: [+16] 61 74 74 72 69 62 75 74 65 00
RequiresVersion(3, 1);
LittleEndian();
typedef struct {
char name[32];
uint32 offset;
uint32 compressed_size;
uint32 size;
BYTE padding2C__[4] <hidden=true>;
} ARCHIVE_ITEM;
Assert(sizeof(ARCHIVE_ITEM) == 0x30);
typedef struct {
local uint i, entry_count = (ReadUInt(16 + 32) - 16) / sizeof(ARCHIVE_ITEM);
BYTE reserved00__[16] <hidden=true>;
ARCHIVE_ITEM item[entry_count];
for (i = 0; i < entry_count; ++i)
struct {
FSeek(item[i].offset);
if (item[i].compressed_size == item[i].size)
ubyte raw[item[i].size];
else
ubyte compressed[item[i].compressed_size];
} data;
} ARCHIVE;
ARCHIVE pl2 <open=true>;
PSD
Multilayered image in the proprietary Adobe Photoshop PSD format.
The following reduced version only includes what this engine uses. Note that some versions of Photoshop may save incompatible files.
//--- 010 Binary Template: PL2_PSD.bt
// Authors: Crazycatz00
// Version: 1.0
// Category: Game
// File Mask: *.psd
// ID Bytes: 38 42 50 53 00 01
// Note: Hidden fields aren't read by the engine.
RequiresVersion(3);
BigEndian();
typedef struct {
char signature[4]; // Must be "8BPS"
uint16 version; // Must be 1
BYTE reserved06__[6] <hidden=true>;
uint16 channel_count <hidden=true>;
uint32 height <hidden=true>;
uint32 width <hidden=true>;
uint16 bit_depth; // Must be 8
uint16 color_mode; // Must be 3 (RGB)
uint32 color_section_size;
BYTE color_section[color_section_size] <hidden=true>;
uint32 resource_section_size;
BYTE resource_section[resource_section_size] <hidden=true>;
uint32 layer_section_size;
uint32 layer_info_section_size <hidden=true>;
int16 layer_count; // If negative, absolute value is the layer count and 1st alpha is merged result's transparency
local uint16 real_layer_count = Abs(layer_count);
struct {
int32 top;
int32 left;
int32 bottom;
int32 right;
uint16 channel_count;
struct {
int16 id; // -1 = A, 0 = R, 1 = G, 2 = B
uint32 data_size;
} channel_info[channel_count];
char blend_mode_signature[4] <hidden=true>;
char blend_mode_key[4] <hidden=true>;
ubyte opacity;
ubyte clipping <hidden=true>;
ubyte flags <hidden=true>;
ubyte filter <hidden=true>;
uint32 exta_size;
union {
BYTE ignored00__[exta_size] <hidden=true>;
struct {
uint32 mask_size;
BYTE mask[mask_size] <hidden=true>;
uint32 blending_size;
BYTE blending[blending_size] <hidden=true>;
ubyte name_size; // Must be < 32
char name[name_size];
} fields;
} extra;
} layer[real_layer_count] <optimize=false>;
local int i, j;
for (i = 0; i < real_layer_count; ++i)
struct {
for (j = 0; j < layer[i].channel_count; ++j)
struct {
uint16 compression_mode; // Must be 0 (raw) or 1 (RLE)
ubyte data[layer[i].channel_info[j].data_size - 2];
} channel;
} layer_channels;
} IMAGE;
IMAGE psd <open=true>;
TCM
Text camera animation.
; Engine parser is more forgiving.
tcm =
*el %s"@TCM100" nl
cameraanim
%s"<___end___>" nl
cameraanim =
%s"[CAMERAANIM]" nl
range-ln ; frames
*cameraanim-ln ; [frames]
; Position X, Y, Z; Look X, Y, Z; FoV
cameraanim-ln = *WSP vector3 1*WSP vector3 1*WSP float *WSP nl
; begin, end; count = end - begin + 1
range-ln = *WSP integer 1*WSP integer *WSP nl
nl = CRLF *el
el = [comment] CRLF
comment = "#" *(%x01-0C / %x0E-FF)
vector3 = float 1*WSP float 1*WSP float
float = ["+" / "-"] (1*DIGIT ["." *DIGIT] / "." 1*DIGIT) ["e" ["+" / "-"] 1*DIGIT]
integer = ["+" / "-"] 1*DIGIT
WSP = SP / HTAB / %x0B-0C
TMB
Binary 3D model.
//--- 010 Binary Template: PL2_TMB.bt
// Authors: Crazycatz00
// Version: 1.0
// Category: Game
// File Mask: *.tmb
// ID Bytes: 54 4D 42 30
RequiresVersion(3);
LittleEndian();
typedef struct {
ubyte r, g, b, a;
} COLOR;
typedef struct {
float r, g, b, a;
} FCOLOR;
typedef float MATRIX[16];
typedef struct {
float x, y, z;
} VECTOR3;
typedef struct {
char signature[4]; // "TMB0"
int32 texture_count;
struct {
char name[32];
int16 width;
int16 height;
struct {
ubyte b, g, r, a;
} pixel[(int)width * height];
} texture[texture_count] <optimize=false>;
int32 material_count;
struct {
FCOLOR diffuse;
FCOLOR ambient;
FCOLOR specular;
FCOLOR emissive;
float shininess;
int32 texture_id;
} material[material_count] <optimize=true>;
int32 object_count;
struct {
char name[32];
MATRIX transform;
int32 face_count;
int32 backface_culling_flag; // enable back-face culling if 1
int32 material_count;
struct {
int32 id;
int32 face_begin;
int32 face_count;
} material[material_count];
struct {
struct {
VECTOR3 location;
float bone_weight[3];
ubyte bone_index[3];
BYTE padding1B__ <hidden=true>;
float normal[3];
COLOR color;
float texture_uv[2];
} vertex[3] <optimize=true>;
} face[face_count] <optimize=true>;
} object[object_count] <optimize=false>;
int32 bone_count;
struct {
MATRIX inverse_transform;
} bone[bone_count] <optimize=true>;
int32 point_count;
struct {
uint32 padding00__ <hidden=true>; // Pointer to name in memory
VECTOR3 translation;
VECTOR3 rotation;
VECTOR3 scaling;
} point[point_count] <optimize=true>;
struct {
string name;
} point_name[point_count] <optimize=false>;
} MODEL;
MODEL tmb <open=true>;
TSB
Binary 3D animation.
//--- 010 Binary Template: PL2_TSB.bt
// Authors: Crazycatz00
// Version: 1.0
// Category: Game
// File Mask: *.tsb
// ID Bytes: 54 53 42 30
RequiresVersion(3, 1);
LittleEndian();
typedef float MATRIX[16];
Assert(sizeof(MATRIX) == 0x40);
typedef struct {
float x, y, z;
} VECTOR3;
Assert(sizeof(VECTOR3) == 0x0C);
typedef struct {
char signature[4]; // "TSB0"
BYTE padding04__[12] <hidden=true>;
int32 bone_count;
int32 frame_count;
int32 loop_start_frame;
int32 point_count;
struct {
struct {
MATRIX transform;
} bone[bone_count];
} bone_frame[frame_count] <optimize=true>;
struct {
struct {
VECTOR3 translation;
VECTOR3 rotation;
VECTOR3 scaling;
} point[point_count];
} point_frame[frame_count] <optimize=true>;
struct {
string name;
} point_name[point_count] <optimize=false>;
} ANIMATION;
ANIMATION tsb <open=true>;
TSM
Text 3D model.
The only known source of files in this format is the PL2 trial (pl2_tfinal). Release versions cannot load this format.
TSS
Text 3D animation.
The only known source of files in this format is the PL2 trial (pl2_tfinal). Release versions can be modified to load this format.
; Engine parser is more forgiving.
tss =
*el %s"@TSS100" nl
joints
jointanim
locators
locatoranim
[looppoint]
%s"<___end___>" nl
joints =
%s"[JOINTS]" nl
count-ln
*name-ln ; [joints]
jointanim =
%s"[JOINTANIM]" nl
range-ln ; frames
*jointanim-ln ; [frames * joints]
; Transform matrix
jointanim-ln = *WSP matrix4x4 *WSP nl
locators =
%s"[LOCATORS]" nl
count-ln
*name-ln ; [locators]
locatoranim =
%s"[LOCATORANIM]" nl
range-ln ; ignored; uses range from <jointanim>
*locatoranim-ln ; [frames * locators]
; Translate X, Y, Z; Rotate X, Y, Z; Scale X, Y, Z
locatoranim-ln = *WSP vector3 1*WSP vector3 1*WSP vector3 *WSP nl
looppoint =
%s"[LOOPPOINT]" nl
*WSP integer *WSP nl ; frame
count-ln = *WSP integer *WSP nl
name-ln = *(WSP / VCHAR) nl
; begin, end; count = end - begin + 1
range-ln = *WSP integer 1*WSP integer *WSP nl
nl = CRLF *el
el = [comment] CRLF
comment = "#" *(%x01-0C / %x0E-FF)
matrix4x4 = vector4 1*WSP vector4 1*WSP vector4 1*WSP vector4
vector3 = float 1*WSP float 1*WSP float
vector4 = float 1*WSP float 1*WSP float 1*WSP float
float = ["+" / "-"] (1*DIGIT ["." *DIGIT] / "." 1*DIGIT) ["e" ["+" / "-"] 1*DIGIT]
integer = ["+" / "-"] 1*DIGIT
WSP = SP / HTAB / %x0B-0C
//--- 010 Script: PL2_TSB2TSS.1sc
// Requires: PL2_TSB.bt
// Authors: Crazycatz00
// Version: 1.0
// Category: Game
RequiresVersion(3);
LittleEndian();
void FPMatrix(int fp, const MATRIX& m, const char pf[])
{
FPrintf(
fp, "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f%s", m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9],
m[10], m[11], m[12], m[13], m[14], m[15], pf
);
}
void FPVector3(int fp, const VECTOR3& v, const char pf[]) { FPrintf(fp, "%f %f %f%s", v.x, v.y, v.z, pf); }
void tsb2tss(const ANIMATION& tsb, int fp)
{
local int i, j;
FPrintf(fp, "@TSS100\r\n");
FPrintf(fp, "\r\n[JOINTS]\r\n"); // Line contents ignored
FPrintf(fp, "%d\r\n", tsb.bone_count);
for (i = 0; i < tsb.bone_count; ++i)
FPrintf(fp, "\tjoint_%d\r\n", i);
FPrintf(fp, "\r\n[JOINTANIM]\r\n"); // Line contents ignored
FPrintf(fp, "%d %d\r\n", 0, tsb.frame_count - 1);
for (i = 0; i < tsb.frame_count; ++i) {
FPrintf(fp, "# frame %d\r\n", i);
for (j = 0; j < tsb.bone_count; ++j)
FPMatrix(fp, tsb.bone_frame[i].bone[j].transform, "\r\n");
}
// Bug: Engine fails to set `loop_start_frame` if skipping this block; trash data would be used.
if (true /* bugfix */ || tsb.point_count != 0 || tsb.loop_start_frame != 0) {
FPrintf(fp, "\r\n[LOCATORS]\r\n");
FPrintf(fp, "%d\r\n", tsb.point_count);
for (i = 0; i < tsb.point_count; ++i)
FPrintf(fp, "\t%s\r\n", tsb.point_name[i].name);
FPrintf(fp, "\r\n[LOCATORANIM]\r\n"); // Line contents ignored
FPrintf(fp, "%d %d\r\n", 0, tsb.frame_count - 1); // Line contents ignored
for (i = 0; i < tsb.frame_count; ++i) {
FPrintf(fp, "# frame %d\r\n", i);
for (j = 0; j < tsb.point_count; ++j) {
FPVector3(fp, tsb.point_frame[i].point[j].translation, " ");
FPVector3(fp, tsb.point_frame[i].point[j].rotation, " ");
FPVector3(fp, tsb.point_frame[i].point[j].scaling, "\r\n");
}
}
if (tsb.loop_start_frame != 0) {
FPrintf(fp, "\r\n[LOOPPOINT]\r\n"); // Line contents ignored
FPrintf(fp, "\t%d\r\n", tsb.loop_start_frame);
}
}
FPrintf(fp, "\r\n<___end___>\r\n");
}
int fp = FileNew("Text", false);
tsb2tss(tsb, fp);
FileSelect(fp);