unit data;

interface

uses
  classes, sysUtils,

  windows, graphics;


type

  TColor24 = packed record
    r, g, b : byte;
  end;


  TIndex = packed record
    start : DWord;
    length : Integer;
    unknown : DWord;
  end;


  PIndexArray = ^TIndexArray;
  TIndexArray = array [0..0] of TIndex;


  TCache = packed record
    refcount : smallint;
    data : TMemoryStream;
  end;


  PCacheArray = ^TCacheArray;
  TCacheArray = array [0..0] of TCache;


  TTexture = array [0..0] of Word;
  PTexture = ^TTexture;


  PRawTileArt = ^TRawTileArt;
  TRawTileArt = array [0..0] of Word;


  PRunTileArt = ^TRunTileArt;
  TRunTileArt = packed record
    flag : DWord;
    width,
    height : word;
    rowOffsets : array [0..0] of word;
  end;


  TAnimPalette = array [0..256-1] of word;


  PAnimationHeader = ^TAnimationHeader;
  TAnimationHeader = packed record
    palette : TAnimPalette;
    frameCount : DWord;
    frameOffset : array [0..0] of DWord;
  end;


  PAnimationFrame = ^TAnimationFrame;
  TAnimationFrame = packed record
    ImageCenterX : word;
    ImageCenterY : word;
    Width : word;
    Height : word;
  end;


  TAnim = array [0..0] of Word;
  PAnim = ^TAnim;


  PMapCell = ^TMapCell;
  TMapCell = packed record
    color : word;
    altitude : shortint;
  end;


  PMapBlock = ^TMapBlock;
  TMapBlock = packed record
    header : DWord;
    mapCells : array [0..64-1] of TMapCell;
  end;


  PMultiBlock = ^TMultiBlock;
  TMultiBlock = packed record
    BlockNum : word;
    X : smallInt;
    Y : smallInt;
    Alt : smallInt;
    Flags : DWord;
  end;

  PMultiBlockArray = ^TMultiBlockArray;
  TMultiBlockArray = array [0..0] of TMultiBlock;


  PRadarColorsArray = ^TRadarColorsArray;
  TRadarColorsArray = array [0..0] of word;


  PMapStatic = ^TMapStatic;
  TMapStatic = packed record
    color : word;
    x, y : byte;
    altitude : shortint;
    unkown : word;
  end;


  PMapStaticArray = ^TMapStaticArray;
  TMapStaticArray = array [0..0] of TMapStatic;


  // begin of my own data structures

  TUOData = class;


  TMapObjectX = class (TObject)
    color : word;
    altitude : shortint;
    unkown : word;

    runTileArtStream : TMemoryStream;
    runTileArt : PRunTileArt;

    next : TMapObjectX;

    destructor destroy; override;
  end;


  TGroundX = record
    color : word;
    altitude : shortint;

    art : TMemoryStream;
    texture : TMemoryStream;
    mapObject : TMapObjectX;
  end;


  TMapBlockX = class (TObject)
    mapX, mapY : integer;
    ground : array [0..7, 0..7] of TGroundX;
    mapStaticStream : TMemoryStream;

    procedure load (uodata : TUOData; x0, y0 : integer);
    procedure unload (uodata : TUOData);
    procedure insertMapStatic (uodata : TUOData; var next : TMapObjectX;
                               mapStatic : PMapStatic; runTileArtStream : TMemoryStream);
    procedure deleteMapObject (uodata : TUOData; var next : TMapObjectX);
  end;


  TResource = class (TObject)
    indexStream : TMemoryStream;
    cacheStream : TMemoryStream;
    stream : TFileStream;
    indexesSize : integer;
    indexes : PIndexArray;
    cache : PCacheArray;

    constructor create (filename, indexFilename : string);
    destructor destroy; override;
    procedure clearCache;
    procedure deleteCache;
  end;


  TUOData = class (TObject)
  private
    procedure loadUOConfig;
    function resolveFilename (filename : string) : string;
  public
    cdromdatapath : string;

    textureResource : TResource;
    artResource : TResource;
    animResource : TResource;
    mapStaticResource : TResource;
    multiResource : TResource;

    mapStream : TFileStream;

    radarColorsStream : TMemoryStream;
    radarColorsSize : integer;
    radarColors : ^TRadarColorsArray;

    constructor create;
    destructor destroy; override;

    procedure loadMapBlockFromFile (var m : TMapBlock; mapIndex : integer);
    procedure loadBlobFromFile (m : TMemoryStream; const resource : TResource; index : integer);
    function loadBlobFromCache (const resource : TResource; index : integer) : TMemoryStream;
    procedure unloadBlobFromCache (const resource : TResource; index : integer);
  end;


function convertUOColor (color : word) : TColor;
function convertUOColor24 (color : word) : TColor24;

procedure convertTextureToBitmap (texture : PTexture; bitmap : TBitmap; large : boolean);

procedure convertAnimationFrameToBitmap (m : TMemoryStream;
                                    bitmap : TBitmap;
                                    centreX, centreY : integer; flipX : boolean;
                                    colorMap : TColor;
                                    frameIndex : integer);

procedure convertRawTileToBitmap (m : TMemoryStream; bitmap : TBitmap; startX, startY : integer);
procedure convertRunTileToBitmap (m : TMemoryStream; bitmap : TBitmap; startX, startY : integer);

procedure drawTriangleTexture (p1, p2, p3 : TPoint;
                               dest : TBitmap;
                               tp1, tp2, tp3 : TPoint;
                               source : TBitmap);

implementation

uses
  unit1;

function convertUOColor (color : word) : TColor;
begin
  result :=
   ((((color shr 10) and $1F) * $FF div $1F) +
   ((((color shr 5) and $1F) * $FF div $1F) shl 8) +
   ((( color and $1F) * $FF div $1F) shl 16));
end;


function convertUOColor24 (color : word) : TColor24;
begin
  result.r := (((color shr 10) and $1F) * $FF div $1F);
  result.g := (((color shr  5) and $1F) * $FF div $1F);
  result.b := (((color shr  0) and $1F) * $FF div $1F);
end;


procedure swapPoint (var p0, p1 : TPoint);
var
  p : TPoint;
begin
  p := p0;
  p0 := p1;
  p1 := p;
end;


procedure swapExtended (var d0, d1 : extended);
var
  d : extended;
begin
  d := d0;
  d0 := d1;
  d1 := d;
end;


procedure swapInt (var i0, i1 : integer);
var
  i : integer;
begin
  i := i0;
  i0 := i1;
  i1 := i;
end;


procedure drawTriangleTexture (p1, p2, p3 : TPoint;
                               dest : TBitmap;
                               tp1, tp2, tp3 : TPoint;
                               source : TBitmap);
  procedure doScanLine (const startP, finishP, startTP, finishTP : TPoint);
  var
    x, y, d : integer;
    tx, ty, tx0, ty0, tx1, ty1, dtx, dty : extended;
    r0, r1 : extended;
    x0, x1 : integer;
    sourceScanline : PByteArray;
    destScanline : PByteArray;
  begin
    for y := startp.y to finishP.y - 1 do
    begin
      if (y >= 0) and (y < dest.height) then
      begin
        destScanline := dest.scanline [y];

        r0 := (y - startP.y) / (finishP.y - startP.y);
        x0 := trunc (startP.x + (finishP.x - startP.x) * r0);

        r1 := (y - p1.y) / (p3.y - p1.y);
        x1 := trunc (p1.x + (p3.x - p1.x) * r1);

        d := abs (x0 - x1) + 1;

        tx0 := startTP.x + (finishTP.x - startTP.x) * r0;
        ty0 := startTP.y + (finishTP.y - startTP.y) * r0;

        tx1 := tp1.x + (tp3.x - tp1.x) * r1;
        ty1 := tp1.y + (tp3.y - tp1.y) * r1;

        dtx := (tx1 - tx0) / d;
        dty := (ty1 - ty0) / d;

        tx := tx0;
        ty := ty0;

        if x0 > x1 then
        begin
          swapInt (x0, x1);
          dtx := (tx0 - tx1) / d;
          dty := (ty0 - ty1) / d;
          tx := tx1;
          ty := ty1;
        end
        else
        begin
          dtx := (tx1 - tx0) / d;
          dty := (ty1 - ty0) / d;
          tx := tx0;
          ty := ty0;
        end;

        for x := x0 to x1 do
        begin
          if (x >= 0) and (x < dest.width)
           and (trunc (tx) >= 0) and (trunc (tx) < (source.width - 1))
           and (trunc (ty) >= 0) and (trunc (ty) < (source.height - 1)) then
          begin
            sourceScanLine := source.scanline [trunc (ty)];
            destScanLine [trunc (x) * 3 + 0] := sourceScanline [trunc (tx) * 3 + 0];
            destScanLine [trunc (x) * 3 + 1] := sourceScanline [trunc (tx) * 3 + 1];
            destScanLine [trunc (x) * 3 + 2] := sourceScanline [trunc (tx) * 3 + 2];
          end;

          tx := tx + dtx;
          ty := ty + dty;
        end;
      end;
    end;
  end;
begin
  if p2.y < p1.y then
  begin
    swapPoint (p2, p1);
    swapPoint (tp2, tp1);
  end;

  if p3.y < p1.y then
  begin
    swapPoint (p3, p1);
    swapPoint (tp3, tp1);
  end;

  if p3.y < p2.y then
  begin
    swapPoint (p3, p2);
    swapPoint (tp3, tp2);
  end;

  doScanLine (p1, p2, tp1, tp2);
  doScanLine (p2, p3, tp2, tp3);
end;


procedure convertTextureToBitmap (texture : PTexture; bitmap : TBitmap; large : boolean);
var
  x, y : integer;
  size : integer;
  scanline : PByteArray;
  c : TColor24;
begin
  if large then size := 128 else size := 64;

  for y := 0 to size - 1 do
  begin
    scanline := bitmap.scanline [y];
    for x := 0 to size - 1 do
    begin
      c := convertUOColor24 (texture [y * size + x]);
      scanline^ [x * 3 + 0] := c.b;
      scanline^ [x * 3 + 1] := c.g;
      scanline^ [x * 3 + 2] := c.r;
    end;
  end;
end;


procedure convertAnimationFrameToBitmap (m : TMemoryStream;
                                    bitmap : TBitmap;
                                    centreX, centreY : integer; flipX : boolean;
                                    colorMap : TColor;
                                    frameIndex : integer);
var
  animationHeader : PAnimationHeader;
  animationFrame : PAnimationFrame;

  RowHeader : word;
  RowOfs : word;
  RunLength : word;
  LineNum : word;
  Unknown : word;

  PrevLineNum, X, Y, X0 : Word;

  B : Byte;
  i : integer;
  c : TColor24;
  scanline : PByteArray;
  crashPreventCount : integer;

  palette : array [0..$100-1] of TColor24;
begin
  AnimationHeader := PAnimationHeader (m.memory);

  animationFrame := PAnimationFrame (longint (m.memory) +
   sizeOf (TAnimPalette) + AnimationHeader.frameOffset [frameIndex]);

  m.seek (sizeOf (TAnimPalette) + AnimationHeader.frameOffset [frameIndex] + sizeOf (TAnimationFrame), soFromBeginning);

  for i := low (palette) to high (palette) do
  begin
    c := convertUOColor24 (AnimationHeader.palette [i]);
    if (colorMap <> 0) and (c.r = c.g) {and (c.r = c.b)} then
    begin
      c.r := (((colorMap div $000001) mod $100) * c.r) div $100;
      c.g := (((colorMap div $000100) mod $100) * c.g) div $100;
      c.b := (((colorMap div $010000) mod $100) * c.b) div $100;

      if (c.r>=235) and (c.g>=235) and (c.b>=235) then
        messagebeep (0);
    end;
    palette [i] := c;
  end;


  PrevLineNum := $FF;
  Y := centreY - animationFrame.ImageCenterY - animationFrame.Height;

  crashPreventCount := 0;
  while crashPreventCount < 500 do
  begin
    m.read (RowHeader, sizeOf (RowHeader));
    m.read (RowOfs, sizeOf (RowOfs));

    if (RowHeader = $7FFF) or (RowOfs = $7FFF) then
      exit;

    RunLength := RowHeader and $FFF;
    LineNum := RowHeader shr 12;
    Unknown := RowOfs and $3F;
    RowOfs := RowOfs shr 6;

    if flipX then
      X := centreX - (((animationFrame.imageCenterX + RowOfs) mod 512) - animationFrame.imageCenterX)
    else
      X := centreX + (((animationFrame.imageCenterX + RowOfs) mod 512) - animationFrame.imageCenterX);

    if (PrevLineNum <> $FF) and (LineNum <> PrevLineNum) then
      inc (y);
    PrevLineNum := LineNum;

    if not ((Y < 0) or (y >= bitmap.height)) then
    begin
      scanline := bitmap.scanline [y];

      for i := 0 to RunLength-1 do
      begin
        m.read (b, sizeOf (b));

        if flipX then
          X0 := X - i
        else
          X0 := X + i;

        if (X0 >= 0) and (X0 < bitmap.width) then
        begin
          c := palette [b];
          scanline^ [X0 * 3 + 0] := c.b;
          scanline^ [X0 * 3 + 1] := c.g;
          scanline^ [X0 * 3 + 2] := c.r;
        end;
      end;
    end
    else
      m.seek (RunLength, soFromCurrent);

    inc (crashPreventCount);
  end;
end;


procedure convertRawTileToBitmap (m : TMemoryStream; bitmap : TBitmap; startX, startY : integer);
var
  rawTileArt : PRawTileArt;
  x, y, i : integer;
  w : word;
  c : TColor24;
  scanline : PByteArray;
begin
  rawTileArt := PRawTileArt (m.memory);
  i := 1;

  for y := 0 to 22 - 1 do
    if not (((startY + y) < 0) or ((startY + y) >= bitmap.height)) then
    begin
      scanline := bitmap.scanline [startY + y];

      for x := startX + 21 - y to startX + 22 + y do
        if (x >= 0) and (x < bitmap.width) then
        begin
          c := convertUOColor24 (rawTileArt [i]);
          scanline^ [x * 3 + 0] := c.b;
          scanline^ [x * 3 + 1] := c.g;
          scanline^ [x * 3 + 2] := c.r;
          inc (i);
        end;
    end;

  for y := 22 to 44 - 1 do
    if not (((startY + y) < 0) or ((startY + y) >= bitmap.height)) then
    begin
      scanline := bitmap.scanline [startY + y];

      for x := (startX + y - 22) to (startX + 43 - (y - 22)) do
        if (x >= 0) and (x < bitmap.width) then
        begin
          c := convertUOColor24 (rawTileArt [i]);
          scanline^ [x * 3 + 0] := c.b;
          scanline^ [x * 3 + 1] := c.g;
          scanline^ [x * 3 + 2] := c.r;
          inc (i);
        end;
    end;
end;


procedure convertRunTileToBitmap (m : TMemoryStream; bitmap : TBitmap; startX, startY : integer);
var
  runTileArt : PRunTileArt;
  x, x0, y, i : integer;
  w : word;
  c : TColor24;
  scanline : PByteArray;
  XOffs, run : word;
begin
  runTileArt := PRunTileArt (m.memory);

  if (runTileArt.width = 0) or (runTileArt.width >= 1024)
   or (runTileArt.height = 0) or (runTileArt.height >= 1024) then
    exit;

  x := 0;
  y := 0;
  m.seek (sizeOf (runTileArt.flag)
          + sizeOf (runTileArt.height)
          + sizeOf (runTileArt.width)
          + runTileArt.height * sizeOf (word)
          + runTileArt.rowOffsets  [y] * sizeOf (word), soFromBeginning);
  while (y < runTileArt.height) do
  begin
    m.read (XOffs, sizeOf (XOffs));
    m.read (run, sizeOf (run));
    if (XOffs + run >= 2048) then
      exit
    else if (XOffs + Run <> 0) then
    begin
      x := x + XOffs;
      //Load Run number of pixels at X (See 1.2 Colors for pixel info)

      if not (((startY + y) < 0) or ((startY + y) >= bitmap.height)) then
      begin
        scanline := bitmap.scanline [startY + y];
        for i := 0 to run - 1 do
        begin
          x0 := (startX + x + i);

          m.read (w, sizeOf (w));
          if (x0 >= 0) and (x0 < bitmap.width) then
          begin
            c := convertUOColor24 (w);
            scanline^ [x0 * 3 + 0] := c.b;
            scanline^ [x0 * 3 + 1] := c.g;
            scanline^ [x0 * 3 + 2] := c.r;
          end;
        end;
      end
      else
        for i := 0 to run - 1 do
          m.read (w, sizeOf (w));

      x := x + run;
    end
    else
    begin
      X := 0;
      y := y + 1;
      m.seek (sizeOf (runTileArt.flag)
              + sizeOf (runTileArt.height)
              + sizeOf (runTileArt.width)
              + runTileArt.height * sizeOf (word)
              + runTileArt.rowOffsets  [y] * sizeOf (word), soFromBeginning);
    end;
  end;
end;


destructor TMapObjectX.destroy;
begin
  if next <> nil then
    next.free;

  inherited;
end;


procedure TMapBlockX.load (uodata : TUOData; x0, y0 : integer);
var
  i, j, x, y : integer;
  mapBlock : TMapBlock;

  mapStatic : PMapStatic;
  mapStatics : PMapStaticArray;
  mapStaticSize : integer;

  runTileArtStream : TMemoryStream;
  runTileArt : PRunTileArt;
begin
  mapX := x0;
  mapY := y0;
  i := mapX * 512 + mapY;

  uodata.loadMapBlockFromFile (mapBlock, i);
  for y := 0 to 7 do
    for x := 0 to 7 do
    begin
      ground [x, y].color := mapBlock.mapCells [y * 8 + x].color;
      ground [x, y].altitude := mapBlock.mapCells [y * 8 + x].altitude;

      ground [x, y].art := uodata.loadBlobFromCache (uodata.artResource, ground [x, y].color);
      ground [x, y].texture := uodata.loadBlobFromCache (uodata.textureResource, ground [x, y].color);
    end;

  mapStaticStream := uodata.loadBlobFromCache (uodata.mapStaticResource, i);
  if assigned (mapStaticStream) and assigned (mapStaticStream.memory) then
  begin
    mapStatics := PMapStaticArray (mapStaticStream.memory);
    mapStaticSize := mapStaticStream.size div sizeOf (TMapStatic);
    for j := 0 to mapStaticSize - 1 do
    begin
      mapStatic := @mapStatics [j];
      if (mapStatic.x >= 0) and (mapStatic.x < 8)
       and (mapStatic.y >= 0) and (mapStatic.y < 8) then
      begin
        runTileArtStream := uodata.loadBlobFromCache (uodata.artResource, mapStatic.color + 16384);
        runTileArt := runTileArtStream.memory;
        if runTileArt <> nil then
          insertMapStatic (uodata, ground [mapStatic.x, mapStatic.y].mapObject, mapStatic, runTileArtStream)
        else
          uodata.unloadBlobFromCache (uodata.artResource, mapStatic.color + 16384);
      end;
    end;
  end;
end;


procedure TMapBlockX.unload (uodata : TUOData);
var
  i, x, y : integer;
  next : TMapObjectX;
begin
  i := mapX * 512 + mapY;

  for y := 0 to 7 do
    for x := 0 to 7 do
    begin
      uodata.unloadBlobFromCache (uodata.artResource, ground [x, y].color + 16384);
      uodata.unloadBlobFromCache (uodata.textureResource, ground [x, y].color + 16384);
      deleteMapObject (uodata, ground [x, y].mapObject);
    end;

  uodata.unloadBlobFromCache (uodata.mapStaticResource, i);
end;


procedure TMapBlockX.insertMapStatic (uodata : TUOData; var next : TMapObjectX;
                                      mapStatic : PMapStatic; runTileArtStream : TMemoryStream);
var
  m : TMapObjectX;
  runTileArt : PRunTileArt;
begin
  runTileArt := runTileArtStream.memory;

  if (next = nil) or (mapStatic.altitude < next.altitude)
   or ((mapStatic.altitude = next.altitude) and (runTileArt.height < next.runTileArt.height)) then
  begin
    m := TMapObjectX.create;
    m.color := mapStatic.color;
    m.altitude := mapStatic.altitude;
    m.runTileArtStream := runTileArtStream;
    m.runTileArt := runTileArt;

    if next = nil then
    begin
      next := m;
    end
    else
    begin
      m.next := next;
      next := m;
    end;
  end
  else
    insertMapStatic (uodata, next.next, mapStatic, runTileArtStream);
end;


procedure TMapBlockX.deleteMapObject (uodata : TUOData; var next : TMapObjectX);
begin
  if next = nil then
    exit
  else
  begin
    deleteMapObject (uodata, next.next);

    uodata.unloadBlobFromCache (uodata.artResource, next.color + 16384);
    next.free;
    next := nil;
  end;
end;


constructor TResource.create (filename, indexFilename : string);
begin
  inherited create;

  IndexStream := TMemoryStream.create;
  CacheStream := TMemoryStream.create;
  Stream := TFileStream.create (filename, fmOpenRead);

  indexStream.loadFromFile (indexFilename);
  indexesSize := indexStream.size div sizeOf (TIndex);
  indexes := indexStream.memory;

  cacheStream.size := indexesSize * sizeOf (TCache);
  fillChar (cacheStream.memory^, cacheStream.size, 0);
  cache := cacheStream.memory;
end;


destructor TResource.destroy;
begin
  deleteCache;

  IndexStream.free;
  CacheStream.free;
  Stream.free;

  inherited;
end;


procedure TResource.clearCache;
var
  index : integer;
begin
  for index := 0 to indexesSize - 1 do
    if (cache [index].data <> nil) and (cache [index].refCount <= 0) then
    begin
      cache [index].data.free;
      cache [index].data := nil;
      cache [index].refCount := 0;
    end;
end;


procedure TResource.deleteCache;
var
  index : integer;
begin
  for index := 0 to indexesSize - 1 do
    if (cache [index].data <> nil) then
    begin
      cache [index].data.free;
      cache [index].data := nil;
      cache [index].refCount := 0;
    end;
end;


constructor TUOData.create;
begin
  inherited;

  loadUOConfig;

  textureResource := TResource.create (resolveFilename ('texmaps.mul'), resolveFilename ('texidx.mul'));
  artResource := TResource.create (resolveFilename ('art.mul'), resolveFilename ('artidx.mul'));
  animResource := TResource.create (resolveFilename ('anim.mul'), resolveFilename ('anim.idx'));
  mapStaticResource := TResource.create (resolveFilename ('statics0.mul'), resolveFilename ('staidx0.mul'));
  multiResource := TResource.create (resolveFilename ('multi.mul'), resolveFilename ('multi.idx'));

  mapStream := TFileStream.create (resolveFilename ('map0.mul'), fmOpenRead);
  radarColorsStream := TMemoryStream.create;

  radarColorsStream.loadFromFile (resolveFilename ('radarcol.mul'));
  radarColorsSize := radarColorsStream.size div sizeOf (word);
  radarColors := pointer (radarColorsStream.memory);
end;


destructor TUOData.destroy;
begin
  textureResource.free;
  artResource.free;
  animResource.free;
  mapStaticResource.free;
  multiResource.free;

  mapStream.free;
  radarColorsStream.free;

  inherited;
end;


procedure TUOData.loadUOConfig;
const
  CdRomDataPathStr = 'CdRomDataPath=';
var
  sl : TStringList;
  i : integer;
begin
  sl := nil;
  try
    sl := TStringList.create;
    sl.loadFromFile ('uo.cfg');
    for i := 0 to sl.count - 1 do
    begin
      if copy (sl [i], 1, length (CdRomDataPathStr)) = 'CdRomDataPath=' then
        cdromdatapath := copy (sl [i], length (CdRomDataPathStr) + 1, length (sl [i]) - length (CdRomDataPathStr));
    end;
  finally
    sl.free;
  end;
end;


function TUOData.resolveFilename (filename : string) : string;
begin
  if fileExists (filename) then
    result := filename
  else if fileExists (cdromdatapath + '\' + filename) then
    result := cdromdatapath + '\' + filename
  else
    raise exception.create (filename + ' not found');
end;


procedure TUOData.loadMapBlockFromFile (var m : TMapBlock; mapIndex : integer);
var
  offset : integer;
begin
  offset := mapIndex * sizeOf (TMapBlock);

  mapStream.seek (offset, soFromBeginning);
  mapStream.read (m, sizeOf (m));
end;


procedure TUOData.loadBlobFromFile (m : TMemoryStream; const resource : TResource; index : integer);
begin
  try
    if (index >= 0)
     and (index < resource.indexesSize)
     and (resource.indexes [index].length > 0) then
    begin
	m.size := resource.indexes [index].length;

	resource.stream.seek (resource.indexes [index].start, soFromBeginning);
	resource.stream.read (PChar (m.memory)^, m.size);
    end;
  except
  end;
end;


function TUOData.loadBlobFromCache (const resource : TResource; index : integer) : TMemoryStream;
begin
  if (index >= 0)
   and (index < resource.indexesSize)
   and (resource.indexes [index].length > 0) then
  begin
    if resource.cache [index].data = nil then
    begin
      resource.cache [index].data := TMemoryStream.create;
      loadBlobFromFile (resource.cache [index].data, resource, index);
    end;

    result := resource.cache [index].data;
    inc (resource.cache [index].refCount);
  end
  else
    result := nil;
end;


procedure TUOData.unloadBlobFromCache (const resource : TResource; index : integer);
begin
  dec (resource.cache [index].refCount);
end;




end.
