unit Eq2maped1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, TFlatMemoUnit, TFlatListBoxUnit, TFlatEditUnit,
  ActnList, Menus, TFlatSpinEditUnit, ComCtrls, RzTreeVw, JvSimpleXml,
  TFlatComboBoxUnit, zResStorage, eq2mapedfn;

type
  // ===
  TShapeList = class(TList)
  private
  Owner : TComponent;
  protected
  public
  parent : TWinControl;
  constructor Create(AOwner : TComponent);
  Destructor Destroy; override;
  function Add: TShape;
  function GetItem(idx : integer):TShape;
  procedure Clear; override;
  function GetIndex(Shape : TShape):integer;
  end;
  // ===
  TForm1 = class(TForm)
    Panel1: TPanel;
    Image1: TImage;
    xmlscan: TJvSimpleXML;
    OpenDlg: TOpenDialog;
    Panel2: TPanel;
    jvTreeView1: TRzTreeView;
    Label1: TLabel;
    Label3: TLabel;
    FlatEdit3: TFlatEdit;
    Label2: TLabel;
    FlatMemo1: TFlatMemo;
    Label4: TLabel;
    MainMenu1: TMainMenu;
    ActionList1: TActionList;
    FileOpen: TAction;
    FileSave: TAction;
    N1: TMenuItem;
    N2: TMenuItem;
    N3: TMenuItem;
    FileExit: TAction;
    N4: TMenuItem;
    posbtn: TShape;
    FlatEdit4: TFlatEdit;
    FileMAP: TAction;
    openmap: TOpenDialog;
    ed_locX: TFlatSpinEditInteger;
    ed_locY: TFlatSpinEditInteger;
    N5: TMenuItem;
    MAPADD: TAction;
    A1: TMenuItem;
    cb_icontype: TFlatComboBox;
    FlatEdit1: TFlatEdit;
    MAPPOI: TAction;
    POIO1: TMenuItem;
    Timer1: TTimer;
    MAPLOCAUTO: TAction;
    Label5: TLabel;
    N7: TMenuItem;
    N8: TMenuItem;
    MapAddZonerect: TAction;
    Zone1: TMenuItem;
    M2: TMenuItem;
    N9: TMenuItem;
    N10: TMenuItem;
    MAPDEL: TAction;
    D1: TMenuItem;
    StatusBar1: TStatusBar;
    FileSaveBOM: TAction;
    N6: TMenuItem;
    UTF8U1: TMenuItem;
    FileSaveAs: TAction;
    M1: TMenuItem;
    SaveAsDlg: TSaveDialog;
    Help1: TMenuItem;
    DataFiles: TzResStorage;
    FileOpenZone: TAction;
    N11: TMenuItem;
    procedure xmlscanDecodeValue(Sender: TObject; var Value: String);
    procedure xmlscanEncodeValue(Sender: TObject; var Value: String);
    procedure FormCreate(Sender: TObject);
    procedure ApplyData;
    procedure jvTreeView1Change(Sender: TObject; Node: TTreeNode);
    procedure FileOpenExecute(Sender: TObject);
    procedure FileSaveExecute(Sender: TObject);
    procedure FlatEdit3Change(Sender: TObject);
    procedure FileExitExecute(Sender: TObject);
    procedure FlatEdit4Change(Sender: TObject);
    procedure FileMAPExecute(Sender: TObject);
    procedure MAPADDExecute(Sender: TObject);
    procedure MAPPOIUpdate(Sender: TObject);
    procedure MAPPOIExecute(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ShapeMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure Timer1Timer(Sender: TObject);
    procedure jvTreeView1Changing(Sender: TObject; Node: TTreeNode;
      var AllowChange: Boolean);
    procedure Image1DragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure ShapeDragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure ShapeDragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure MAPLOCAUTOExecute(Sender: TObject);
    procedure MAPLOCAUTOUpdate(Sender: TObject);
    procedure MapAddZonerectExecute(Sender: TObject);
    procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure posbtnMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure ShapeMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure MAPDELUpdate(Sender: TObject);
    procedure MAPDELExecute(Sender: TObject);
    procedure FileSaveBOMExecute(Sender: TObject);
    procedure FileSaveBOMUpdate(Sender: TObject);
    procedure Help1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    ismodified : boolean;
    isLoaded : boolean;
    ischange : boolean;
    isUTF8Save : boolean;
    automapcalc : boolean;
    calcpathloc : boolean;
    MapPoint : TShapeList;
    // map information
    IconFileName : string;
    MapSource : string;
    MapZoneRect : TRect;
    MapZoneName : string;
    MapWidth, MapHeight : integer;
    MapZonedef : TStringList;
    // ===
    function LoadDDS(FileName : string):TBitmap;
    function LoadXML(FileName : string):boolean;
    function ReFreshView:string;
    procedure LoadINI;
    procedure SetShape(ix : integer; isAdd : boolean);
    function LoadMapFileInfo(mapname : string):boolean;
    function GetMapZoneLocPath(x, y : integer; isbar : boolean):string;
    function GetMapZonePathLoc(pathloc : string; var x,y : integer):string;
    procedure SetBarText(ix : integer; str : string);
  end;

var
  Form1: TForm1;
  exepath : string;
  eq2uipath : string;
  ddsgamma : extended = 0.7; // lower => bright

const
        pathdmp = '..\Defaults\'+pathmap;
        PgName = 'Eq2MAP Editor';

implementation

{$R *.DFM}

uses imaging, imagingtypes, cUnicodeCodecs, inifiles, poipost1, addpointf, addzone, filectrl, help, ZoneList;

// TShapeList;
constructor TShapeList.Create(AOwner : TComponent);
begin
        inherited Create;
        Owner := AOwner;
        Parent := nil;
end;

destructor TShapeList.Destroy;
begin
        Clear;
        inherited Destroy;
end;

procedure TShapeList.Clear;
var
        i : integer;
        tmpshp : TShape;
begin
        if self.count>0 then
        for i:=0 to pred(self.count) do begin
                tmpshp := TShape(self.Items[i]);
                if assigned(tmpshp) then tmpshp.free;
        end;
        inherited Clear;
end;

function TShapeList.GetItem(idx : integer):TShape;
begin
        Result := TShape(items[idx]);
end;

function TShapeList.Add:TShape;
begin
        Result := TShape.Create(Owner);
        if Result<>nil then begin
                inherited add(Pointer(Result));
                Result.Parent := Parent;
        end;
end;

function TShapeList.GetIndex(Shape : TShape):integer;
begin
        Result := IndexOf(Pointer(Shape));
end;


function TForm1.LoadDDS(FileName : string):TBitmap;
var
        Img : TImageData;
        i : integer;
begin
        Result := nil;
        InitImage(Img);
        try
        if imaging.LoadImageFromFile(Filename,Img) then begin
                Result := TBitmap.Create;
                try
                Result.HandleType := bmDIB;
                Result.PixelFormat := pf32bit;
                Result.Width  := Img.Width;
                Result.Height := Img.Height;
                ConvertImage(Img,ifA8R8G8B8);
                for i:=0 to pred(Img.Height) do begin
                        Move(PColor32RecArray(Img.Bits)[I * Img.Width],Result.ScanLine[i]^,Img.Width * GetPixelBytes(Img.Format));
                end;
                GCorrection(result,ddsgamma);
                except
                        Result.Free;
                        Result := nil;
                end;
        end else begin
                result := TBitmap.Create;
                try
                Result.HandleType := bmDIB;
                Result.PixelFormat := pf32bit;
                Result.Width := 512;
                Result.Height:= 512;
                Result.Canvas.FillRect(RECT(0,0,511,511));
                except
                        Result.Free;
                        Result := nil;
                end;
        end;
        finally
                FreeImage(Img);
        end;
end;

procedure TForm1.SetShape(ix : integer; isAdd : boolean);
var
        so : string;
        sx, sy, sw, sh : integer;
        ss : TShape;
begin
        // draw Mappoint
        with xmlscan.Root.Items.Item[ix].Properties do begin
                so := ItemNamed['Location'].Value;
                try
                sx := strtoint(copy(so,0,pos(',',so)-1));
                sy := strtoint(copy(so,pos(',',so)+1,5));
                except
                        sx := 0; sy := 0;
                end;
                so := ItemNamed['Size'].Value;
                try
                sw := strtoint(copy(so,0,pos(',',so)-1));
                sh := strtoint(copy(so,pos(',',so)+1,5));
                except
                        sw := 12; sh := 12;
                end;
                dec(sw,2); dec(sh,2);
                //
                try
                if isAdd then ss := MapPoint.Add else ss := MapPoint.GetItem(ix);
                ss.Top  := sy - sw shr 1;
                ss.Left := sx - sh shr 1;
                ss.Width := sw;
                ss.Height := sh;
                ss.Shape := stEllipse;
                so := ItemNamed['IconStyle'].Value;
                if so = 'CLST' then ss.Brush.Color := clr_clst else
                 if so = 'LOC' then ss.Brush.Color := clr_loc else
                 if so = 'MOB' then ss.Brush.Color := clr_mob else
                 if so = 'NAME' then ss.Brush.Color := clr_name else
                 if so = 'NPC' then ss.Brush.Color := clr_npc else
                 if so = 'QST' then ss.Brush.Color := clr_qst else
                        ss.Brush.Color := clr_zone;
                ss.OnMouseDown := ShapeMouseDown;
                ss.OnDragOver := ShapeDragOver;
                ss.OnDragDrop := ShapeDragDrop;
                ss.OnMouseMove := ShapeMouseMove;
                ss.ShowHint := true;
                //if not isAdd then ss.Refresh;
                ss.Hint := ItemNamed['Tooltip'].Value;
                except
                end;
        end;
end;

function TForm1.ReFreshView:string;
var
        i : integer;
        tmp : TTreenode;
begin
        isChange := true;
        try
        jvTreeView1.Items.BeginUpdate;
        jvTreeView1.Items.Clear;
        MapPoint.Clear;
        FlatEdit4.Text := xmlscan.Root.Properties.ItemNamed['1location'].Value;
        FlatEdit4.Modified := false;
        Result  := xmlscan.Root.Properties.ItemNamed['Name'].Value;
        MapZoneName := Result;
        try
        for i:=0 to pred(xmlscan.Root.Items.Count) do begin
                with xmlscan.Root.Items.Item[i].Properties do begin
                tmp := jvTreeView1.Items.Add(nil,ItemNamed['Name'].Value);
                if tmp<>nil then begin
                tmp.Data := pointer(i);
                jvTreeView1.Items.AddChild(tmp,ItemNamed['Location'].Value);
                jvTreeView1.Items.AddChild(tmp,ItemNamed['pathfindlocation'].Value);
                jvTreeView1.Items.AddChild(tmp,ItemNamed['IconStyle'].Value);
                jvTreeView1.Items.AddChild(tmp,ItemNamed['Tooltip'].Value);
                SetShape(i,true);
                end;
                end;
        end;
        except
        end;
        jvTreeView1.Items.EndUpdate;
        posbtn.Top := 0;
        posbtn.Left:= 0;
        except
        end;
        isChange := false;
end;

function TForm1.LoadXML(FileName : string):Boolean;
var
        sFile : TFileStream;
        buff : array[0..3] of byte;
        isSafe : boolean;
        itm : TJvSimpleXMLProp;
        xs : string;
begin
        // load
        Result := false;
        isSafe := false;
        MapLocAuto.Checked := false;
        AutoMapCalc := false;
        fillchar(buff,3,0);
        jvTreeView1.Items.BeginUpdate;
        jvTreeView1.Items.Clear;
        jvTreeView1.Items.EndUpdate;
        xmlScan.Prolog.Clear;
        xmlScan.Root.Clear;
        try
        sFile := TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
        try
                        // check header
                if 3<>sFile.Read(buff,3) then raise exception.Create('Error : Bad File');
                if (buff[0]<>$EF) or (buff[1]<>$BB) or (buff[2]<>$BF) then sFile.Position := 0;
                XmlScan.LoadFromStream(sFile);
                // patch items = bug fix in icon definition file
                itm := xmlscan.Root.Properties.ItemNamed['1Location'];
                if itm<>nil then itm.Name := '1location';
                // View Map
                xs := ReFreshView;
                isSafe := true;
                Result := true;
                // get mapwidth and height
                loadMapFileinfo(xs);
                try
                xs := xmlscan.Root.Properties.ItemNamed['Size'].Value;
                MapWidth  := Strtoint(copy(xs,1,pos(',',xs)-1));
                MapHeight := StrtoInt(copy(xs,pos(',',xs)+1,3));
                except
                        MapWidth := 436; MapHeight := 506;
                end;
        finally
                sFile.Free;
                if not isSafe then raise exception.Create('Error : Loading File');
        end;
        except
        on e:exception do ShowMessage(e.message);
        end;
end;

procedure TForm1.xmlscanDecodeValue(Sender: TObject; var Value: String);
var
        sx : Widestring;
begin
        sx := UTF8StringToWideString(Value);
        Value := DecodeXML(sx);
end;

procedure TForm1.xmlscanEncodeValue(Sender: TObject; var Value: String);
var
        sx : String;
begin
        sx := WideStringToUTF8String(Value);
        Value := EncodeXML(sx);
end;

procedure TForm1.LoadINI;
var
        xf : TIniFile;
        sr : string;
        buf : TStringList;
        i : integer;
begin
        sr := ChangeFileExt(Application.EXEName,'.INI');
        try
        if FileExists(sr) then begin
        xf := TINIFile.Create(sr);
        try
                ddsgamma := xf.ReadFloat('CONFIG','MAPGAMMA',0.7);
                if xf.ValueExists('CONFIG','PATH') then
                        EXEPath := xf.ReadString('CONFIG','PATH',ExtractFilePath(Application.EXEName));
                // load Mapzonerect definition
                // mapname, mapzone
                if xf.SectionExists('MAPZONEDEF') then begin
                        buf := TStringList.Create;
                        try
                        xf.ReadSectionValues('MAPZONEDEF',buf);
                        if buf.Count>0 then
                        for i:=0 to pred(buf.Count) do begin
                                MapZoneDef.Add(trim(splitstr(buf.Strings[i],'=',sr)));
                                MapZoneDef.Add(trim(sr));
                        end;
                        finally
                        buf.free;
                        end;
                end;
        finally
                xf.free;
        end;
        end;
        except
        end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
        ix : integer;
begin
        isLoaded := false;
        ischange := false;
        isUTF8Save := true;
        automapcalc := false;
        calcpathloc := false;
        MapZoneDef := TStringList.Create;
        for ix:=0 to 33 do MapZoneDef.Add(MapOther[ix]);
        ix := GetSystemDefaultLangID;
        if lo(ix)<>LANG_KOREAN then begin
                isUTF8Save := false;
                Font.Name := 'MS Sans Serif';
                Font.Size := 8;
                FileOpen.Caption := '&Open Icon Page';
                FileOpenZone.Caption := 'Open Zone(&L)';
                FileSave.Caption := '&Save Icon Page';
                FileExit.Caption := 'E&xit';
                FileMap.Caption := 'Open &Map File';
                FileSaveBOM.Caption := 'Use UTF&8BOM Header';
                FileSaveAs.Caption := 'Save &As Icon Page';
                //dxbutton3.Caption:= 'Apply';
                N1.Caption := '&File';
                N5.Caption := '&POI';
                Help1.Caption := '&About';
                MAPADD.Caption := '&Add POI';
                MAPDEL.Caption := '&Delete POI';
                MAPPOI.Caption := 'Post P&OI to maps.eq2interface.com';
                MAPLocAuto.Caption := 'Auto&calc Location';
                MapAddZonerect.Caption := 'Add &Zonerect';
                Label1.Caption := 'Loc ';
                Label2.Caption := 'Name';
                Label3.Caption := 'Type';
                Label4.Caption := 'Path';
        end;
        Exepath := ExtractFilePath(Application.ExeName);
        // Create Data File
        try
        if not FileExists(EXEPath+'zonenames.dat') then DataFiles.SaveToFile(EXEPath+'zonenames.dat','zonenames.txt');
        except
        end;
        // ===
        try
                loadINI;
        except
        end;
        ix := pos('\UI\',uppercase(exepath));
        if ix > 0 then eq2uipath := copy(exepath,1,ix+3) + 'Default\' else eq2uipath := ExePath;
        openMap.InitialDir := EXEPath+pathmap;
        mappoint := TShapeList.Create(self);
        mappoint.parent := panel1;
end;

function GetTagptr(itm :tjvSimpleXMLProps; Name : string; sr : TStringList):boolean;
var
        idx : TjvSimpleXMLProp;
begin
        Result := false;
        idx := itm.ItemNamed[Name];
        if idx<>nil then begin
                sr.Add(idx.Name);
                sr.Add(idx.Value);
                result := true;
        end;
end;

procedure TForm1.ApplyData;
var
        ix, sw ,sh : integer;
        sel : TTreeNode;
        sl, sp : string;
begin
        if not isLoaded then exit;
        // ===
        sel := jvTreeView1.Selected;
        if sel=nil then exit;
        if sel.Parent<>nil then sel := sel.Parent;
        ix := integer(sel.data);

        if ix <> -1 then begin
        ischange := true;
        with xmlscan.Root.Items.Item[ix].Properties do begin
        try
        ItemNamed['Tooltip'].Value := FlatMemo1.Text;
        ItemNamed['Name'].Value := FlatEdit3.Text;
        ItemNamed['IconStyle'].Value := cb_icontype.Text;
        ItemNamed['Location'].Value := ed_locX.Text +','+ed_locY.Text;
        //
        if not calcpathloc then sp := GetMapZoneLocPath(ed_locX.Value, ed_locY.Value, false);
        if AutoMapCalc then if not calcpathloc then FlatEdit1.Text := sp;
        //
        ItemNamed['pathfindlocation'].Value := FlatEdit1.Text;
        if calcPathloc then begin
                sp := GetMapZonePathLoc(FlatEdit1.Text, sw, sh);
                if sp<>'' then FlatEdit1.Text := sp;
        end;
        if AutoMapCalc then if calcPathloc then
                if sp<>'' then begin
                        ed_locX.Value := sw;
                        ed_locy.Value := sh;
                        ItemNamed['Location'].Value := ed_locX.Text +','+ed_locY.Text;
                end;
        if calcPathloc then sp := inttostr(sw) +','+ inttostr(sh);
        Label5.Caption := sp;
        if cb_icontype.Text='ZONE' then sl := '14,14' else sl := '12,12';
        ItemNamed['Size'].Value := sl;
        ItemNamed['ScrollExtent'].Value := sl;
        //
        Sel.DeleteChildren;
        Sel.Text := FlatEdit3.Text;
        jvTreeView1.Items.AddChild(sel,ItemNamed['Location'].Value);
        jvTreeView1.Items.AddChild(sel,ItemNamed['pathfindlocation'].Value);
        jvTreeView1.Items.AddChild(sel,ItemNamed['IconStyle'].Value);
        jvTreeView1.Items.AddChild(sel,ItemNamed['Tooltip'].Value);
        //
        setShape(ix,false);
        // set focus pointer
        try
        sw := strtoint(splitstr(sl,',',sp));
        sh := strtoint(sp);
        posbtn.Width := sw;
        posbtn.Height:= sh;
        posbtn.Left := ed_locX.Value - sw shr 1;
        posbtn.Top := ed_locy.Value - sh shr 1;
        except
        end;

        FlatMemo1.Modified := false;
        FlatEdit3.Modified := false;
        //FileSave.Enabled := true;
        isModified := false;
        //dxbutton3.Enabled := false;
        except
        end;
        end; // with
        calcpathLoc := false;
        ischange := false;
        end;
end;

procedure TForm1.jvTreeView1Change(Sender: TObject; Node: TTreeNode);
var
        ix, x, y, sw, sh : integer;
        st, sx : string;
        sel : TTreeNode;
begin
        sel := jvTreeView1.Selected;
        if sel=nil then exit;
        if sel.Parent<>nil then exit;
        ix := integer(sel.data);
        if ix <> -1 then begin
                ischange := true;
                try
                st := xmlscan.Root.Items.Item[ix].Properties.ItemNamed['Location'].Value;
                x := strtoint(copy(st,0,pos(',',st)-1));
                y := strtoint(copy(st,pos(',',st)+1,5));
                ed_locX.Value := x;
                ed_locY.Value := y;
                cb_icontype.ItemIndex := cb_icontype.Items.IndexOf(xmlscan.Root.Items.Item[ix].Properties.ItemNamed['IconStyle'].Value);
                FlatEdit3.Text := xmlscan.root.items.item[ix].Properties.ItemNamed['Name'].Value;
                FlatEdit1.Text := xmlscan.Root.Items.Item[ix].Properties.ItemNamed['pathfindlocation'].Value;
                FlatMemo1.Text := xmlscan.root.items.item[ix].Properties.ItemNamed['Tooltip'].Value;
                sx := xmlscan.root.items.item[ix].Properties.ItemNamed['Size'].Value;
                sw := strtoint(copy(sx,0,pos(',',sx)-1));
                sh := strtoint(copy(sx,pos(',',sx)+1,2));
                posbtn.Width := sw;
                posbtn.Height:= sh;
                posbtn.Left := x - sw shr 1;
                posbtn.Top := y - sh shr 1;
                // posbtn.Visible := true;
                except
                end;
                FlatMemo1.Modified := false;
                FlatEdit3.Modified := false;
                ed_locX.Modified := false;
                ed_locY.Modified := false;
                ischange := false;
                //dxbutton3.Enabled := false;
        end;
end;

procedure TForm1.FileOpenExecute(Sender: TObject);
var
        fr : string;
        tb : TBitmap;
        zf : TForm7;
        ret : integer;
begin
        if TAction(Sender).Name <> 'FileOpenZone' then begin
        if DirectoryExists(EXEPath+pathico) then opendlg.InitialDir := EXEPath+pathico;
        if not openDlg.Execute then exit;
        end else begin
                // fileopenzone
                ret := mrCancel;
                try
                zf := TForm7.Create(self);
                try
                ret := zf.ShowModal;
                if ret = mrOk then opendlg.FileName := EXEPath+pathico+zf.ZoneFileName;
                except
                end;
                zf.free;
                except
                end;
                if ret <> mrOk then exit;
        end;
                if not FileExists(opendlg.FileName) then begin
                        showmessage('File "'+ExtractFileName(opendlg.FileName)+'" is not found.');
                        exit;
                end;
                isLoaded := false;
                IconFileName := opendlg.FileName;
                self.caption := PgName + ' ['+ EXtractFileName(IconFileName)+']';
                isLoaded := LoadXML(IconFileName);
                // ===
                if isLoaded then begin
                fr := EXEPath + mapsource;
                tb := nil;
                if not FileExists(fr) then fr := eq2uipath + mapsource;
                if FileExists(fr) then tb := LoadDDS(fr) else fr := ChangeFileExt(IconFileName,'.DDS');
                if FileExists(fr) then tb := LoadDDS(fr);
                if tb=nil then begin
                        tb := TBitmap.Create;
                        try
                        tb.HandleType := bmDIB;
                        tb.PixelFormat := pf32bit;
                        tb.Width := 512;
                        tb.Height:= 512;
                        except
                                tb.free;
                                tb := nil;
                        end;
                end;
                Image1.Picture.Bitmap.Assign(tb);
                if tb<>nil then tb.free;
                ed_locX.Value := 0;
                ed_locY.Value := 0;
                cb_icontype.ItemIndex := -1;
                FlatEdit3.Text := '';
                FlatEdit1.Text := '';
                FlatMemo1.Text := '';
                MapAdd.Enabled := true;
                // isLoaded := true;
                ismodified := false;
                end; // if isloaded
end;

procedure TForm1.FileSaveExecute(Sender: TObject);
var
        sname : string;
        sFile : TFileStream;
        mFile : TMemoryStream;
        isSafe : boolean;
        firstCR : boolean;
        rbyte : byte;
        pData : PByteArray;
        //i : integer;
        //itm : TjvSimpleXMLProps;
begin
        if not isLoaded then begin
                ShowMessage('No File Loaded');
                exit;
        end;
        if isModified then begin timer1.Enabled := false; ApplyData; end;
        sname := XmlScan.Root.Properties.ItemNamed['Name'].Value;
        // Refine Tags
        {for i:=0 to pred(XmlScan.Root.Items.Count) do begin
                if XmlScan.Root.Items.Item[i].Name = 'Icon' then begin
                        itm := XmlScan.Root.Items.Item[i].Properties;
                        SortItems(itm);
                end;
        end;}
        // Save As
        if TAction(Sender).Name = 'FileSaveAs' then begin
                SaveAsDlg.InitialDir := ExtractFilePath(IconFileName);
                SaveAsDlg.FileName := ExtractFileName(IconFileName);
                if SaveAsDlg.Execute then begin
                        IconFileName := SaveAsDlg.FileName;
                end else exit;
        end;
        // save to stream first
        isSafe := false; firstCR := false;

        mFile := TMemoryStream.Create;
        try
                XMLScan.SaveToStream(mFile);
                mFile.Position := 0;
                sFile := TFileStream.Create(IconFileName,fmCreate);
                try
                    if isUTF8Save then begin
                        // Save to UIBuilder Format
                        if 3<>sFile.Write(tagUTF8BOM,3) then raise exception.Create('Error');
                        while not firstCR do begin
                                if 1<>mFile.Read(rbyte,1) then break;
                                if rbyte=$0d then begin
                                        if 1<>mFile.Read(rbyte,1) then raise exception.Create('Error');
                                        rbyte := $0a;
                                        if 1<>sFile.Write(rbyte,1) then raise exception.Create('Error');
                                        firstCR := true;
                                end else begin
                                        if 1<>sFile.Write(rbyte,1) then raise exception.Create('Error');
                                end;
                        end;
                    end; // if isutf8
                        pData := mFile.Memory;
                        sFile.Write(pData^[mFile.Position],mFile.Size - mFile.Position);
                        isSafe := true;
                finally
                        sFile.Free;
                        if not isSafe then raise exception.Create('Error');
                end;
        //
        //FileSave.Enabled := false;
        finally
                mFile.Free;
                if not isSafe then Showmessage('Error in saving file');
        end;
end;

procedure TForm1.FlatEdit3Change(Sender: TObject);
begin
        if isLoaded then //dxButton3.Enabled := true;
        if not ischange then
        begin
                isModified := true;
                Timer1.Enabled := false;
                Timer1.Enabled := True;
                if Sender.ClassType = TFlatEdit then
                        if TFlatEdit(Sender).Name = 'FlatEdit1' then calcpathloc := true else calcpathloc := false;
        end;
end;

procedure TForm1.FileExitExecute(Sender: TObject);
begin
        close;
end;

procedure TForm1.FlatEdit4Change(Sender: TObject);
begin
        if isLoaded then
        if not isChange then
        if Flatedit4.Modified then begin
                xmlscan.Root.Properties.ItemNamed['1location'].Value := FlatEdit4.Text;
                FlatEdit4.Modified := false;
                //FileSave.Enabled := true;
        end;

end;

procedure TForm1.FileMAPExecute(Sender: TObject);
var
        TB : TBitmap;
begin
        if DirectoryExists(EXEPath + pathmap) then OpenMap.InitialDir := EXEPath + pathmap;
        if OpenMap.Execute then begin
                tb := nil;
                try
                if FileExists(OpenMap.FileName) then tb := LoadDDS(OpenMap.FileName);
                if tb=nil then begin
                        tb := TBitmap.Create;
                        try
                        tb.HandleType := bmDIB;
                        tb.PixelFormat := pf32bit;
                        tb.Width := 512;
                        tb.Height:= 512;
                        except
                                tb.free;
                                tb := nil;
                        end;
                end;
                Image1.Picture.Bitmap.Assign(tb);
                if tb<>nil then tb.free;
                except
                        ShowMessage('Map Loading Fail.');
                end;
        end;
end;

procedure TForm1.MAPADDExecute(Sender: TObject);
var
        tmp : TjvsimpleXMLElemClassic;
        fmadd : TForm3;
        so : string;
begin
        if isLoaded then begin
                fmadd := TForm3.Create(self);
                try
                if mrOK = fmadd.ShowModal then begin
                        try
                        tmp := xmlscan.Root.Items.add('Icon');
                        tmp.Properties.Add('IconStyle',fmadd.cb_icontype.Text);
                        tmp.Properties.Add('IconType','map');
                        tmp.Properties.Add('Location',fmadd.ed_locx.text+','+fmadd.ed_locy.text);
                        tmp.Properties.Add('Name',fmadd.ed_name.Text);
                        tmp.Properties.Add('OnHoverIn','Parent.Parent.Parent.Parent.WC_Titlebar.QM_LocEntry.QM_LocText.LocalText=pathfindlocation');
                        tmp.Properties.Add('pathfindlocation',fmadd.ed_path.Text);
                        // if 14 by 14 size
                        if fmadd.cb_icontype.Text = 'ZONE' then so := '14,14' else so := '12,12';
                        tmp.Properties.Add('ScrollExtent',so);
                        tmp.Properties.Add('Size',so);
                        tmp.Properties.Add('Tooltip',fmadd.ed_tooltip.Text);
                        tmp.Properties.Add('TreatAsButton','true');
                        ReFreshView;
                        //FileSave.Enabled := true;
                        except
                                Showmessage('Cannot add point');
                        end;
                end;
                finally
                        fmadd.free;
                end;
        end;
end;

procedure TForm1.MAPPOIUpdate(Sender: TObject);
var
        sel : TTreenode;
begin
        sel := jvTreeView1.Selected;
        if sel = nil then MAPPOI.Enabled := false else begin
                if isLoaded and (sel.Parent=nil) then MAPPOI.Enabled := true else MAPPOI.Enabled := false;
        end;
end;

procedure TForm1.MAPPOIExecute(Sender: TObject);
var
        sform : TForm2;
        sel : TTreenode;
        ix : integer;
begin
        sel := jvTreeView1.Selected;
        if sel=nil then exit;
        if sel.Parent<>nil then exit;
        ix := integer(sel.data);
        try
                sform := TForm2.Create(self);
        try
                sform.ed_name.Text := xmlscan.Root.Items[ix].Properties.ItemNamed['Name'].Value;
                sform.SetType(xmlscan.Root.Items[ix].Properties.ItemNamed['IconStyle'].Value);
                sform.ed_zone.Text := xmlscan.Root.Properties.ItemNamed['1location'].Value;
                sform.ed_pathloc.Text := xmlscan.Root.Items[ix].Properties.ItemNamed['pathfindlocation'].Value;
                sform.ed_desc.Text := xmlscan.Root.Items[ix].Properties.ItemNamed['Tooltip'].Value;
                sform.ShowModal;
        finally
                sform.Free;
        end;
        except
                Showmessage('Summit Fail.');
        end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
        MapZoneDef.Free;
        MapPoint.Free;
end;

procedure TForm1.ShapeMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
        i, ix : integer;
begin
        if (button = mbLeft) or (button = mbRight) then begin
                ix := Mappoint.GetIndex(TShape(Sender));
                if ix > -1 then begin
                        if jvTreeView1.Items.Count>0 then
                        for i:=0 to pred(jvTreeView1.Items.Count) do begin
                                if integer(jvTreeView1.Items.Item[i].Data) = ix then
                                        if jvTreeView1.Items.Item[i].Parent = nil then
                                begin
                                                jvTreeView1.Items.Item[i].Selected := false;
                                                jvTreeView1.Items.Item[i].Selected := true;
                                                break;
                                end;
                        end;
                TShape(Sender).BringtoFront;
                if (ssShift in Shift) or (Button = mbRight) then begin
                        posbtn.Visible := false;
                        TShape(Sender).BeginDrag(True);
                        end;
                end;
        end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
        Timer1.Enabled := false;
        applydata;
end;

procedure TForm1.jvTreeView1Changing(Sender: TObject; Node: TTreeNode;
  var AllowChange: Boolean);
begin
        if isLoaded then
                if ismodified then begin
                        Timer1.Enabled := false;
                        applydata;
                end;
end;

procedure TForm1.Image1DragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
var
        ss : TShape;
begin
        if Source is TShape then
                if TShape(Source).Name <> 'posbtn' then begin
                        accept := true;
                        ss := Source as TShape;
                        ss.left := x - ss.width shr 1;
                        ss.top := y - ss.height shr 1;
                        ed_locx.value := x;
                        ed_locy.value := y;
                        if State = dsDragEnter then posbtn.Visible := false;
                end;
end;

procedure TForm1.ShapeDragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
var
        ss : TShape;
begin
        if sender = Source then begin
        Accept := true;
        ss := Source as TShape;
        ss.Left := ss.Left + x - ss.Width shr 1;
        ss.Top := ss.Top + y - ss.Height shr 1;
        ed_locx.Value := ss.Left + ss.width shr 1;
        ed_locy.Value := ss.top + ss.height shr 1;
        if State = dsDragLeave then begin
                posbtn.Left := ss.left -(posbtn.width - ss.width) shr 1;
                posbtn.Top := ss.top -(posbtn.height - ss.height) shr 1;
                posbtn.Visible := true;
        end else if State = dsDragEnter then posbtn.Visible := false;
        end;
end;

procedure TForm1.ShapeDragDrop(Sender, Source: TObject; X, Y: Integer);
begin
        if Sender = Source then begin
        posbtn.Left := ed_locx.value - posbtn.width shr 1;
        posbtn.Top := ed_locy.value - posbtn.height shr 1;
        posbtn.Visible := true;
        end;
end;

function TForm1.LoadMapFileInfo(mapname : string):boolean;
var
        fxml : TjvSimpleXML;
        i : integer;
        sr, ss : string;
        coname : string;
begin
        result := false;
        mapsource := ''; sr := '';
        mapzonerect := RECT(0,0,0,0);
        coname := exepath + pathico + 'core_mapstyles.xml';
        try
        if fileexists(coname) then begin
        fxml := TjvSimpleXML.Create(self);
        try
        fxml.LoadFromFile(coname);
        with fxml.Root.Items do begin
        if Count>0 then begin
                for i:=0 to pred(Count) do begin
                        try
                        if Item[i].Name = 'ImageStyle' then begin
                                if Item[i].Properties.ItemNamed['Name'].Value = mapname then begin
                                        try
                                        sr := Item[i].Properties.ItemNamed['zonerect'].Value;
                                        label5.Caption := sr;
                                        // get zonerect
                                        ss := splitstr(sr,',',sr);
                                        mapzonerect.Left  := strtoint(ss);
                                        ss := splitstr(sr,',',sr);
                                        mapzonerect.Top   := strtoint(ss);
                                        ss := splitstr(sr,',',sr);
                                        mapzonerect.Right := strtoint(ss);
                                        mapzonerect.Bottom:= strtoint(sr);
                                        except
                                                mapzonerect := RECT(0,0,0,0);
                                        end;
                                        // map source relative path
                                        mapsource := Item[i].Items.Item[0].Properties.ItemNamed['Source'].Value;
                                end;
                        end;
                        except
                        end;
                end;
                //
                if sr = '' then begin
                        for i:=(MapZoneDef.Count-2) downto 0 do
                        if lowercase(MapZoneDef.Strings[i])=lowercase(mapname) then begin
                                try
                                sr := MapZoneDef.Strings[i+1];
                                Label5.caption := sr;
                                ss := splitstr(sr,',',sr);
                                mapzonerect.Left  := strtoint(ss);
                                ss := splitstr(sr,',',sr);
                                mapzonerect.Top   := strtoint(ss);
                                ss := splitstr(sr,',',sr);
                                mapzonerect.Right := strtoint(ss);
                                mapzonerect.Bottom:= strtoint(sr);
                                except
                                        MapzoneRect := Rect(0,0,0,0);
                                end;
                                break;
                        end;
                end;
        end;
        end; // end with
        // ===
        result := true;
        finally
                fxml.free;
        end;
        end; // end if fileexist
                if mapsource='' then mapsource := pathmap+'map_'+mapname+'.dds';
        except
        end;
end;

function TForm1.GetMapZoneLocPath(x, y : integer; isbar : boolean):string;
var
        resX, resY: double;
        sx, sr : string;
begin
        result := '0, 0, 0';
        // get middle height value
        try
        if not isbar then begin
        sx := splitstr(FlatEdit1.Text,',',sr);
        sx := trim(splitstr(sr,',',sr));
        end else sx := '';
        if sx = '' then sx := '0';
        except
                sx := '0';
        end;

        try
        resX := -(Mapzonerect.left + x * (MapZoneRect.Right - MapZoneRect.Left) / MapWidth);
        resY := Mapzonerect.top + y * (Mapzonerect.bottom - mapzonerect.top) / MapHeight;
        //
        result := format('%0.2f, %s, %0.2f',[resX,sx,resY]);
        except
                Result := '0, '+sx+', 0';
        end;
end;


procedure TForm1.MAPLOCAUTOExecute(Sender: TObject);
var
        sx : string;
begin
        MapLocAuto.Checked := not MapLocAuto.Checked;
        AutoMapCalc := MapLocAuto.Checked;
        if AutoMapCalc then sx := 'AutoCalc' else sx := '';
        SetBarText(4,sx);
end;

procedure TForm1.MAPLOCAUTOUpdate(Sender: TObject);
begin
        MapLocAuto.Enabled := (MapZoneRect.Left <> 0) or (MapZoneRect.Right <> 0)
                        or (MapZoneRect.Top <> 0) or (MapZoneRect.Bottom <> 0);
        if not MapLOCAuto.Enabled then begin
                MapLOCAuto.Checked := false;
                AutoMapCalc := false;
        end else MapLOCAuto.Checked := AutoMAPCalc;
end;

function TForm1.GetMapZonePathLoc(pathloc : string; var x,y : integer):string;
var
        ss, sr : string;
        px, py : double;
begin
        result := '';
        sr := pathloc;
        try
        ss := trim(splitstr(sr,',',sr));
        px := StrtoFloat(ss);
        ss := trim(splitstr(sr,',',sr));
        py := strtoFloat(trim(sr));
        if trim(sr)<>'' then begin
        Result := format('%0.2f, %s, %0.2f',[px,ss,py]);
        px := -px;
        x := - round(MapWidth * (MapZonerect.Left - px) / (MapZonerect.Right - MapZoneRect.Left));
        y := - round(Mapheight * (MapZonerect.Top - py) / (MapZoneRect.Bottom - MapZoneRect.Top));
        end;
        except
                Result := '';
        end;
end;

procedure TForm1.MapAddZonerectExecute(Sender: TObject);
var
        sf : TForm4;
begin
        try
        sf := TForm4.Create(self);
        try
        sf.inipath := changefileext(application.Exename,'.INI');
        sf.FlatEdit1.Text := MapZoneName;
        sf.FlatEdit2.Text := format('%d,%d,%d,%d',[MapZoneRect.Left,MapZoneRect.Top,MapZoneRect.Right,MapZoneRect.Bottom]);
        if mrOk = sf.ShowModal then begin
                MapZonedef.Add(sf.FlatEdit1.Text);
                MapZoneDef.Add(sf.FlatEdit2.Text);
                if isLoaded then LoadMapFileInfo(MapZoneName);
        end;
        finally
        sf.free;
        end;
        except
        end;
end;

procedure TForm1.SetBarText(ix : integer; str : string);
var
        sx, sy : string;
begin
        case ix of
        1 : begin
                StatusBar1.Panels.Items[1].Text := str;
                try
                sx := splitstr(str,',',sy);
                if isloaded then sx := GetMapZoneLocPath(strtoint(sx),strtoint(sy),true) else sx := '';
                except
                end;
                StatusBar1.Panels.Items[2].Text := sx;
            end;
        else StatusBar1.Panels.Items[ix].Text := str;
        end;
end;

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
        SetBarText(1,inttostr(X)+','+inttostr(Y));
end;

procedure TForm1.posbtnMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
        SetBarText(1,inttostr(PosBtn.Left+X)+','+inttostr(PosBtn.Top+Y));
end;

procedure TForm1.ShapeMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
        ss : TShape;
begin
        ss := Sender as TShape;
        SetBarText(1,inttostr(ss.Left+X)+','+inttostr(ss.Top+Y));
end;


procedure TForm1.MAPDELUpdate(Sender: TObject);
begin
        MAPDEL.Enabled := JvTreeView1.Selected<>nil;
end;

procedure TForm1.MAPDELExecute(Sender: TObject);
var
        tn : TTreeNode;
        ix , rt : integer;
begin
        tn := jvTreeView1.Selected;
        if tn <> nil then begin
                if tn.Parent<>nil then tn := tn.Parent;
                ix := integer(tn.data);
                if ix <> -1 then begin
                        // delete item
                        rt := MessageBox(self.Handle,'Delete This POI?','Eq2MAP Editor - Warning',MB_YESNO or MB_DEFBUTTON2);
                        if rt = IDYes then begin
                        xmlscan.Root.Items.Delete(ix);
                        ReFreshView;
                        end;
                end;
        end;
end;

procedure TForm1.FileSaveBOMExecute(Sender: TObject);
begin
        FileSaveBom.Checked := not FileSaveBom.Checked;
        isUTF8Save := FileSaveBom.Checked;
end;

procedure TForm1.FileSaveBOMUpdate(Sender: TObject);
begin
        FileSaveBOM.Checked := isUTF8Save;
end;

procedure TForm1.Help1Click(Sender: TObject);
var
        hd : TForm5;
begin
        try
        hd := TForm5.Create(self);
        try
        hd.ShowModal;
        finally
        hd.free;
        end;
        except
        end;
end;

end.
