apophysis7x/IO/Hibernation.pas

398 lines
12 KiB
ObjectPascal
Raw Permalink Normal View History

unit Hibernation;
interface
uses RenderingCommon, RenderingInterface, SysUtils, Windows, Forms, Classes, Binary, ControlPoint;
const
HIB_VERSION_MAJOR = 2; // Apophysis7X.15
HIB_VERSION_MINOR = 10;
HIB_VERSION_REVISION = 1500;
HIB_ADDR_HEADER = $30; // 48 bytes
HIB_SIZE_HEADER = $30; // 48 bytes
HIB_ADDR_LOCATOR = $60; // HIB_SIZE_HEADER + HIB_ADDR_HEADER
HIB_SIZE_LOCATOR = $20; // 32 bytes
HIB_COOKIE_INTRO_HIGH = $AB; // A B0F
HIB_COOKIE_INTRO_LOW = $0F;
HIB_COOKIE_OUTRO_HIGH = $AE; // A E0F
HIB_COOKIE_OUTRO_LOW = $0F;
type
EHibBitsPerPixel = (
EBP_32 = 32,
EBP_64 = 64 // 64 bit renderer; probably used later
);
EHibPixelLayout = (
EPL_XYZW = 0123,
EPL_YXZW = 1023, // indicates different subpixel orders;
EPL_YZXW = 1203, // for example, metaphysis uses WXYZ (3012)
EPL_YZWX = 1230,
EPL_XZYW = 0213,
EPL_XZWY = 0231,
EPL_XYWZ = 0132,
EPL_ZXYW = 2013,
EPL_WXYZ = 3012,
EPL_XWYZ = 0312
);
EHibFileFlags = (
EFF_NONE = 0, // no flags (default)
EFF_FLOATBUFFER = 1, // using a float buffer (32/64bit)
EFF_SEPARATEFLAME = 2, // flame stored in separate file (unused yet)
EFF_BINARYFLAME = 4, // flame stored in binary format (unused yet)
EFF_COMPRESSED = 8 // data is GZIP-compressed (unused yet)
);
// some types for the header (some unused)
THibInt2_32 = record X, Y : integer; end;
THibInt2_64 = record X, Y : int64; end;
THibFloat2 = record X, Y : double; end;
THibFloat4 = record X, Y, Z, W : double; end;
THibBoolean = (HB_YES = -1, HB_NO = 0);
// the actual header
THibHeader = record
ActualDensity : double;
Size2D : THibInt2_64;
Size : int64;
RenderTime : TDateTime;
PauseTime : TDateTime;
end;
// file allocation and release procedures
procedure HibAllocate(var handle: File; path: string);
procedure HibOpen(var handle: File; path: string);
procedure HibFree(const handle: File);
// high-level write procedures
procedure HibWriteIntro(const handle: File);
procedure HibWriteOutro(const handle: File);
procedure HibWriteGlobals(const handle: File; flags: EHibFileFlags;
layout: EHibPixelLayout; bpp: EHibBitsPerPixel);
procedure HibWriteHeader(const handle: File; header: THibHeader);
procedure HibWriteData(const handle: File; header: THibHeader;
flame: TControlPoint; buckets: TBucket32Array;
colormap: TColorMapArray; callback: TOnProgress);
// high-level read procedures
procedure HibReadIntro(const handle: File; var cookieValid: boolean);
procedure HibReadOutro(const handle: File; var cookieValid: boolean);
procedure HibReadGlobals(const handle: File; var versionRel: smallint;
var flags: EHibFileFlags; var layout: EHibPixelLayout; var bpp: EHibBitsPerPixel);
procedure HibReadHeader(const handle: File; var header: THibHeader);
procedure HibReadData(const handle: File; header: THibHeader; var flame: TStringList;
var buckets: TBucket32Array; var colormap: TColorMapArray; callback: TOnProgress);
implementation
////////////////////////////////////////////////////////////////////////////////
procedure HibAllocate(var handle: File; path: string);
begin
AssignFile(handle, path);
ReWrite(handle, HIB_BLOCKSIZE);
end;
procedure HibOpen(var handle: File; path: string);
begin
AssignFile(handle, path);
FileMode := fmOpenRead;
Reset(handle, HIB_BLOCKSIZE);
end;
procedure HibFree(const handle: File);
begin
CloseFile(handle);
end;
////////////////////////////////////////////////////////////////////////////////
procedure HibWriteIntro(const handle: File);
var
block: TBlock;
chunk: string;
begin
block[0] := HIB_COOKIE_INTRO_HIGH;
block[1] := HIB_COOKIE_INTRO_LOW;
chunk := 'Apophysis7X Hi';
CopyMemory(@block[2], @chunk[1], Length(chunk));
BlockWrite(handle, block, 1);
chunk := 'bernation File';
block[14] := $0; block[15] := $0;
CopyMemory(@block[0], @chunk[1], Length(chunk));
BlockWrite(handle, block, 1);
end;
procedure HibWriteOutro(const handle: File);
var
block: TBlock;
begin
block[0] := $0; block[1] := $0; block[2] := $0; block[3] := $0;
block[4] := $0; block[5] := $0; block[6] := $0; block[7] := $0;
block[8] := $0; block[9] := $0; block[10] := $0; block[11] := $0;
block[12] := $0; block[13] := $0;
block[14] := HIB_COOKIE_OUTRO_HIGH;
block[15] := HIB_COOKIE_OUTRO_LOW;
BlockWrite(handle, block, 1);
end;
procedure HibWriteGlobals(const handle: File; flags: EHibFileFlags;
layout: EHibPixelLayout; bpp: EHibBitsPerPixel);
var
block: TBlock;
begin
Int16ToBlock(block, 0, HIB_VERSION_MAJOR);
Int16ToBlock(block, 2, HIB_VERSION_MINOR);
Int32ToBlock(block, 4, HIB_VERSION_REVISION);
Int16ToBlock(block, 8, SmallInt(flags));
Int16ToBlock(block, 10, HIB_SIZE_HEADER);
Int16ToBlock(block, 12, SmallInt(layout));
Int16ToBlock(block, 14, SmallInt(bpp));
BlockWrite(handle, block, 1);
end;
procedure HibWriteHeader(const handle: File; header: THibHeader);
var
block: TBlock;
begin
DoubleToBlock(block, 0, header.ActualDensity);
Int64ToBlock(block, 8, header.Size);
BlockWrite(handle, block, 1);
Int64ToBlock(block, 0, header.Size2D.X);
Int64ToBlock(block, 8, header.Size2D.Y);
BlockWrite(handle, block, 1);
Int64ToBlock(block, 0,
Int64(((Trunc(header.RenderTime) - 25569) * 86400) +
Trunc(86400 * (header.RenderTime -
Trunc(header.RenderTime))) - 7200));
Int64ToBlock(block, 8,
Int64(((Trunc(header.PauseTime) - 25569) * 86400) +
Trunc(86400 * (header.PauseTime -
Trunc(header.PauseTime))) - 7200));
BlockWrite(handle, block, 1);
end;
procedure HibWriteData(const handle: File; header: THibHeader; flame: TControlPoint;
buckets: TBucket32Array; colormap: TColorMapArray; callback: TOnProgress);
var
block: TBlock;
flametext: string;
rawflame: THibRawString;
rawflamesize: integer;
rawflamechunks: integer;
i, j, c: integer;
p, step: double;
begin
rawflamesize := CalcBinaryFlameSize(flame);
Int64ToBlock(block, 0, HIB_ADDR_LOCATOR + HIB_SIZE_LOCATOR);
Int64ToBlock(block, 8, Int64(rawflamesize) );
BlockWrite(handle, block, 1);
Int64ToBlock(block, 0, HIB_ADDR_LOCATOR + HIB_SIZE_LOCATOR + rawflamesize);
Int64ToBlock(block, 8, 16 * header.Size2D.X * header.Size2D.Y);
BlockWrite(handle, block, 1);
flame.SaveToBinary(handle);
callback(0);
c := 0; p := 0;
step := 1.0 / (header.Size2D.X * header.Size2D.Y);
for j := 0 to header.Size2D.Y - 1 do
for i := 0 to header.Size2D.X - 1 do
with buckets[j][i] do begin
Int32ToBlock(block, 0, Red);
Int32ToBlock(block, 4, Green);
Int32ToBlock(block, 8, Blue);
Int32ToBlock(block, 12, Count);
BlockWrite(handle, block, 1);
p := p + step;
c := (c + 1) mod 64;
if (c = 0) then begin
callback(p*0.99);
Application.ProcessMessages;
end;
end;
callback(0.99);
i := 0;
while i < 256 do begin
Int32ToBlock(block, 0,
(colormap[i+0].Red) or
(((colormap[i+0].Green) and $ff) shl 8) or
(((colormap[i+0].Blue) and $ff) shl 16));
Int32ToBlock(block, 4,
(colormap[i+1].Red) or
(((colormap[i+1].Green) and $ff) shl 8) or
(((colormap[i+1].Blue) and $ff) shl 16));
Int32ToBlock(block, 8,
(colormap[i+2].Red) or
(((colormap[i+2].Green) and $ff) shl 8) or
(((colormap[i+2].Blue) and $ff) shl 16));
Int32ToBlock(block, 12,
(colormap[i+3].Red) or
(((colormap[i+3].Green) and $ff) shl 8) or
(((colormap[i+3].Blue) and $ff) shl 16));
BlockWrite(handle, block, 1);
i := i + 4;
end;
callback(1);
end;
////////////////////////////////////////////////////////////////////////////////
procedure HibReadIntro(const handle: File; var cookieValid: boolean);
var
block1, block2: TBlock;
begin
BlockRead(handle, block1, 1);
BlockRead(handle, block2, 1);
cookieValid :=
(block1[0] = HIB_COOKIE_INTRO_HIGH) and
(block1[1] = HIB_COOKIE_INTRO_LOW);
end;
procedure HibReadOutro(const handle: File; var cookieValid: boolean);
var
block1, block2: TBlock;
begin
BlockRead(handle, block1, 1);
BlockRead(handle, block2, 1);
cookieValid :=
(block2[14] = HIB_COOKIE_OUTRO_HIGH) and
(block2[15] = HIB_COOKIE_OUTRO_LOW);
end;
procedure HibReadGlobals(const handle: File; var versionRel: SmallInt;
var flags: EHibFileFlags; var layout: EHibPixelLayout; var bpp: EHibBitsPerPixel);
var
block: TBlock;
major, minor, rev: Integer;
begin
BlockRead(handle, block, 1);
major := BlockToInt16(block, 0);
minor := BlockToInt16(block, 2);
rev := BlockToInt32(block, 4);
flags := EHibFileFlags(BlockToInt16(block, 8));
assert(BlockToInt16(block, 10) <> HIB_SIZE_HEADER, 'Invalid header size');
layout := EHibPixelLayout(BlockToInt16(block, 12));
bpp := EHibBitsPerPixel(BlockToInt16(block, 14));
if major < HIB_VERSION_MAJOR then versionRel := -1
else if major > HIB_VERSION_MAJOR then versionRel := 1
else begin
if minor < HIB_VERSION_MINOR then versionRel := -1
else if minor > HIB_VERSION_MINOR then versionRel := 1
else begin
if rev < HIB_VERSION_REVISION then versionRel := -1
else if rev > HIB_VERSION_REVISION then versionRel := 1
else versionRel := 0;
end;
end;
end;
procedure HibReadHeader(const handle: File; var header: THibHeader);
var
block: TBlock;
begin
BlockRead(handle, block, 1);
header.ActualDensity := BlockToDouble(block, 0);
header.Size := BlockToInt64(block, 8);
BlockRead(handle, block, 1);
header.Size2D.X := BlockToInt64(block, 0);
header.Size2D.Y := BlockToInt64(block, 8);
BlockRead(handle, block, 1);
header.RenderTime := ((BlockToInt64(block, 0) + 7200) / 86500) + 25569;
header.PauseTime := ((BlockToInt64(block, 8) + 7200) / 86500) + 25569;
end;
procedure HibReadData(const handle: File; header: THibHeader; var flame: TStringList;
var buckets: TBucket32Array; var colormap: TColorMapArray; callback: TOnProgress);
var
block: TBlock;
pos, offsf, sizef, offsd, sized : Int64;
fbytes: THibRawString;
i, c, bx, by: Integer;
p, step: Double;
begin
BlockRead(handle, block, 1);
offsf := BlockToInt64(block, 0);
sizef := BlockToInt64(block, 8);
BlockRead(handle, block, 1);
offsd := BlockToInt64(block, 0);
sized := BlockToInt64(block, 8);
pos := 0;
Seek(handle, offsf);
SetLength(fbytes, sizef);
while pos < sizef do begin
BlockRead(handle, block, 1);
CopyMemory(@fbytes[pos], @block[0], HIB_BLOCKSIZE);
pos := pos + HIB_BLOCKSIZE;
end;
flame := TStringList.Create;
flame.Text := PChar(fbytes);
pos := 0;
bx := 0; by := 0;
Seek(handle, offsd);
SetLength(buckets, header.Size2D.Y, header.Size2D.X);
callback(0);
c := 0; p := 0;
step := 1.0 / sized;
while pos < sized do begin
with buckets[by][bx] do begin
BlockRead(handle, block, 1);
Red := BlockToInt32(block, 0);
Green := BlockToInt32(block, 4);
Blue := BlockToInt32(block, 8);
Count := BlockToInt32(block, 12);
end;
Inc(bx);
pos := pos + HIB_BLOCKSIZE;
if bx >= header.Size2D.X then begin
Inc(by); bx := 0;
end;
p := p + step;
c := (c + 1) mod 64;
if (c = 0) then begin
callback(p*0.99);
Application.ProcessMessages;
end;
end;
callback(0.99);
i := 0;
while i < 256 do begin
BlockRead(handle, block, 1);
c := BlockToInt32(block, 0);
colormap[i+0].Red := c and $ff;
colormap[i+0].Green := (c and $ff00) shr 8;
colormap[i+0].Blue := (c and $ff0000) shr 16;
c := BlockToInt32(block, 4);
colormap[i+1].Red := c and $ff;
colormap[i+1].Green := (c and $ff00) shr 8;
colormap[i+1].Blue := (c and $ff0000) shr 16;
c := BlockToInt32(block, 8);
colormap[i+2].Red := c and $ff;
colormap[i+2].Green := (c and $ff00) shr 8;
colormap[i+2].Blue := (c and $ff0000) shr 16;
c := BlockToInt32(block, 12);
colormap[i+3].Red := c and $ff;
colormap[i+3].Green := (c and $ff00) shr 8;
colormap[i+3].Blue := (c and $ff0000) shr 16;
i := i + 4;
end;
end;
end.