unit DXMap;

interface

uses
  Windows, Messages, SysUtils, Classes, DXSprite, DXDraws, Sprites;

type
  THandleNotifyEvent = procedure(Sender: TObject; var Handled: Boolean) of object;

  TDXMap = class(TComponent)
  private
    FMapName, FLibName: ShortString;
    FDXSpriteEngine: TDXSpriteEngine;
    FMapSprite: TMapSprite;
    FGetImageByName: Boolean;
    FDXImageList: TDXImageList;
    FOnImageLibraryNotFound: THandleNotifyEvent;
    function GetMapSprite: TMapSprite;
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation);
  public
    property MapName: ShortString read FMapName write FMapName;
    property LibName: ShortString read FLibName write FLibName;
    property MapSprite: TMapSprite read GetMapSprite write FMapSprite;
    procedure LoadMap(FileName: String);
    procedure SaveMap(FileName: String);
    function GetLibNameFromMapFile(FileName: String): ShortString;
  published
    property SpriteEngine: TDXSpriteEngine read FDXSpriteEngine write FDXSpriteEngine;
    property ImageLibrary: TDXImageList read FDXImageList write FDXImageList;
    property OnImageLibraryNotFound: THandleNotifyEvent read FOnImageLibraryNotFound write FOnImageLibraryNotFound;
    property GetImageByName: Boolean read FGetImageByName write FGetImageByName default false;
  end;

procedure Register;

implementation

var Header: String[6] = 'DXMap1';

procedure Register;
begin
  RegisterComponents('DelphiXEx', [TDXMap]);
end;

{ TDXMap }

function TDXMap.GetLibNameFromMapFile(FileName: String): ShortString;
var Stream: TFileStream;
    h: String[6];
    c: Integer;
    HasMap: Boolean;
    LibFilename: ShortString;
begin
  Stream := TFileStream.Create(FileName, fmOpenRead);
  Stream.Read(h, Length(Header) + 1);
  if h <> HEADER then begin
    raise EFOpenError.Create('Unknown or unsupported fileformat!');
    Stream.Free;
    Exit;
  end;

  Stream.Read(c, SizeOf(c));
  Stream.Read(HasMap, SizeOf(HasMap));

  Stream.Read(LibFilename[0], 1);
  Stream.Read(LibFilename[1], Byte(LibFilename[0]));

  Result := LibFilename;

  Stream.Free;
end;

function TDXMap.GetMapSprite: TMapSprite;
begin
  Result := FMapSprite;
end;

procedure TDXMap.LoadMap(FileName: String);
var Stream: TFileStream;
    h: String[6];
    c, i: Integer;
    Handled, HasMap: Boolean;
    Folder: String;
    Sprite: TObjectSprite;
begin

  if not Assigned (FDXImageList) then Exit;
  if not Assigned (FDXSpriteEngine) then Exit;

  FMapName := Filename;
  Stream := TFileStream.Create(FMapName, fmOpenRead);
  Stream.Read(h, Length(Header) + 1);
  if h <> Header then begin
    raise EFOpenError.Create('Unknown or unsupported fileformat!');
    Stream.Free;
    Exit;
  end;

  Stream.Read(c, SizeOf(c));
  Stream.Read(HasMap, SizeOf(HasMap));

  Stream.Read(FLibName[0], 1);
  Stream.Read(FLibName[1], Byte(FLibName[0]));

  Folder := ExtractFileDir(FMapName);
  FLibName := IncludeTrailingPathDelimiter(Folder) + ExtractFilename(FLibName);
  if not FileExists(FLibName) then
    if Assigned(FOnImageLibraryNotFound) then begin
      Handled := false;
      FOnImageLibraryNotFound(Self, Handled);
      if not Handled then begin
        Stream.Free;
        FMapName := EmptyStr;
        FLibName := EmptyStr;
        Exit;
      end;
  end else begin
    raise EFOpenError.CreateFmt('File "%s" not found!', [FLibName]);
    Stream.Free;
    FMapName := EmptyStr;
    FLibName := EmptyStr;
    Exit;
  end;

  FDXImageList.Items.LoadFromFile(FLibName);

  try
    for i := 1 to c do begin
      Sprite := TObjectSprite.Create(FDXSpriteEngine.Engine);
      Sprite.GetImageByName := FGetImageByName;
      Sprite.LoadData(FDXImageList, Stream);
    end;
    if HasMap then begin
      if not Assigned(FMapSprite) then begin
        FMapSprite := TMapSprite.Create(FDXSpriteEngine.Engine);
        FMapSprite.Name := 'BackgroundMap';
        FMapSprite.Z := -1;
        FMapSprite.Image := nil;
        FMapSprite.SetMapSize(1, 1);
      end;
      FMapSprite.LoadData(FDXImageList, Stream)
    end;
  except
    raise EFOpenError.Create('Error reading file! File may be corrupt.');
    Stream.Free;
    FMapName := EmptyStr;
    FLibName := EmptyStr;
    Exit;
  end;

  Stream.Free;
end;

procedure TDXMap.SaveMap(FileName: String);
var Stream: TFileStream;
    i, c: Integer;
    HasMap: Boolean;
    Sprite: TSprite;
    lf: ShortString;
begin

  if not Assigned (FDXImageList) then Exit;
  if not Assigned (FDXSpriteEngine) then Exit;

  Stream := TFileStream.Create(Filename, fmCreate);
  Stream.Write(Header, Length(Header) + 1);

  c := 0;

  for i := 0 to FDXSpriteEngine.Engine.AllCount - 1 do begin
    Sprite := FDXSpriteEngine.Engine.Items[i];
    if Sprite is TObjectSprite then Inc (c);
  end;

  Stream.Write(c, SizeOf(c));
  HasMap := Assigned(FMapSprite); // and Assigned(MapSprite.Image);
  Stream.Write(HasMap, SizeOf(HasMap));
  lf := ExtractFilename(FLibName);
  Stream.Write(lf, Length(lf) + 1);
  for i := 0 to FDXSpriteEngine.Engine.AllCount - 1 do begin
    Sprite := FDXSpriteEngine.Engine.Items[i];
    if Sprite is TObjectSprite then
      with Sprite as TObjectSprite do
        TObjectSprite(Sprite).StoreData(Stream);
  end;

  if HasMap then
    with FMapSprite do
      FMapSprite.StoreData(Stream);

  Stream.Free;
end;

procedure TDXMap.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation=opRemove) and (AComponent=FDXSpriteEngine) then
    FDXSpriteEngine := nil;
  if (Operation=opRemove) and (AComponent=FDXImageList) then
    FDXImageList := nil;
end;

end.
