Skip to content

Instantly share code, notes, and snippets.

@kovrov
Created April 23, 2011 13:28
Show Gist options
  • Select an option

  • Save kovrov/938607 to your computer and use it in GitHub Desktop.

Select an option

Save kovrov/938607 to your computer and use it in GitHub Desktop.
fb
import resource;
import std.stdio;
import std.stream;
import std.conv;
enum Flag { End = 1 << 7 }
class Cutscene
{
ushort _id;
resource.CMD res_cmd;
resource.POL res_pol;
std.stream.Stream cmdStream;
ubyte frameDelay;
ubyte _clearScreen;
this(ushort id)
{
_id = id;
}
void play()
{
auto cutName = _offsetsTable[_id][0];
this.load(cutName);
auto cutOff = _offsetsTable[_id][1];
this.mainLoop(cutOff);
}
void load(ushort cutName)
{
assert (cutName != 0xFFFF);
auto name = _namesTable[cutName & 0b11111111];
this.res_cmd = new resource.CMD("DATA/"~name~".cmd");
this.res_pol = new resource.POL("DATA/"~name~".pol");
}
void mainLoop(ushort offset)
{
this.cmdStream = cast(std.stream.Stream)this.res_cmd;
assert (this.cmdStream !is null);
if (offset != 0)
{
this.cmdStream.seekSet((offset + 1) * 2);
this.cmdStream.read(offset);
this.cmdStream.seekSet(0);
}
ushort startOffset;
this.cmdStream.read(startOffset);
startOffset = cast(ushort)((startOffset + 1) * 2);
this.cmdStream.seekSet(startOffset + offset);
writefln("startOffset = %d; offset = %d; cmd = %s;", startOffset, offset, this.cmdStream.position());
for (ubyte op = 7<<2; !(op & Flag.End); this.cmdStream.read(op))
{
writefln("play() opcode = 0x%X (%d)", op, (op >> 2));
_opcodeTable[op >> 2](this);
}
}
static
{
void op_markCurPos(ref Cutscene ctx)
{
writeln("op_markCurPos");
}
void op_refreshScreen(ref Cutscene ctx)
{
writeln("op_refreshScreen");
ctx.cmdStream.read(ctx._clearScreen);
}
void op_waitForSync(ref Cutscene ctx)
{
writeln("op_waitForSync");
ctx.cmdStream.read(ctx.frameDelay); ctx.frameDelay *= 4;
}
void op_drawShape(ref Cutscene ctx)
{
writeln("op_drawShape");
ushort shapeOffset;
ctx.cmdStream.read(shapeOffset);
short x = 0, y = 0;
if (shapeOffset & (1 << 15))
{
ctx.cmdStream.read(x);
ctx.cmdStream.read(y);
}
foreach (primitive; ctx.res_pol.scenes[shapeOffset & 0b_00000111_11111111])
{
switch (primitive.type)
{
case primitive.Type.Polygon:
writefln("Polygon %s", to!string(primitive.polygon));
break;
case primitive.Type.Point:
writefln("Point %s", to!string(primitive.point));
break;
case primitive.Type.Ellipse:
writefln("Ellipse %s", to!string(primitive.ellipse));
break;
}
}
}
void op_setPalette(ref Cutscene ctx)
{
writeln("op_setPalette");
ubyte num, palNum;
ctx.cmdStream.read(num);
ctx.cmdStream.read(palNum);
}
void op_drawStringAtBottom(ref Cutscene ctx)
{
writeln("op_drawStringAtBottom");
ushort strId;
ctx.cmdStream.read(strId);
}
void op_nop(ref Cutscene ctx)
{
writeln("op_nop");
}
void op_skip3(ref Cutscene ctx)
{
writeln("op_skip3");
}
void op_refreshAll(ref Cutscene ctx)
{
writeln("op_refreshAll");
}
void op_drawShapeScale(ref Cutscene ctx)
{
writeln("op_drawShapeScale");
short x = 0, y = 0;
ushort shapeOffset;
ctx.cmdStream.read(shapeOffset);
if (shapeOffset & (1 << 15))
{
ctx.cmdStream.read(x);
ctx.cmdStream.read(y);
}
ushort zoom = 512;
if (shapeOffset & (1 << 14))
{
ctx.cmdStream.read(zoom);
zoom += 512;
}
ubyte _shape_ix, _shape_iy;
ctx.cmdStream.read(_shape_ix);
ctx.cmdStream.read(_shape_iy);
ushort r1, r2 = 180, r3 = 90;
ctx.cmdStream.read(r1);
if (shapeOffset & (1 << 13))
{
ctx.cmdStream.read(r2);
}
if (shapeOffset & (1 << 12))
{
ctx.cmdStream.read(r3);
}
}
void op_drawShapeScaleRotate(ref Cutscene ctx)
{
writeln("op_drawShapeScaleRotate");
ushort shapeOffset;
ctx.cmdStream.read(shapeOffset);
short x = 0, y = 0;
if (shapeOffset & (1 << 15))
{
ctx.cmdStream.read(x);
ctx.cmdStream.read(y);
}
ushort zoom = 512;
if (shapeOffset & (1 << 14))
{
ctx.cmdStream.read(zoom);
zoom += 512;
}
ubyte _shape_ix, _shape_iy;
ctx.cmdStream.read(_shape_ix);
ctx.cmdStream.read(_shape_iy);
ushort r1, r2 = 180, r3 = 90;
ctx.cmdStream.read(r1);
if (shapeOffset & (1 << 13))
{
ctx.cmdStream.read(r2);
}
if (shapeOffset & (1 << 12))
{
ctx.cmdStream.read(r3);
}
}
void op_drawCreditsText(ref Cutscene ctx)
{
writeln("op_drawCreditsText");
}
void op_drawStringAtPos(ref Cutscene ctx)
{
writeln("op_drawStringAtPos");
ushort strId;
ctx.cmdStream.read(strId);
if (strId != 0xFFFF)
{
short x,y;
ctx.cmdStream.read(x); x *= 8;
ctx.cmdStream.read(y); y *= 8;
}
}
void op_handleKeys(ref Cutscene ctx)
{
writeln("op_handleKeys");
assert (false);
}
auto _opcodeTable = [
&op_markCurPos,
&op_refreshScreen,
&op_waitForSync,
&op_drawShape,
&op_setPalette,
&op_markCurPos,
&op_drawStringAtBottom,
&op_nop,
&op_skip3,
&op_refreshAll,
&op_drawShapeScale,
&op_drawShapeScaleRotate,
&op_drawCreditsText,
&op_drawStringAtPos,
&op_handleKeys];
immutable ushort[2][] _offsetsTable = [
[0x0000, 0x0000], [0x0001, 0x0003], [0x0001, 0x0004], [0xFFFF, 0x0000],
[0x0001, 0x0002], [0x0003, 0x0000], [0x0004, 0x0000], [0xFFFF, 0x0100],
[0xFFFF, 0x0000], [0x0006, 0x0000], [0x0001, 0x0001], [0xFFFF, 0x0000],
[0xFFFF, 0x0200], [0x8007, 0x0000], [0x0003, 0x0001], [0x0001, 0x000B],
[0x0001, 0x0005], [0x0009, 0x0000], [0x0001, 0x0006], [0xFFFF, 0x0000],
[0x000B, 0x0000], [0x0001, 0x000A], [0xFFFF, 0x0001], [0xFFFF, 0x0002],
[0xFFFF, 0x0000], [0x000D, 0x0004], [0x000D, 0x0000], [0x000D, 0x0001],
[0x000D, 0x0002], [0x000D, 0x0003], [0xFFFF, 0x0000], [0xFFFF, 0x0001],
[0x0001, 0x000C], [0x0001, 0x000D], [0x0001, 0x000E], [0x0001, 0x000F],
[0x0001, 0x0010], [0x000F, 0x0000], [0x000F, 0x0001], [0x000F, 0x0001],
[0x000F, 0x0003], [0x000F, 0x0002], [0x000F, 0x0004], [0x0001, 0x0008],
[0x0001, 0x0007], [0x000F, 0x0005], [0xFFFF, 0x0000], [0x0004, 0x0001],
[0x0011, 0x0000], [0x0001, 0x0009], [0x0012, 0x0000], [0xFFFF, 0x0000],
[0x0014, 0x0000], [0x0015, 0x0000], [0x0016, 0x0000], [0x0016, 0x0001],
[0xFFFF, 0x0012], [0x0017, 0x0000], [0x0001, 0x0011], [0x0018, 0x0000],
[0x0001, 0x0013], [0x0019, 0x0000], [0x001A, 0x0000], [0x0019, 0x0001],
[0x001B, 0x0000], [0x001C, 0x0000], [0x000F, 0x0006], [0x000F, 0x0006],
[0x000F, 0x0007], [0x000F, 0x0008], [0x000F, 0x0009], [0x000F, 0x000A],
[0x001D, 0x0000], [0x001B, 0x0001], [0x001E, 0x0000], [0xFFFF, 0x0000]];
immutable string[] _namesTable = [
"DEBUT",
"OBJET",
"CARTE",
"GEN",
"CHUTE",
"CODE",
"DESINTEG",
"intro1",//"INTRO1",
"STREM",
"HOLOSEQ",
"CARTEID",
"PONT",
"ASC",
"MAP",
"METRO",
"MISSIONS",
"GENMIS",
"MEMO",
"TAXI",
"ACCROCHE",
"VOYAGE",
"TELEPORT",
"LIFT",
"ESPIONS",
"LOG",
"FIN",
"GENEXP",
"LOGOS",
"OVER",
"SCORE",
"INTRO2"];
}
}
void main()
{
auto cut = new Cutscene(13);
cut.play();
}
import std.stdio;
import std.system : Endian;
import std.stream : MemoryStream, EndianStream, Stream;
class CMD
{
ubyte[] _data;
this(string path)
{
auto f = std.stdio.File(path, "rb");
_data.length = cast(size_t)f.size();
_data = f.rawRead(_data);
}
Stream opCast(T)() if (is(Stream == T))
{
return new EndianStream(new MemoryStream(_data), Endian.BigEndian);
}
}
class POL
{
align (1) struct Point { short x; short y; }
align (1) struct Ellipse { Point center; ushort rx; ushort ry; }
struct Primitive
{
enum Type { Ellipse, Point, Polygon }
Type type;
ubyte color;
bool hasAlphaColor;
short x, y;
union
{
Ellipse ellipse;
Point point;
Point[] polygon;
}
}
Primitive[][] scenes;
this(string path)
{
scope file = new std.stream.BufferedFile(path);
scope stream = new EndianStream(file, Endian.BigEndian);
ushort shape_offset_index, unknown_index, shape_data_index, vertices_offset_index, vertices_data_index;
stream.seekSet(2); stream.read(shape_offset_index);
stream.seekSet(6); stream.read(unknown_index);
stream.seekSet(14); stream.read(shape_data_index);
stream.seekSet(10); stream.read(vertices_offset_index);
stream.seekSet(18); stream.read(vertices_data_index);
scope ushort[] shape_offset_table;
shape_offset_table.length = (unknown_index - shape_offset_index) / 2;
stream.seekSet(shape_offset_index);
foreach (ref shape_offset; shape_offset_table)
stream.read(shape_offset);
scope ushort[] vertices_offset_table;
vertices_offset_table.length = (vertices_data_index - vertices_offset_index) / 2;
stream.seekSet(vertices_offset_index);
foreach (ref vertices_offset; vertices_offset_table)
stream.read(vertices_offset);
foreach (i, shape_offset; shape_offset_table)
{
stream.seekSet(shape_data_index + shape_offset);
ushort primitive_count; stream.read(primitive_count);
scenes.length += 1;
scenes[$-1].length = primitive_count;
foreach (ref primitive; scenes[$-1])
{
ushort primitive_header; stream.read(primitive_header);
if (primitive_header & (1 << 15))
{
stream.read(primitive.x);
stream.read(primitive.y);
}
stream.read(primitive.color);
primitive.hasAlphaColor = (primitive_header & (1 << 14)) != 0;
auto shape_data_position = stream.position();
auto vertices_offset = vertices_offset_table[primitive_header & 0b_00111111_11111111];
stream.seekSet(vertices_data_index + vertices_offset);
ubyte numVertices; stream.read(numVertices);
if (numVertices & (1 << 7))
{
primitive.type = Primitive.Type.Ellipse;
stream.read(primitive.ellipse.center.x);
stream.read(primitive.ellipse.center.y);
stream.read(primitive.ellipse.rx);
stream.read(primitive.ellipse.ry);
}
else if (numVertices == 0)
{
primitive.type = Primitive.Type.Point;
stream.read(primitive.point.x);
stream.read(primitive.point.y);
}
else
{
primitive.type = Primitive.Type.Polygon;
primitive.polygon = []; // union initialization is unreliable?
short ix, iy;
stream.read(ix);
stream.read(iy);
primitive.polygon.length += 1;
primitive.polygon[$-1].x = ix;
primitive.polygon[$-1].y = iy;
byte[] vertices;
vertices.length = numVertices * 2;
stream.readExact(vertices.ptr, vertices.length);
auto vertices_ptr = vertices.ptr;
foreach (n; 0 .. numVertices)
{
short x = *vertices_ptr++;
short y = *vertices_ptr++;
if (y == 0 && n != (numVertices - 1) && vertices_ptr[1] == 0)
{
ix += x;
}
else
{
ix += x;
iy += y;
primitive.polygon.length += 1;
primitive.polygon[$-1].x = ix;
primitive.polygon[$-1].y = iy;
}
}
}
stream.seekSet(shape_data_position);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment