unit uMulti;

interface

uses Classes, SysUtils, GR32, Dialogs, Graphics, uArt, uTiledata;

type TMulti = Class
private
        idxStream, mulStream : TMemoryStream;
public
        Loaded : Boolean;
        constructor Create;
        destructor Free;
        procedure Load( idxPath, mulPath : String );
        procedure Save( idxPath, mulPath : String );
        function Exists( Id : Integer ) : Boolean;
        function GetMulti( Id : Integer ) : TBitmap32;
        procedure SaveToSphere51a( Id : Integer; FilePath : String );
        procedure SaveToWsc( Id : Integer; FilePath : String );
        procedure SaveToText( Id : Integer; FilePath : String );
        procedure SaveToUOAText( Id : Integer; FilePath : String );
        procedure LoadFromSphere51a( Id : Integer; FilePath : String );
        procedure LoadFromWsc( Id : Integer; FilePath : String );
        procedure LoadFromText( Id : Integer; FilePath : String );
        procedure LoadFromUOAText( Id : Integer; FilePath : String );
        procedure Delete( Id : Integer );
end;

var     Multi : TMulti;

implementation

uses uUtilities, StrLib, uGaugeForm;

type    TIndexRecord = packed record
                Offset: Integer;
                Length: Integer;
                Extra: Integer
        end;

        TMultiItem = packed record
                ID : Word;
                X : SmallInt;
                Y : SmallInt;
                Z : SmallInt;
                isInvisible : Integer;                
                Hoehe, Breite : Word;
        end;

        PMultiItem = ^TMultiItem;

constructor TMulti.Create;
begin
        idxStream := TMemoryStream.Create;
        mulStream := TMemoryStream.Create;
        Loaded := False;
end;

destructor TMulti.Free;
begin
        idxStream.Free;
        mulStream.Free;
end;

procedure TMulti.Load( idxPath, mulPath : String );
begin
        idxStream.LoadFromFile( idxPath );
        mulStream.LoadFromFile( mulPath );
        loaded := true;
end;

procedure TMulti.Save( idxPath, mulPath : String );
begin
        idxStream.SaveToFile( idxPath );
        mulStream.SaveToFile( mulPath );
end;

function TMulti.Exists( Id : Integer ) : Boolean;
var     IndexRecord : TIndexRecord;
begin
        if not Loaded then begin
                Result := False;
                exit;
        end;

        idxStream.Seek( Id*12, soFromBeginning );
        idxStream.Read( IndexRecord, sizeof( TIndexRecord ) );

        if ( IndexRecord.Offset = -1 ) or ( IndexRecord.Length < 1 ) then begin
                Result := False;
                exit;
        end;

        Result := true;
end;

function TMulti.GetMulti( Id : Integer ) : TBitmap32;
var     Item : PMultiItem;
        Anzahl, I, J : Integer;
        IndexRecord : TIndexRecord;
        ItemList : TList;
        TmpX, TmpY, X1, X2, Y1, Y2 : Integer;
        CX, CY, AX, AY : Integer;
begin
        Result := TBitmap32.Create;
        if not Art.loaded then begin
                showmessage( 'Load Art first.' );
                exit;
        end;

        X1 := 1000; Y1 := 1000; X2 := -1000; Y2 := -1000;

        idxStream.Seek( Id*12, soFromBeginning );
        idxStream.Read( IndexRecord, 12 );

        mulStream.Seek( IndexRecord.Offset, soFromBeginning );
        Anzahl := IndexRecord.Length div 12;

        ItemList := TList.Create;
        for I := 0 to Anzahl-1 do begin
                //showmessage( inttostr( I ) +':'+ inttostr( Anzahl ) );
                new( Item );
                mulStream.Read( Item.Id, 2 );
                mulStream.Read( Item.X, 2 );
                mulStream.Read( Item.Y, 2 );
                mulStream.Read( Item.Z, 2 );
                mulStream.Read( Item.isInvisible, 4 );

                Item.Hoehe := Art.GetStaticTileHeight( Item.Id );
                Item.Breite := Art.GetStaticTileWidth( Item.Id );

                J := 0;
                while( J < ItemList.Count ) and
                        ( Item.X+Item.Y > PMultiItem( ItemList.Items[J] ).X+PMultiItem( ItemList.Items[J] ).Y ) do
                                inc( J );

                while ( J < ItemList.Count ) and
                        (Item.X+Item.Y = PMultiItem( ItemList.Items[J] ).X+PMultiItem( ItemList.Items[J] ).Y ) and
                        (Item.Z > PMultiItem( ItemList.Items[J] ).Z ) do
                                inc( J );

                while( J < ItemList.Count ) and
                        (Item.X+Item.Y = PMultiItem( ItemList.Items[J] ).X+PMultiItem( ItemList.Items[J] ).Y ) and
                        (Item.Z = PMultiItem( ItemList.Items[J] ).Z ) and
                        (Item.Hoehe > PMultiItem( ItemList.Items[J] ).Hoehe) do
                                inc( J );
                ItemList.Insert( J, Item );

                TmpX := ( Item.X - Item.Y ) *22 - ( Item.Breite div 2 );
                TmpY := ( Item.X + Item.Y ) *22 - Item.Hoehe - Item.Z*4;

                if TmpX < X1 then X1 := TmpX;
                if TmpY < Y1 then Y1 := TmpY;
                if TmpX+Item.Breite > X2 then X2 := TmpX+Item.Breite;
                if TmpY+Item.Hoehe > Y2 then Y2 := TmpY+Item.Hoehe;
        end;

        Result.SetSize( X2-X1, Y2-Y1 );
        CX := -X1;
        CY := -Y1;

        for I:=0 to ItemList.Count-1 do begin
                Item := ItemList.Items[I];
                AX := CX + (Item.X - Item.Y) * 22 - (Item.Breite div 2);
                AY := CY + (Item.X + Item.Y) * 22 - Item.Z*4 - Item.Hoehe;
                Art.PaintStaticTile( Item.ID, AX, AY, Result );
        end;

        for I := 0 to ItemList.Count-1 do begin
                Dispose( ItemList.Items[I] );
        end;
        ItemList.Free;
end;

procedure TMulti.SaveToSphere51a( Id : Integer; FilePath : String );
var     Liste : TStringList;
        IndexRecord : TIndexRecord;
        Anzahl, I : Integer;
        ItemId : Word;
        X, Y, Z : SmallInt;
        Flag : Integer;
        Serial : Integer;
begin
        Liste := TStringList.Create;

        idxStream.Seek( Id*12, soFromBeginning );
        idxStream.Read( IndexRecord, 12 );

        mulStream.Seek( IndexRecord.Offset, soFromBeginning );
        Anzahl := IndexRecord.Length div 12;

        Serial := 1078812051;
        for I := 0 to Anzahl-1 do begin
                inc( Serial );
                mulStream.Read( ItemId, 2 );
                mulStream.Read( X, 2 );
                mulStream.Read( Y, 2 );
                mulStream.Read( Z, 2 );
                mulStream.Read( Flag, 4 );
                Liste.Add( Format( '[WORLDITEM 0%x]', [ItemId] ) );
                Liste.Add( Format( 'SERIAL=0%x', [SERIAL] ) );
                Liste.Add( Format( 'ID=0%x', [ItemId] ) );
                Liste.Add( Format( 'P=%d,%d,%d', [X+5400,Y+1100,Z] ) );
                Liste.Add( '' );                
        end;
        Liste.SaveToFile( FilePath );
        Liste.Free;
end;

procedure TMulti.SaveToWsc( Id : Integer; FilePath : String );
var     Liste : TStringList;
        IndexRecord : TIndexRecord;
        Anzahl, I : Integer;
        ItemId : Word;
        X, Y, Z : SmallInt;
        Flag : Integer;
        Serial : Integer;
begin
        Liste := TStringList.Create;

        idxStream.Seek( Id*12, soFromBeginning );
        idxStream.Read( IndexRecord, 12 );

        mulStream.Seek( IndexRecord.Offset, soFromBeginning );
        Anzahl := IndexRecord.Length div 12;

        Serial := 1078812051;
        for I := 0 to Anzahl-1 do begin
                inc( Serial );
                mulStream.Read( ItemId, 2 );
                mulStream.Read( X, 2 );
                mulStream.Read( Y, 2 );
                mulStream.Read( Z, 2 );
                mulStream.Read( Flag, 4 );
                Liste.Add( Format( 'SECTION WORLDITEM %d', [I] ) );
                Liste.Add( '{' );
                Liste.Add( Format( 'SERIAL %d', [SERIAL] ) );
                Liste.Add( Format( 'ID %d', [ItemId] ) );
                Liste.Add( Format( 'X %d', [X+5400] ) );
                Liste.Add( Format( 'Y %d', [Y+1100] ) );
                Liste.Add( Format( 'Z %d', [Z] ) );
                Liste.Add( '}' );
                Liste.Add( '' );
        end;
        Liste.SaveToFile( FilePath );
        Liste.Free;
end;

procedure TMulti.SaveToText( Id : Integer; FilePath : String );
var     Liste : TStringList;
        IndexRecord : TIndexRecord;
        Anzahl, I : Integer;
        ItemId : Word;
        X, Y, Z : SmallInt;
        Flag : Integer;
begin
        Liste := TStringList.Create;

        idxStream.Seek( Id*12, soFromBeginning );
        idxStream.Read( IndexRecord, 12 );

        mulStream.Seek( IndexRecord.Offset, soFromBeginning );
        Anzahl := IndexRecord.Length div 12;

        for I := 0 to Anzahl-1 do begin
                mulStream.Read( ItemId, 2 );
                mulStream.Read( X, 2 );
                mulStream.Read( Y, 2 );
                mulStream.Read( Z, 2 );
                mulStream.Read( Flag, 4 );
                Liste.Add( Format( '0x%x %d %d %d 0x0 %d', [ItemId,X,Y,Z,Flag] ) );
        end;
        Liste.SaveToFile( FilePath );
        Liste.Free;
end;

procedure TMulti.SaveToUOAText( Id : Integer; FilePath : String );
var     Liste : TStringList;
        IndexRecord : TIndexRecord;
        Anzahl, I : Integer;
        ItemId : Word;
        X, Y, Z : SmallInt;
        Flag : Integer;
begin
        Liste := TStringList.Create;

        idxStream.Seek( Id*12, soFromBeginning );
        idxStream.Read( IndexRecord, 12 );

        mulStream.Seek( IndexRecord.Offset, soFromBeginning );
        Anzahl := IndexRecord.Length div 12;

        Liste.Add( '6 version' );
        Liste.Add( '1 template id' );
        Liste.Add( '-1 item version' );
        Liste.Add( Format( '%d num components', [Anzahl] ) );

        for I := 0 to Anzahl-1 do begin
                mulStream.Read( ItemId, 2 );
                mulStream.Read( X, 2 );
                mulStream.Read( Y, 2 );
                mulStream.Read( Z, 2 );
                mulStream.Read( Flag, 4 );
                Liste.Add( Format( '%d %d %d %d %d', [ItemId,X,Y,Z,Flag] ) );
        end;
        Liste.SaveToFile( FilePath );
        Liste.Free;
end;

procedure TMulti.LoadFromSphere51a( Id : Integer; FilePath : String );
var     Liste : TStringList;
        IndexRecord : TIndexRecord;
        I : Integer;
        CX, CY : Integer;
        MaxX, MaxY, MinX, MinY, MinZ : Integer;
        ItemListe : TList;
        Item : PMultiItem;
        Zeile : String;
        ItemId, X, Y, Z : Integer;
        Temp : String;
        PosListe : TStringList;
begin
        mulStream.Seek( 0, soFromEnd );
        IndexRecord.Offset := mulStream.Position;

        Liste := TStringList.Create;
        Liste.LoadFromFile( FilePath );

        ItemListe := TList.Create;

        MaxX := 0;
        MaxY := 0;
        MinX := 10000;
        MinY := 10000;
        MinZ := 10000;
        ItemId := -1;
        X := 0;
        Y := 0;
        Z := 0;

        for I := 0 to Liste.Count-1 do begin
                Zeile := Liste.Strings[ I ];
                if Pos( '[WORLDITEM ', Zeile ) <> 0 then begin
                        Temp := '0x' + Copy( Zeile, 13, Length( Zeile )-13 );
                        if ItemId <> -1 then begin
                                new( Item );
                                Item.ID := ItemId;
                                Item.X := X;
                                Item.Y := Y;
                                Item.Z := Z;
                                Item.isInvisible := 1;
                                ItemListe.Add( Item );
                        end;
                        ItemId := StrToInt( Temp );
                end
                else if Pos( 'ID=', Zeile ) <> 0 then begin
                        Temp := '0x' + Copy( Zeile, 5, Length( Zeile )-4 );
                        ItemId := StrToInt( Temp );
                end
                else if Pos( 'P=', Zeile ) <> 0 then begin
                        Temp := Copy( Zeile, 3, Length( Zeile )-2 );

                        PosListe := TStringList.Create;
                        StrLib.Split( Temp, ',', PosListe );
                        X := StrToInt( PosListe.Strings[0] );
                        Y := StrToInt( PosListe.Strings[1] );
                        Z := StrToInt( PosListe.Strings[2] );
                        PosListe.Free;

                        if MaxX < X then MaxX := X;
                        if MaxY < Y then MaxY := Y;
                        if MinX > X then MinX := X;
                        if MinY > Y then MinY := Y;
                        if MinZ > Z then MinZ := Z;
                end;
        end;

        if ItemId <> -1 then begin
                new( Item );
                Item.ID := ItemId;
                Item.X := X;
                Item.Y := Y;
                Item.Z := Z;
                Item.isInvisible := 1;
                ItemListe.Add( Item );
        end;

        Liste.Free;

        CX := MaxX - ( MaxX - MinX ) div 2;
        CY := MaxY - ( MaxY - MinY ) div 2;

        for I := 0 to ItemListe.Count-1 do begin
                Item := ItemListe.Items[ I ];
                Item.X := Item.X - CX;
                Item.Y := Item.Y - CY;
                Item.Z := Item.Z - MinZ;

                mulStream.Write( Item.Id, 2 );
                mulStream.Write( Item.X, 2 );
                mulStream.Write( Item.Y, 2 );
                mulStream.Write( Item.Z, 2 );
                mulStream.Write( Item.isInvisible, 4 );
        end;

        for I := 0 to ItemListe.Count-1 do begin
                Dispose( ItemListe.Items[I] );
        end;
        ItemListe.Free;

        IndexRecord.Length := mulStream.Position - IndexRecord.Offset;
        IndexRecord.Extra := -1;

        idxStream.Seek( Id*12, soFromBeginning );
        idxStream.Write( IndexRecord, 12 );
end;

procedure TMulti.LoadFromWsc( Id : Integer; FilePath : String );
var     Liste : TStringList;
        IndexRecord : TIndexRecord;
        I : Integer;
        CX, CY : Integer;
        MaxX, MaxY, MinX, MinY, MinZ : Integer;
        ItemListe : TList;
        Item : PMultiItem;
        Zeile : String;
        ItemId, X, Y, Z : Integer;
        Temp : String;
begin
        mulStream.Seek( 0, soFromEnd );
        IndexRecord.Offset := mulStream.Position;

        Liste := TStringList.Create;
        Liste.LoadFromFile( FilePath );

        ItemListe := TList.Create;

        MaxX := 0;
        MaxY := 0;
        MinX := 10000;
        MinY := 10000;
        MinZ := 10000;
        ItemId := -1;
        X := 0;
        Y := 0;
        Z := 0;

        for I := 0 to Liste.Count-1 do begin
                Zeile := Liste.Strings[ I ];
                if Pos( 'SECTION WORLDITEM ', Zeile ) <> 0 then begin
                        if ItemId <> -1 then begin
                                new( Item );
                                Item.ID := ItemId;
                                Item.X := X;
                                Item.Y := Y;
                                Item.Z := Z;
                                Item.isInvisible := 1;
                                ItemListe.Add( Item );
                        end;
                end
                else if Pos( 'ID ', Zeile ) <> 0 then begin
                        Temp := Copy( Zeile, 4, Length( Zeile )-3 );
                        ItemId := StrToInt( Temp );
                end
                else if Pos( 'X ', Zeile ) <> 0 then begin
                        Temp := Copy( Zeile, 3, Length( Zeile )-2 );
                        X := StrToInt( Temp );

                        if MaxX < X then MaxX := X;
                        if MinX > X then MinX := X;
                end
                else if Pos( 'Y ', Zeile ) <> 0 then begin
                        Temp := Copy( Zeile, 3, Length( Zeile )-2 );
                        Y := StrToInt( Temp );

                        if MaxY < Y then MaxY := Y;
                        if MinY > Y then MinY := Y;
                end
                else if Pos( 'Z ', Zeile ) <> 0 then begin
                        Temp := Copy( Zeile, 3, Length( Zeile )-2 );
                        Z := StrToInt( Temp );

                        if MinZ > Z then MinZ := Z;
                end;
        end;

        if ItemId <> -1 then begin
                new( Item );
                Item.ID := ItemId;
                Item.X := X;
                Item.Y := Y;
                Item.Z := Z;
                Item.isInvisible := 1;
                ItemListe.Add( Item );
        end;

        Liste.Free;

        CX := MaxX - ( MaxX - MinX ) div 2;
        CY := MaxY - ( MaxY - MinY ) div 2;

        for I := 0 to ItemListe.Count-1 do begin
                Item := ItemListe.Items[ I ];
                Item.X := Item.X - CX;
                Item.Y := Item.Y - CY;
                Item.Z := Item.Z - MinZ;

                mulStream.Write( Item.Id, 2 );
                mulStream.Write( Item.X, 2 );
                mulStream.Write( Item.Y, 2 );
                mulStream.Write( Item.Z, 2 );
                mulStream.Write( Item.isInvisible, 4 );
        end;

        for I := 0 to ItemListe.Count-1 do begin
                Dispose( ItemListe.Items[I] );
        end;
        ItemListe.Free;

        IndexRecord.Length := mulStream.Position - IndexRecord.Offset;
        IndexRecord.Extra := -1;

        idxStream.Seek( Id*12, soFromBeginning );
        idxStream.Write( IndexRecord, 12 );
end;

procedure TMulti.LoadFromText( Id : Integer; FilePath : String );
var     Liste, L2 : TStringList;
        IndexRecord : TIndexRecord;
        I : Integer;
        CX, CY : Integer;
        MaxX, MaxY, MinX, MinY, MinZ : Integer;
        ItemListe : TList;
        Item : PMultiItem;
        Zeile : String;
begin
        mulStream.Seek( 0, soFromEnd );
        IndexRecord.Offset := mulStream.Position;

        Liste := TStringList.Create;
        Liste.LoadFromFile( FilePath );

        ItemListe := TList.Create;

        MaxX := 0;
        MaxY := 0;
        MinX := 10000;
        MinY := 10000;
        MinZ := 10000;

        for I := 0 to Liste.Count-1 do begin
                Zeile := Liste.Strings[I];
                L2 := TStringList.Create;
                StrLib.Split( Zeile, ' ', L2 );

                new( Item );
                Item.ID := StrToInt( L2.Strings[0] );
                Item.X := StrToInt( L2.Strings[1] );
                Item.Y := StrToInt( L2.Strings[2] );
                Item.Z := StrToInt( L2.Strings[3] );
                Item.isInvisible := StrToInt( L2.Strings[5] );
                L2.Free;

                if MaxX < Item.X then MaxX := Item.X;
                if MinX > Item.X then MinX := Item.X;
                if MaxY < Item.Y then MaxY := Item.Y;
                if MinY > Item.Y then MinY := Item.Y;
                if MinZ > Item.Z then MinZ := Item.Z;
                ItemListe.Add( Item );
        end;

        Liste.Free;

        CX := MaxX - ( MaxX - MinX ) div 2;
        CY := MaxY - ( MaxY - MinY ) div 2;

        for I := 0 to ItemListe.Count-1 do begin
                Item := ItemListe.Items[ I ];
                Item.X := Item.X - CX;
                Item.Y := Item.Y - CY;
                Item.Z := Item.Z - MinZ;

                mulStream.Write( Item.Id, 2 );
                mulStream.Write( Item.X, 2 );
                mulStream.Write( Item.Y, 2 );
                mulStream.Write( Item.Z, 2 );
                mulStream.Write( Item.isInvisible, 4 );
        end;

        for I := 0 to ItemListe.Count-1 do begin
                Dispose( ItemListe.Items[I] );
        end;
        ItemListe.Free;

        IndexRecord.Length := mulStream.Position - IndexRecord.Offset;
        IndexRecord.Extra := -1;

        idxStream.Seek( Id*12, soFromBeginning );
        idxStream.Write( IndexRecord, 12 );
end;

procedure TMulti.LoadFromUOAText( Id : Integer; FilePath : String );
var     Liste, L2 : TStringList;
        IndexRecord : TIndexRecord;
        I, Anzahl : Integer;
        CX, CY : Integer;
        MaxX, MaxY, MinX, MinY, MinZ : Integer;
        ItemListe : TList;
        Item : PMultiItem;
        Zeile : String;
begin
        mulStream.Seek( 0, soFromEnd );
        IndexRecord.Offset := mulStream.Position;

        Liste := TStringList.Create;
        Liste.LoadFromFile( FilePath );

        ItemListe := TList.Create;

        MaxX := 0;
        MaxY := 0;
        MinX := 10000;
        MinY := 10000;
        MinZ := 10000;

        Zeile := Liste.Strings[ 3 ];
        L2 := TStringList.Create;
        L2.Delimiter := ' ';
        L2.DelimitedText := Zeile;
        Anzahl := StrToInt( L2.Strings[ 0 ] );
        L2.Free;

        for I := 1 to Anzahl do begin
                Zeile := Liste.Strings[I+3];
                L2 := TStringList.Create;
                StrLib.Split( Zeile, ' ', L2 );

                new( Item );
                Item.ID := StrToInt( L2.Strings[0] );
                Item.X := StrToInt( L2.Strings[1] );
                Item.Y := StrToInt( L2.Strings[2] );
                Item.Z := StrToInt( L2.Strings[3] );
                Item.isInvisible := StrToInt( L2.Strings[4] );
                L2.Free;

                if MaxX < Item.X then MaxX := Item.X;
                if MinX > Item.X then MinX := Item.X;
                if MaxY < Item.Y then MaxY := Item.Y;
                if MinY > Item.Y then MinY := Item.Y;
                if MinZ > Item.Z then MinZ := Item.Z;
                ItemListe.Add( Item );
        end;

        Liste.Free;

        CX := MaxX - ( MaxX - MinX ) div 2;
        CY := MaxY - ( MaxY - MinY ) div 2;

        for I := 0 to ItemListe.Count-1 do begin
                Item := ItemListe.Items[ I ];
                Item.X := Item.X - CX;
                Item.Y := Item.Y - CY;
                Item.Z := Item.Z - MinZ;

                mulStream.Write( Item.Id, 2 );
                mulStream.Write( Item.X, 2 );
                mulStream.Write( Item.Y, 2 );
                mulStream.Write( Item.Z, 2 );
                mulStream.Write( Item.isInvisible, 4 );
        end;

        for I := 0 to ItemListe.Count-1 do begin
                Dispose( ItemListe.Items[I] );
        end;
        ItemListe.Free;

        IndexRecord.Length := mulStream.Position - IndexRecord.Offset;
        IndexRecord.Extra := -1;

        idxStream.Seek( Id*12, soFromBeginning );
        idxStream.Write( IndexRecord, 12 );
end;

procedure TMulti.Delete( Id : Integer );
var     IndexRec : TIndexRecord;
begin
        IndexRec.Offset := -1;
        IndexRec.Length := -1;
        IndexRec.Extra := -1;

        idxStream.Seek( Id*12, soFromBeginning );
        idxStream.Write( IndexRec, sizeof( TIndexRecord ) );
end;

end.
