unit uVerdata;

interface

uses Classes, SysUtils, GR32, Dialogs, Graphics, uUtilities, uAnimations;

type TVerdata = class
private
        mulStream : TMemoryStream;
        indexList : TList;
        IndexCount : integer;
        function GetId( RealId : Integer ) : Integer;
        function GetRealAnimId( Id : Integer ) : Integer;
        function AnimCount( Id : integer ) : integer;
        function GetAnimType( Id : integer ) : word;
public
        loaded : boolean;
        constructor Create;
        destructor Free;
        procedure Load( Filename : String );
        procedure Save( Filename : String );
        procedure Delete( FileTyp, Id : Integer );
        function Exists( FileTyp, Id : integer ) : Boolean;
        function SaveFirstAnimPicture( Id : integer ) : TBitmap32;
        function SaveMapArt( Id : integer ) : TBitmap32;
        procedure LoadMapArt( Id : integer; FilePath : String );
        function SaveStaticArt( Id : integer ) : TBitmap32;
        procedure LoadStaticArt( Id : integer; FilePath : String );
        function SaveTex( Id : integer ) : TBitmap32;
        procedure LoadTex( Id : integer; FilePath : String );
        function SaveGump( Id : integer ) : TBitmap32;
        procedure LoadGump( Id : integer; FilePath : String );
        procedure SaveAnimToFile( Id : integer; Filename : String );
        procedure LoadAnimFromFile( Id : integer; Filename : String );
        procedure SaveAnimToDirectory( Id : integer; dirPath : String );
        procedure LoadAnimFromDirectory( Id : integer; dirPath : String; UseColor : boolean );        
end;

var     Verdata : TVerdata;

implementation

uses StrLib, uGaugeForm;

type    TVerIndexRecord = packed record
                FileTyp : integer;
                ID : integer;
                Offset : integer;
                Length : integer;
                Extra : integer;
        end;

        TVerIndex = class( TObject )
                FileTyp : integer;
                ID : integer;
                Offset : integer;
                Length : integer;
                Extra : integer;
        end;

        TCenter = packed record
                CenterX : integer;
                CenterY : integer;
        end;

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

constructor TVerdata.Create;
begin
        mulStream := TMemoryStream.Create;
        indexList := TList.Create;
        loaded := false;
end;

destructor TVerdata.Free;
begin
        mulStream.Free;
        indexList.Free;
end;

procedure TVerdata.Load( Filename : String );
var     i : integer;
        VerIndexRecord : TVerIndexRecord;
        VerIndex : TVerIndex;
begin
        mulStream.LoadFromFile( Filename );
        mulStream.Seek( 0, soFromBeginning );

        mulStream.Read( IndexCount, 4 );
        for i := 0 to IndexCount-1 do begin
                mulStream.Read( VerIndexRecord, sizeof( TVerIndexRecord ) );
                VerIndex := TVerIndex.Create;
                VerIndex.FileTyp := VerIndexRecord.FileTyp;
                VerIndex.ID := VerIndexRecord.ID;
                VerIndex.Offset := VerIndexRecord.Offset;
                VerIndex.Length := VerIndexRecord.Length;
                VerIndex.Extra := VerIndexRecord.Extra;              
                IndexList.Add( VerIndex );
        end;
        loaded := true;
end;

procedure TVerdata.Save( Filename : String );
var     fStream : TFileStream;
        i : Integer;
        VerIndexRecord : TVerIndexRecord;
        CountNew : Integer;
        mStream : TMemoryStream;
        Buf : PByteArray;
begin
        if FileExists( Filename ) then
                DeleteFile( Filename );
        fStream := TFileStream.Create( Filename, fmCreate );
        fStream.Seek( 0, soFromBeginning );

        mStream := TMemoryStream.Create;
        mStream.Seek( 0, soFromBeginning );
        CountNew := IndexList.Count;

        if CountNew < 1 then begin
                CountNew := 0;
                fStream.Write( CountNew, 4 );
                fStream.Free;
                exit;
        end;

        fStream.Write( CountNew, 4 );
        GaugeForm.Activate;
        for i := 0 to CountNew-1 do begin
                GaugeForm.SetBalken( trunc( i*100 / (CountNew-1) ) );
                VerIndexRecord.FileTyp := TVerIndex( IndexList.Items[i] ).FileTyp;
                VerIndexRecord.ID := TVerIndex( IndexList.Items[i] ).ID;
                VerIndexRecord.Length := TVerIndex( IndexList.Items[i] ).Length;
                VerIndexRecord.Extra := TVerIndex( IndexList.Items[i] ).Extra;
                if ( TVerIndex( IndexList.Items[i] ).Offset <> -1 ) and ( TVerIndex( IndexList.Items[i] ).Length <> -1 ) then begin
                        VerIndexRecord.Offset := 4 + CountNew*sizeof( TVerIndexRecord ) + mStream.Position;
                        GetMem( Buf, VerIndexRecord.Length );
                        mulStream.Seek( TVerIndex( IndexList.Items[i] ).Offset, soFromBeginning );
                        mulStream.Read( Buf^, VerIndexRecord.Length );
                        mStream.Write( Buf^, VerIndexRecord.Length );
                        FreeMem( Buf, VerIndexRecord.Length );
                end
                else begin
                        VerIndexRecord.Offset := -1;
                        VerIndexRecord.Length := -1;
                        VerIndexRecord.Extra := -1;
                end;
                fStream.Write( VerIndexRecord, sizeof( TVerIndexRecord ) );                
        end;
        mStream.Seek( 0, soFromBeginning );
        fStream.CopyFrom( mStream, mStream.Size );
        GaugeForm.Deactivate;

        fStream.Free;
        mStream.Free;
end;

procedure TVerdata.Delete( Filetyp, Id : Integer );
var     I, J : Integer;
begin
        if Filetyp <> typ_anim then begin
                for I:=0 to IndexList.Count-1 do
                        if ( TVerIndex( IndexList.Items[i] ).FileTyp = Filetyp ) and ( TVerIndex( IndexList.Items[i] ).Id = Id ) then begin
                                IndexList.Delete( I );
                                exit;
                        end;
        end
        else begin
                for j:=0 to AnimCount( Id ) do begin
                        for i:=0 to IndexList.Count-1 do
                                if ( TVerIndex( IndexList.Items[i] ).FileTyp = Filetyp ) and ( TVerIndex( IndexList.Items[i] ).Id = GetRealAnimId( Id )+j ) then begin
                                        IndexList.Delete( i );
                                        break;
                                end;
                end;
        end;
end;

function TVerdata.GetId( RealId : Integer ) : Integer;
begin
        if RealId < 22000 then
                Result := RealId div 110
        else if RealId < 35000 then
                Result := ( RealId - 22000 ) div 65
        else
                Result := ( RealId - 35000 ) div 175;
end;

function TVerdata.Exists( FileTyp, Id : Integer ) : Boolean;
var     i : Integer;
begin
        if not loaded then begin
                Result := false;
                exit;
        end;

        if Filetyp = typ_anim then begin
                for i := 0 to IndexList.Count-1 do begin
                        if ( TVerIndex( IndexList.Items[i] ).FileTyp = Filetyp ) and ( TVerIndex( IndexList.Items[i] ).Id = GetRealAnimId(Id) ) and ( TVerIndex( IndexList.Items[i] ).Offset <> -1 ) then begin
                                Result := true;
                                exit;
                        end;
                end;
        end
        else begin
                for i := 0 to IndexList.Count-1 do begin
                        if ( TVerIndex( IndexList.Items[i] ).FileTyp = Filetyp ) and ( TVerIndex( IndexList.Items[i] ).Id = Id ) and ( TVerIndex( IndexList.Items[i] ).Offset <> -1 ) then begin
                                Result := true;
                                exit;
                        end;
                end;
        end;
        Result := false;
end;

function TVerdata.GetRealAnimId( Id : Integer ) : Integer;
begin
        if Id < 200 then begin
                Result := Id*110;
        end
        else begin
                if Id < 400 then begin
                        Result := ( Id - 200 ) * 65 + 22000;
                end
                else begin
                        Result := ( Id - 400 ) * 175 + 35000;
                end;
        end;
end;

function TVerdata.AnimCount( Id : Integer ) : Integer;
begin
        if Id < 200 then
                Result := 110
        else if Id < 400 then
                Result := 65
        else
                Result := 175;
end;

function TVerdata.GetAnimType( Id : integer ) : word;
begin
        If Id < 200 then
                Result := type_HighDetailed
        else if Id < 400 then
                Result := type_LowDetailed
        else
                Result := type_People;
end;

function TVerdata.SaveFirstAnimPicture( Id : Integer ) : TBitmap32;
var     RealId : integer;
        Offset : integer;
        Palette : PWordArray;
        LookupList : PIntegerArray;
        Pixels : PByteArray;
        FrameCount, Start : Integer;
        CenterX, CenterY : smallint;
        Breite, Hoehe : word;
        xRun, xOffset, yOffset : integer;
        Header : longWord;
        x, y, k, i : integer;
begin
        Result := TBitmap32.Create;
        RealId := GetRealAnimId( Id );

        Offset := -1;
        for i:=0 to IndexList.Count-1 do
                if ( TVerIndex( IndexList.Items[i] ).FileTyp = typ_anim ) and ( TVerIndex( IndexList.Items[i] ).Id = RealId ) then begin
                        Offset := TVerIndex( IndexList.Items[i] ).Offset;
                        break;
                end;

        if Offset = -1 then
                exit;

        mulStream.Seek( Offset, soFromBeginning );

        GetMem( Palette, 512 );
        mulStream.Read( Palette^, 512 );
        Start := mulStream.Position;
        mulStream.Read( FrameCount, 4 );
        GetMem( LookupList, FrameCount*4 );
        mulStream.Read( LookupList^, FrameCount*4 );

        mulStream.Seek( Start + LookupList^[0], soFromBeginning );
        mulStream.Read( CenterX, 2 );
        mulStream.Read( CenterY, 2 );
        mulStream.Read( Breite, 2 );
        mulStream.Read( Hoehe, 2 );

        Result.Width := Breite;
        Result.Height := Hoehe;

        mulStream.Read( Header, 4 );

        while ( Header <> $7FFF7FFF ) do begin
                xRun := Header and $FFF;
                xOffset := ( Header shr 22 ) and 1023;
                yOffset := ( Header shr 12 ) and 1023;

                if ( xOffset and 512 ) = 512 then
                        xOffset := xOffset or ( $FFFFFFFF - 511 );
                if ( yOffset and 512 ) = 512 then
                        yOffset := yOffset or ( $FFFFFFFF - 511 );

                x := xOffset + CenterX;
                y := yOffset + CenterY + Hoehe;

                GetMem( Pixels, xRun );
                mulStream.Read( Pixels^, xRun );

                for k := 0 to xRun-1 do begin
                        if ( x < Result.Width ) and ( y < Result.Height ) and ( x >= 0 ) and ( y >= 0 ) then
                                Result.Pixel[ x, y ] := Color15to32( Palette^[ Pixels^[ k ] ] )
                        else begin
                                showmessage( 'BugPixel: ' + inttostr( Breite ) + ':' + inttostr( Hoehe ) + ',' + inttostr( CenterX ) + ':' + inttostr( CenterY ) + ',' + inttostr( xRun ) + ':' + inttostr( xOffset ) + ':' + inttostr( yOffset ) );
                        end;
                        inc( x );
                end;
                FreeMem( Pixels, xRun );
                mulStream.Read( Header, 4 );
        end;

        FreeMem( Palette, 512 );
        FreeMem( LookupList, FrameCount*4 );
end;

procedure TVerdata.SaveAnimToFile( Id : Integer; Filename : String );
var     RealId : integer;
        VerIndex : TVerIndex;
        Anim, AnimationCount : integer;
        TypFile, TypAnim : word;
        fStream : TFileStream;
        RecordList : array[0..255] of TIndexRecord;
        buf : PByteArray;
        i : integer;
begin
        RealId := GetRealAnimId( Id );

        fStream := TFileStream.Create( Filename, fmCreate );
        fStream.Seek( 0, soFromBeginning );
        GaugeForm.Activate;

        TypFile := typ_anim;
        TypAnim := GetAnimType( Id );
        AnimationCount := AnimCount( Id );
        fStream.Write( TypFile, 2 );
        fStream.Write( TypAnim, 2 );

        fStream.Write( RecordList, AnimationCount*12 );

        for Anim := 0 to AnimationCount-1 do begin
                GaugeForm.SetBalken( trunc( (Anim+1)*100 / AnimationCount ) );

                VerIndex := TVerIndex.Create;
                VerIndex.Offset := -1;
                for i:=0 to IndexList.Count-1 do
                        if ( TVerIndex( IndexList.Items[i] ).FileTyp = typ_anim ) and ( TVerIndex( IndexList.Items[i] ).Id = RealId+Anim ) then begin
                                VerIndex := TVerIndex( IndexList.Items[i] );
                                break;
                        end;

                if ( VerIndex.Offset <> -1 ) and ( VerIndex.Length > 0 ) then begin
                        RecordList[Anim].Offset := fStream.Position;
                        RecordList[Anim].Length := VerIndex.Length;
                        RecordList[Anim].Extra := VerIndex.Extra;

                        mulStream.Seek( VerIndex.Offset, soFromBeginning );
                        GetMem( buf, VerIndex.Length );
                        mulStream.Read( buf^, VerIndex.Length );
                        fStream.Write( buf^, VerIndex.Length );
                        FreeMem( buf, VerIndex.Length );
                end
                else begin
                        RecordList[Anim].Offset := VerIndex.Offset;
                        RecordList[Anim].Length := VerIndex.Length;
                        RecordList[Anim].Extra := VerIndex.Extra;
                end;
        end;

        fStream.Seek( 4, soFromBeginning );
        fStream.Write( RecordList, AnimationCount*12 );
        fStream.Free;
        GaugeForm.Deactivate;
end;

procedure TVerdata.SaveAnimToDirectory( Id : Integer; dirPath : String );
var     RealId : integer;
        VerIndex : TVerIndex;
        Palette : PWordArray;
        LookupList : PIntegerArray;
        Pixels : PByteArray;
        FrameCount, Start, Frame, Anim : Integer;
        CenterX, CenterY : smallint;
        Breite, Hoehe : word;
        xRun, xOffset, yOffset : integer;
        Header : longWord;
        x, y, k, l, i : integer;
        Bitmap : TBitmap32;
        Liste, ColorList : TStringList;
begin
        RealId := GetRealAnimId( Id );
        Liste := TStringList.Create;
        GaugeForm.Activate;
        for Anim := 0 to AnimCount( Id )-1 do begin
                GaugeForm.SetBalken( trunc( (Anim+1)*100 / AnimCount( Id ) ) );

                VerIndex := TVerIndex.Create;
                for i:=0 to IndexList.Count-1 do
                        if ( TVerIndex( IndexList.Items[i] ).FileTyp = typ_anim ) and ( TVerIndex( IndexList.Items[i] ).Id = RealId+Anim ) then begin
                                VerIndex := TVerIndex( IndexList.Items[i] );
                                break;
                        end;

                if ( VerIndex.Offset <> -1 ) and ( VerIndex.Length > 0 ) then begin
                        mulStream.Seek( VerIndex.Offset, soFromBeginning );

                        GetMem( Palette, 512 );
                        mulStream.Read( Palette^, 512 );
                        Start := mulStream.Position;

                        ColorList := TStringList.Create;
                        for l := 0 to 255 do begin
                                ColorList.Add( inttostr( Palette^[l] ) );
                        end;
                        ColorList.SaveToFile( dirPath + '\palette_' + inttostr( Anim ) + '.txt' );
                        ColorList.Free;

                        mulStream.Read( FrameCount, 4 );
                        GetMem( LookupList, FrameCount*4 );
                        mulStream.Read( LookupList^, FrameCount*4 );

                        for Frame := 0 to FrameCount-1 do begin
                                Bitmap := TBitmap32.Create;
                                mulStream.Seek( Start + LookupList^[ Frame ], soFromBeginning );
                                mulStream.Read( CenterX, 2 );
                                mulStream.Read( CenterY, 2 );
                                mulStream.Read( Breite, 2 );
                                mulStream.Read( Hoehe, 2 );

                                Bitmap.Width := Breite;
                                Bitmap.Height := Hoehe;

                                Liste.Add( inttostr( Anim ) + ':' + inttostr( Frame ) );
                                Liste.Add( 'CenterX:' + inttostr( CenterX ) );
                                Liste.Add( 'CenterY:' + inttostr( CenterY ) );

                                mulStream.Read( Header, 4 );

                                while ( Header <> $7FFF7FFF ) do begin
                                        xRun := Header and $FFF;
                                        xOffset := ( Header shr 22 ) and 1023;
                                        yOffset := ( Header shr 12 ) and 1023;

                                        if ( xOffset and 512 ) = 512 then
                                                xOffset := xOffset or ( $FFFFFFFF - 511 );
                                        if ( yOffset and 512 ) = 512 then
                                                yOffset := yOffset or ( $FFFFFFFF - 511 );

                                        x := xOffset + CenterX;
                                        y := yOffset + CenterY + Hoehe;

                                        GetMem( Pixels, xRun );
                                        mulStream.Read( Pixels^, xRun );

                                        for k := 0 to xRun-1 do begin
                                                if ( x < Bitmap.Width ) and ( y < Bitmap.Height ) and ( x >= 0 ) and ( y >= 0 ) then
                                                        Bitmap.Pixel[ x, y ] := Color15to32( Palette^[ Pixels^[ k ] ] )
                                                else begin
                                                        showmessage( 'BugPixel: ' + inttostr( Breite ) + ':' + inttostr( Hoehe ) + ',' + inttostr( CenterX ) + ':' + inttostr( CenterY ) + ',' + inttostr( xRun ) + ':' + inttostr( xOffset ) + ':' + inttostr( yOffset ) );
                                                end;
                                                inc( x );
                                        end;
                                        FreeMem( Pixels, xRun );
                                        mulStream.Read( Header, 4 );
                                end;
                                Bitmap.SaveToFile( dirPath + '\' + inttostr( Anim ) + '_' + inttostr( Frame ) + '.bmp' );
                                Bitmap.Free;
                        end;

                        FreeMem( Palette, 512 );
                        FreeMem( LookupList, FrameCount*4 );
                end;
        end;
        GaugeForm.Deactivate;
        Liste.SaveToFile( dirPath + '\liste.txt' );
        Liste.Free;
end;

function TVerdata.SaveMapArt( Id : Integer ) : TBitmap32;
var     y, x : Integer;
        Color : Word;
        Offset : Integer;
        i : Integer;        
begin
        Result := TBitmap32.Create;

        Offset := -1;
        for i:=0 to IndexList.Count-1 do
                if ( TVerIndex( IndexList.Items[i] ).FileTyp = typ_art ) and ( TVerIndex( IndexList.Items[i] ).Id = Id ) then begin
                        Offset := TVerIndex( IndexList.Items[i] ).Offset;
                        break;
                end;
        if Offset = -1 then
                exit;

        mulStream.Seek( Offset, soFromBeginning );
        Result.SetSize( 44, 44 );
        for y := 0 to 21 do begin
                for x := 0 to (y+1)*2-1 do begin
                        mulStream.Read( Color, 2 );
                        Result.Pixel[ 21-y+x, y ] := Color15to32( Color );
                end;
        end;
        for y := 0 to 21 do begin
                for x := 0 to 43-y*2 do begin
                        mulStream.Read( Color, 2 );
                        Result.Pixel[ y+x, y+22 ] := Color15to32( Color );
                end;
        end;
end;

function TVerdata.SaveStaticArt( Id : Integer ) : TBitmap32;
var     Breite, Hoehe : Word;
        Offset : Integer;
        LookupList : PWordArray;
        XOffset, RunLength, RunColor: Word;
        Start : Integer;
        x, y, i : Integer;
begin
        Result := TBitmap32.Create;

        Offset := -1;
        for i:=0 to IndexList.Count-1 do
                if ( TVerIndex( IndexList.Items[i] ).FileTyp = typ_art ) and ( TVerIndex( IndexList.Items[i] ).Id = Id+$4000 ) then begin
                        Offset := TVerIndex( IndexList.Items[i] ).Offset;
                        break;
                end;
        if Offset = -1 then
                exit;

        mulStream.Seek( Offset+4, soFromBeginning );

        mulStream.Read( Breite, 2 );
        mulStream.Read( Hoehe, 2 );

        GetMem( LookupList, Hoehe*2 );
        mulStream.Read( LookupList^, Hoehe*2 );

        Result.SetSize( Breite, Hoehe );
        Start := mulStream.Position;

        for y := 0 to Hoehe-1 do begin
                mulStream.Seek( Start + LookupList^[y]*2, soFromBeginning );
                x := 0;

                mulStream.Read( XOffset, 2 );
                mulStream.Read( RunLength, 2 );

                while ( RunLength <> 0 ) do begin
                        x := x + XOffset;

                        for i := 0 to RunLength-1 do begin
                                mulStream.Read( RunColor, 2 );
                                if ( x < Result.Width ) and ( y < Result.Height ) and ( x >= 0 ) and ( y >= 0 ) then
                                        Result.Pixel[ x+i, y ] := Color15to32( RunColor )
                                else
                                        showmessage( 'Buggy Pixel: ' + inttostr( x ) + ':' + inttostr( y ) );
                        end;

                        x := x + RunLength;
                        mulStream.Read( XOffset, 2 );
                        mulStream.Read( RunLength, 2 );
                end;
        end;
end;

function TVerdata.SaveTex( Id : Integer ) : TBitmap32;
var     Hoehe, Breite : Word;
        x, y : Integer;
        Color : Word;
        Offset, Length : Integer;
        i : Integer;        
begin
        Result := TBitmap32.Create;

        Offset := -1;
        for i:=0 to IndexList.Count-1 do
                if ( TVerIndex( IndexList.Items[i] ).FileTyp = typ_texmaps ) and ( TVerIndex( IndexList.Items[i] ).Id = Id ) then begin
                        Offset := TVerIndex( IndexList.Items[i] ).Offset;
                        Length := TVerIndex( IndexList.Items[i] ).Length;
                        break;
                end;
        if Offset = -1 then
                exit;

        mulStream.Seek( Offset, soFromBeginning );

        if Length = 8192 then begin
                Hoehe := 64;
                Breite := 64;
        end
        else begin
                Hoehe := 128;
                Breite := 128;
        end;

        Result.SetSize( Breite, Hoehe );

        y := 0;
        while y < Hoehe do begin
                x := 0;
                while x < Breite do Begin
                        mulStream.Read( Color, 2 );
                        Result.Pixel[x,y] := Color15to32( Color );
                        inc( x );
                end;
                inc( y );
        end;
end;

function TVerdata.SaveGump( Id : Integer ) : TBitmap32;
var     Breite, Hoehe : Word;
        Offset, Extra, Length : Integer;
        LookupList : PIntegerArray;
        Start : Integer;
        x, y, i, j, Size : integer;
        Buffer : Array[0..2047] of Record Value, Run : Word; end;
begin
        Result := TBitmap32.Create;

        Offset := -1;
        for i:=0 to IndexList.Count-1 do
                if ( TVerIndex( IndexList.Items[i] ).FileTyp = typ_gumpart ) and ( TVerIndex( IndexList.Items[i] ).Id = Id ) then begin
                        Offset := TVerIndex( IndexList.Items[i] ).Offset;
                        Extra := TVerIndex( IndexList.Items[i] ).Extra;
                        Length := TVerIndex( IndexList.Items[i] ).Length;
                        break;
                end;
        if Offset = -1 then
                exit;

        mulStream.Seek( Offset, soFromBeginning );

        Breite := Extra shr 16;
        Hoehe := Extra and $FFFF;

        Result.Width := Breite;
        Result.Height := Hoehe;

        GetMem( LookupList, Hoehe*4 );
        Start := mulStream.Position;
        mulStream.Read( LookupList^, Hoehe*4 );

        y := 0;
        while y < Hoehe do begin
                If y < Hoehe-1 then
                        Size := LookupList^[Y+1]-LookupList^[Y]
                else
                        Size := Length div 4 - LookupList^[Y];

                mulStream.Seek( Start + LookupList^[y]*4, soFromBeginning );

                X := 0;
                mulStream.Read( Buffer, Size*4 );
                for i := 0 to size-1 do begin
                        J := X + Buffer[i].Run;
                        while x < j do begin
                                if (x < Result.Width) and (y < Result.Height) and (x >= 0) and (y >= 0) then
                                        Result.Pixel[x,y] := Color15to32( Buffer[i].Value )
                                else
                                        showmessage( 'Buggy Pixel: ' + inttostr( x ) +','+ inttostr( y ) );
                                inc( x );
                        end;
                end;
                inc( y );
        end;

        FreeMem( LookupList, Hoehe*4 );
end;

procedure TVerdata.LoadStaticArt( Id : Integer; FilePath : String );
var     Bitmap : TBitmap;
        x, y, j, i : Integer;
        LookupList : PWordArray;
        Color32 : Integer;
        Color15 : Word;
        XOffset, RunColor: Word;
        VerIndex : TVerIndex;
        Header : Integer;
        Breite, Hoehe : Word;
        dStream : TMemoryStream;
        CountColor, CountColorless : Integer;
        Buffer : Array[0..2047] of Word;
        found : Boolean;
begin
        Bitmap := TBitmap.Create;
        Bitmap.LoadFromFile( FilePath );
        Breite := Bitmap.Width;
        Hoehe := Bitmap.Height;

        Id := Id+$4000;
        mulStream.Seek( 0, soFromEnd );
        VerIndex := TVerIndex.Create;
        VerIndex.ID := Id;
        VerIndex.FileTyp := typ_art;
        VerIndex.Offset := mulStream.Position;

        Header := 1234;
        mulStream.Write( Header, 4 );
        mulStream.Write( Breite, 2 );
        mulStream.Write( Hoehe, 2 );

        dStream := TMemoryStream.Create;
        dStream.Seek( 0, soFromBeginning  );

        GetMem( LookupList, Hoehe*2 );

        CountColor := 0;
        CountColorless := 0;
        y := 0;

        while y < Hoehe do begin
                x := 0;
                LookupList^[y] := dStream.Position div 2;
                while x < Breite do begin
                        Color32 := Bitmap.Canvas.Pixels[x,y];
                        Color15 := ((Color32 and $F80000) shr 19) or ((Color32 and $F800) shr 6) or ((Color32 and $F8) shl 7);
                        
                        if Color15 = 0 then begin
                                if CountColor > 0 then begin
                                        XOffset := CountColorless;
                                        RunColor := CountColor;
                                        dStream.Write( XOffset, 2 );
                                        dStream.Write( RunColor, 2 );
                                        for j := 0 to CountColor-1 do
                                                dStream.Write( Buffer[j], 2 );
                                        CountColor := 0;
                                        CountColorless := 1;
                                end
                                else
                                        inc( CountColorless );
                        end
                        else begin
                                Buffer[ CountColor ] := Color15;
                                inc( CountColor );
                        end;
                        inc( x );
                end;
                Header := 0;
                if CountColor <> 0 then begin
                        XOffset := CountColorless;
                        RunColor := CountColor;
                        dStream.Write( XOffset, 2 );
                        dStream.Write( RunColor, 2 );
                        for j := 0 to CountColor-1 do
                                dStream.Write( Buffer[j], 2 );
                end;
                dStream.Write( Header, 4 );
                inc( y );
                CountColor := 0;
                CountColorless := 0;
        end;

        mulStream.Write( LookupList^, Hoehe*2 );
        FreeMem( LookupList, Hoehe*2 );
        Bitmap.Free;

        dStream.Seek( 0, soFromBeginning );
        mulStream.CopyFrom( dStream, dStream.Size );
        dStream.Free;

        VerIndex.Length := mulStream.Position - VerIndex.Offset;
        VerIndex.Extra := 0;

        found := false;
        for i:=0 to IndexList.Count-1 do
                if ( TVerIndex( IndexList.Items[i] ).FileTyp = typ_art ) and ( TVerIndex( IndexList.Items[i] ).Id = Id ) then begin
                        TVerIndex( IndexList.Items[i] ).Offset := VerIndex.Offset;
                        TVerIndex( IndexList.Items[i] ).Extra := VerIndex.Extra;
                        TVerIndex( IndexList.Items[i] ).Length := VerIndex.Length;
                        found := true;
                        break;
                end;

        if not found then
                IndexList.Add( VerIndex );
end;

procedure TVerdata.LoadMapArt( Id : Integer; FilePath : String );
var     VerIndex : TVerIndex;
        Bitmap : TBitmap;
        x, y, i : Integer;
        Color32 : Integer;
        Color15 : Word;
        found : Boolean;
begin
        Bitmap := TBitmap.Create;
        Bitmap.LoadFromFile( FilePath );

        mulStream.Seek( 0, soFromEnd );
        VerIndex := TVerIndex.Create;
        VerIndex.ID := Id;
        VerIndex.FileTyp := typ_art;
        VerIndex.Offset := mulStream.Position;

        for y := 0 to 21 do begin
                for x := 0 to (y+1)*2-1 do begin
                        Color32 := Bitmap.Canvas.Pixels[ 21-y+x, y ];
                        Color15 := ((Color32 and $F80000) shr 19) or ((Color32 and $F800) shr 6) or ((Color32 and $F8) shl 7);
                        mulStream.Write( Color15, 2 );
                end;
        end;
        for y := 0 to 21 do begin
                for x := 0 to 43-y*2 do begin
                        Color32 := Bitmap.Canvas.Pixels[ y+x, y+22 ];
                        Color15 := ((Color32 and $F80000) shr 19) or ((Color32 and $F800) shr 6) or ((Color32 and $F8) shl 7);
                        mulStream.Write( Color15, 2 );
                end;
        end;

        VerIndex.Length := mulStream.Position - VerIndex.Offset;
        VerIndex.Extra := 0;

        found := false;
        for i:=0 to IndexList.Count-1 do
                if ( TVerIndex( IndexList.Items[i] ).FileTyp = typ_art ) and ( TVerIndex( IndexList.Items[i] ).Id = Id ) then begin
                        TVerIndex( IndexList.Items[i] ).Offset := VerIndex.Offset;
                        TVerIndex( IndexList.Items[i] ).Extra := VerIndex.Extra;
                        TVerIndex( IndexList.Items[i] ).Length := VerIndex.Length;
                        found := true;
                        break;
                end;

        if not found then
                IndexList.Add( VerIndex );
        Bitmap.Free;
end;

procedure TVerdata.LoadTex( Id : Integer; FilePath : String );
var     VerIndex : TVerIndex;
        Hoehe, Breite : Word;
        x, y, i : Integer;
        Bitmap : TBitmap;
        Color32, Color15 : Integer;
        found : Boolean;
begin
        Bitmap := TBitmap.Create;
        Bitmap.LoadFromFile( FilePath );

        mulStream.Seek( 0, soFromEnd );
        VerIndex := TVerIndex.Create;
        VerIndex.ID := Id;
        VerIndex.FileTyp := typ_texmaps;
        VerIndex.Offset := mulStream.Position;

        Breite := Bitmap.Width;
        Hoehe := Bitmap.Height;
        if not ( (( Breite = 64 ) and ( Hoehe = 64 )) or (( Breite = 128 ) and ( Hoehe = 128 )) ) then begin
                Bitmap.Free;
                showmessage( 'Bitmap muss 64x64 oder 128x128 sein.' );
                exit;
        end;

        for y := 0 to Hoehe-1 do begin
                for x := 0 to Breite-1 do begin
                        Color32 := Bitmap.Canvas.Pixels[x,y];
                        Color15 := ((Color32 and $F80000) shr 19) or ((Color32 and $F800) shr 6) or ((Color32 and $F8) shl 7);
                        mulStream.Write( Color15, 2 );
                end;
        end;

        VerIndex.Length := mulStream.Position - VerIndex.Offset;

        if Hoehe = 64 then
                VerIndex.Extra := 0
        else
                VerIndex.Extra := 1;

        found := false;
        for i:=0 to IndexList.Count-1 do
                if ( TVerIndex( IndexList.Items[i] ).FileTyp = typ_texmaps ) and ( TVerIndex( IndexList.Items[i] ).Id = Id ) then begin
                        TVerIndex( IndexList.Items[i] ).Offset := VerIndex.Offset;
                        TVerIndex( IndexList.Items[i] ).Extra := VerIndex.Extra;
                        TVerIndex( IndexList.Items[i] ).Length := VerIndex.Length;
                        found := true;
                        break;
                end;

        if not found then
                IndexList.Add( VerIndex );
        Bitmap.Free;
end;

procedure TVerdata.LoadGump( Id : Integer; FilePath : String );
var     VerIndex : TVerIndex;
        found : Boolean;
        Hoehe, Breite : Word;
        Bitmap : TBitmap;
        x, y, i : Integer;
        LookupList, Size : PIntegerArray;
        Color32, Color15 : Integer;
        CurrentLength, CurrentColor : Integer;
        New : Boolean;
        Buffer : Array[0..2048] of Record Value, Run : Word; end;
        Buf : Record Value, Run : Word; end;
        BufCounter : Integer;
        Stream : TMemoryStream;
begin
        Bitmap := TBitmap.Create;
        Bitmap.LoadFromFile( FilePath );

        mulStream.Seek( 0, soFromEnd );
        VerIndex := TVerIndex.Create;
        VerIndex.ID := Id;
        VerIndex.FileTyp := typ_gumpart;
        VerIndex.Offset := mulStream.Position;

        Stream := TMemoryStream.Create;
        Stream.Seek( 0, soFromBeginning );

        Hoehe := Bitmap.Height;
        Breite := Bitmap.Width;

        GetMem( LookupList, Hoehe*4 );
        GetMem( Size, Hoehe*4 );

        CurrentColor := 0;
        CurrentLength := 0;
        y := 0;
        BufCounter := 0;
        New := true;
        while y < Hoehe do begin
                x := 0;
                while x < Breite do begin
                        Color32 := Bitmap.Canvas.Pixels[ x, y ];
                        Color15 := ((Color32 and $F80000) shr 19) or ((Color32 and $F800) shr 6) or ((Color32 and $F8) shl 7);
                        If Color15 = CurrentColor then begin
                                inc( CurrentLength );
                                New := false;
                        end
                        else if not New then begin
                                Buffer[ BufCounter ].Value := CurrentColor;
                                Buffer[ BufCounter ].Run := CurrentLength;
                                CurrentColor := Color15;
                                CurrentLength := 1;
                                inc( BufCounter );
                        end
                        else begin
                                CurrentLength := 1;
                                CurrentColor := Color15;
                                New := false;
                        end;

                        if x = Breite-1 then begin
                                CurrentColor := Color15;
                                Buffer[ BufCounter ].Value := CurrentColor;
                                Buffer[ BufCounter ].Run := CurrentLength;
                                CurrentLength := 0;
                                CurrentColor := 0;
                                inc( BufCounter );
                                New := true;
                        end;
                        inc( x );
                end;

                for i := 0 to BufCounter-1 do begin
                        Stream.Write( Buffer[ i ], 4 );
                end;

                Size^[ y ] := BufCounter;
                BufCounter := 0;
                inc( y );
        end;

        VerIndex.Length := Stream.Size + Hoehe*4;

        LookupList^[ Hoehe-1 ] := VerIndex.Length div 4 - Size^[ Hoehe-1 ];
        for i := 0 to Hoehe-2 do begin
                LookupList^[Hoehe-2-i] := LookupList^[Hoehe-1-i] - Size^[Hoehe-2-i];
        end;

        mulStream.Write( LookupList^, Hoehe*4 );
        FreeMem( LookupList, Hoehe*4 );
        FreeMem( Size, Hoehe*4 );

        Stream.Seek( 0, soFromBeginning );
        for i := 0 to (Stream.Size div 4)-1 do begin
                Stream.Read( Buf, 4 );
                mulStream.Write( Buf, 4 );
        end;
        Stream.Free;

        VerIndex.Extra := ( Breite shl 16 ) + Hoehe;

        found := false;
        for i:=0 to IndexList.Count-1 do
                if ( TVerIndex( IndexList.Items[i] ).FileTyp = typ_gumpart ) and ( TVerIndex( IndexList.Items[i] ).Id = Id ) then begin
                        TVerIndex( IndexList.Items[i] ).Offset := VerIndex.Offset;
                        TVerIndex( IndexList.Items[i] ).Extra := VerIndex.Extra;
                        TVerIndex( IndexList.Items[i] ).Length := VerIndex.Length;
                        found := true;
                        break;
                end;

        if not found then
                IndexList.Add( VerIndex );
        Bitmap.Free;
end;

procedure TVerdata.LoadAnimFromDirectory( Id : Integer; dirPath : String; UseColor : boolean );
var     RealId : integer;
        VerIndex : TVerIndex;
        Palette : PWordArray;
        LookupList : PIntegerArray;
        Pixels, buf : PByteArray;
        FrameCount, Frame, Anim : Integer;
        CenterX, CenterY : smallint;
        Breite, Hoehe : word;
        xRun, xOffset, yOffset : integer;
        Header : longWord;
        x, y, k, l, p, i : integer;
        Bitmap : TBitmap;
        Liste, SubList, ColorList : TStringList;
        AnimNr, FrameNr : integer;
        CenterList : array[0..174, 0..15] of TCenter;
        Data : TMemoryStream;
        Color32, Color15 : integer;
        Color : Byte;
        FarbListe : array[0..1023] of byte;
        gefunden, found : boolean;
begin
        Liste := TStringList.Create;
        Liste.LoadFromFile( dirPath + '\liste.txt' );

        for l := 0 to ( Liste.Count div 3 )-1 do begin
                SubList := TStringList.Create;
                StrLib.Split( Liste.Strings[l*3], ':', SubList );
                AnimNr := strtoint( SubList[0] );
                FrameNr := strtoint( SubList[1] );
                SubList.Clear;
                StrLib.Split( Liste.Strings[l*3+1], ':', SubList );
                CenterList[ AnimNr ][ FrameNr ].CenterX := strtoint( SubList[1] );
                SubList.Clear;                
                StrLib.Split( Liste.Strings[l*3+2], ':', SubList );
                CenterList[ AnimNr ][ FrameNr ].CenterY := strtoint( SubList[1] );
                SubList.Free;
        end;
        Liste.Free;

        RealId := GetRealAnimId( Id );
        mulStream.Seek( 0, soFromEnd );
        GaugeForm.Activate;
        for Anim := 0 to AnimCount( Id )-1 do begin
                GaugeForm.SetBalken( trunc( (Anim+1)*100 / AnimCount( Id )  ) );
                VerIndex := TVerIndex.Create;                
                GetMem( Palette, 512 );
                if UseColor then begin
                        if FileExists( dirPath + '\palette_' + inttostr( Anim ) + '.txt' ) then begin
                                ColorList := TStringList.Create;
                                ColorList.LoadFromFile( dirPath + '\palette_' + inttostr( Anim ) + '.txt' );
                                for l := 0 to 255 do begin
                                        Palette^[l] := strtoint( ColorList.Strings[l] );
                                end;
                                p := 256;
                                ColorList.Free;
                        end
                        else begin
                                UseColor := false;
                        end;
                end;

                if not UseColor then begin
                        Palette^[0] := 0;
                        p := 1;
                end;

                FrameCount := 0;
                while true do begin
                        if not FileExists( dirPath + '\' + inttostr( Anim ) + '_' + inttostr( FrameCount ) + '.bmp' ) then begin
                                break;
                        end
                        else
                                inc( FrameCount );
                end;

                GetMem( LookupList, FrameCount*4 );

                Data := TMemoryStream.Create;
                Data.Seek( 0, soFromBeginning );

                if FrameCount = 0 then begin
                        VerIndex.Offset := -1;
                        VerIndex.Length := -1;
                        VerIndex.Extra := -1;
                end
                else begin
                        for Frame := 0 to FrameCount-1 do begin
                                Bitmap := TBitmap.Create;
                                Bitmap.LoadFromFile( dirPath + '\' + inttostr( Anim ) + '_' + inttostr( Frame ) + '.bmp' );

                                LookupList^[ Frame ] := data.Position + FrameCount*4 + 4;

                                Breite := Bitmap.Width;
                                Hoehe := Bitmap.Height;

                                data.Write( CenterList[ Anim ][ Frame ].CenterX, 2 );
                                data.Write( CenterList[ Anim ][ Frame ].CenterY, 2 );
                                data.Write( Breite, 2 );
                                data.Write( Hoehe, 2 );

                                xRun := 0;
                                y := 0;
                                x := 0;

                                while y < Hoehe do begin
                                        Color32 := Bitmap.Canvas.Pixels[ x, y ];
                                        Color15 := ((Color32 and $F80000) shr 19) or ((Color32 and $F800) shr 6) or ((Color32 and $F8) shl 7);

                                        //showmessage( inttostr( Color15 ) );
                                        if Color15 = 0 then begin
                                                if xRun <> 0 then begin
                                                        xOffset := x - xRun - CenterList[ Anim ][ Frame ].CenterX;
                                                        yOffset := y - CenterList[ Anim ][ Frame ].CenterY - Hoehe;

                                                        //showmessage( inttostr( Header ) +':'+ inttostr( xRun ) + ':' + inttostr( xOffset ) + ':' + inttostr( yOffset ) + ':'+ inttostr( x ) +':'+ inttostr( y ) +':'+ inttostr( Hoehe ) );                                                        
                                                        if xOffset < 0 then
                                                                xOffset := xOffset + 1024;
                                                        if yOffset < 0 then
                                                                yOffset := yOffset + 1024;

                                                        Header := xRun or ( yOffset shl 12 ) or ( xOffset shl 22 );

                                                        data.Write( Header, 4 );
                                                        data.Write( FarbListe, xRun );

                                                        xRun := 0;
                                                end;
                                        end
                                        else begin
                                                gefunden := false;

                                                for k := 0 to p-1 do
                                                        if Palette^[k] = Color15 then begin
                                                                gefunden := true;
                                                                Color := k;
                                                                break;
                                                        end;

                                                if not gefunden then begin
                                                        if p = 256 then begin
                                                                showmessage( 'Farbe Zuviel: ' + inttostr( Anim ) + ':' + inttostr( Frame ) + ':' + inttostr( Color15 ) );
                                                                Color := 0;
                                                        end
                                                        else begin
                                                                Palette^[p] := Color15;
                                                                Color := p;
                                                                inc( p );
                                                        end;
                                                end;

                                                FarbListe[ xRun ] := Color;
                                                inc( xRun );
                                        end;

                                        if x = Breite-1 then begin
                                                if xRun <> 0 then begin
                                                        xOffset := x - xRun - CenterList[ Anim ][ Frame ].CenterX +1;
                                                        //+1, da normalerweise nach dem letzten pixel das inc(x) nochmal durchgelaufen ist
                                                        //hier aber nicht, deshalb kompensieren
                                                        yOffset := y - CenterList[ Anim ][ Frame ].CenterY - Hoehe;

                                                        if xOffset < 0 then
                                                                xOffset := xOffset + 1024;
                                                        if yOffset < 0 then
                                                                yOffset := yOffset + 1024;

                                                        Header := xRun or ( yOffset shl 12 ) or ( xOffset shl 22 );

                                                        data.Write( Header, 4 );
                                                        data.Write( FarbListe, xRun );

                                                        xRun := 0;
                                                end;
                                                inc( y );
                                                x := 0;
                                        end
                                        else begin
                                                inc( x );
                                        end;
                                end;

                                Bitmap.Free;
                                Header := $7FFF7FFF;
                                Data.Write( Header, 4 );
                        end;
                end;

                if Data.Size > 0 then begin
                        mulStream.Seek( 0, soFromEnd );

                        VerIndex.Offset := mulStream.Position;
                        mulStream.Write( Palette^, 512 );
                        mulStream.Write( FrameCount, 4 );
                        mulStream.Write( LookupList^, FrameCount*4 );

                        data.Seek( 0, soFromBeginning );

                        GetMem( buf, data.Size );
                        data.Read( buf^, data.Size );
                        mulStream.Write( buf^, data.Size );
                        FreeMem( buf, data.Size );

                        VerIndex.Length := mulStream.Position - VerIndex.Offset;
                        VerIndex.Extra := 0;
                end;

                VerIndex.FileTyp := typ_anim;
                VerIndex.ID := RealId + Anim;

                if VerIndex.Offset <> -1 then begin
                        found := false;
                        for i:=0 to IndexList.Count-1 do
                                if ( TVerIndex( IndexList.Items[i] ).FileTyp = typ_anim ) and ( TVerIndex( IndexList.Items[i] ).Id = VerIndex.ID ) then begin
                                        TVerIndex( IndexList.Items[i] ).Offset := VerIndex.Offset;
                                        TVerIndex( IndexList.Items[i] ).Extra := VerIndex.Extra;
                                        TVerIndex( IndexList.Items[i] ).Length := VerIndex.Length;
                                        found := true;
                                        break;
                                end;

                        if not found then
                                IndexList.Add( VerIndex );
                end;

                FreeMem( LookupList, FrameCount*4 );
                FreeMem( Palette, 512 );
                data.Free;
        end;
        GaugeForm.Deactivate;
end;

procedure TVerdata.LoadAnimFromFile( Id : Integer; Filename : String );
var     RealId : integer;
        VerIndex : TVerIndex;
        Anim, AnimationCount, i : integer;
        TypFile, TypAnim : word;
        fStream : TFileStream;
        RecordList : array[0..255] of TIndexRecord;
        buf : PByteArray;
        found : Boolean;
begin
        RealId := GetRealAnimId( Id );

        fStream := TFileStream.Create( Filename, fmOpenRead );
        fStream.Seek( 0, soFromBeginning );
        GaugeForm.Activate;

        fStream.Read( TypFile, 2 );
        if TypFile <> typ_anim then begin
                showmessage( 'Not an Anim File.' );
                exit;
        end;

        fStream.Read( TypAnim, 2 );
        if TypAnim <> GetAnimType( Id ) then begin
                showmessage( 'Wrong Anim Id ( Type ).' );
                exit;
        end;

        AnimationCount := AnimCount( Id );

        fStream.Read( RecordList, AnimationCount*12 );

        for Anim := 0 to AnimationCount-1 do begin
                GaugeForm.SetBalken( trunc( (Anim+1)*100 / AnimationCount ) );
                VerIndex := TVerIndex.Create;

                if ( RecordList[Anim].Offset <> -1 ) and ( RecordList[Anim].Length <> 0 ) then begin
                        mulStream.Seek( 0, soFromEnd );
                        VerIndex.Offset := mulStream.Position;
                        VerIndex.Length := RecordList[Anim].Length;
                        VerIndex.Extra := RecordList[Anim].Extra;

                        GetMem( buf, VerIndex.Length );
                        fStream.Seek( RecordList[Anim].Offset, soFromBeginning );
                        fStream.Read( buf^, VerIndex.Length );
                        mulStream.Write( buf^, VerIndex.Length );
                        FreeMem( buf, VerIndex.Length );
                end
                else begin
                        VerIndex.Offset := RecordList[Anim].Offset;
                        VerIndex.Length := RecordList[Anim].Length;
                        VerIndex.Extra := RecordList[Anim].Extra;
                end;
                VerIndex.FileTyp := typ_anim;
                VerIndex.ID := RealId + Anim;

                if RecordList[Anim].Offset <> -1 then begin
                        found := false;
                        for i:=0 to IndexList.Count-1 do
                                if ( TVerIndex( IndexList.Items[i] ).FileTyp = typ_anim ) and ( TVerIndex( IndexList.Items[i] ).Id = VerIndex.ID ) then begin
                                        TVerIndex( IndexList.Items[i] ).Offset := VerIndex.Offset;
                                        TVerIndex( IndexList.Items[i] ).Extra := VerIndex.Extra;
                                        TVerIndex( IndexList.Items[i] ).Length := VerIndex.Length;
                                        found := true;
                                        break;
                                end;

                        if not found then
                                IndexList.Add( VerIndex );
                end;
        end;

        fStream.Free;
        GaugeForm.Deactivate;
end;


end.
