www.pudn.com > SiegeOfAvalon.rar > SaveFile.pas


unit SaveFile; 
{******************************************************************************} 
{                                                                              } 
{               Siege Of Avalon : Open Source Edition                          } 
{               -------------------------------------                          } 
{                                                                              } 
{ Portions created by Digital Tome L.P. Texas USA are                          } 
{ Copyright ©1999-2000 Digital Tome L.P. Texas USA                             } 
{ All Rights Reserved.                                                         } 
{                                                                              } 
{ Portions created by Team SOAOS are                                           } 
{ Copyright (C) 2003 - Team SOAOS.                                             } 
{                                                                              } 
{                                                                              } 
{ Contributor(s)                                                               } 
{ --------------                                                               } 
{ Dominique Louis                             } 
{                                                                              } 
{                                                                              } 
{                                                                              } 
{ You may retrieve the latest version of this file at the SOAOS project page : } 
{   http://www.sourceforge.com/projects/soaos                                  } 
{                                                                              } 
{ The contents of this file maybe used with permission, subject to             } 
{ the GNU Lesser General Public License Version 2.1 (the "License"); you may   } 
{ not use this file except in compliance with the License. You may             } 
{ obtain a copy of the License at                                              } 
{ http://www.opensource.org/licenses/lgpl-license.php                          } 
{                                                                              } 
{ Software distributed under the License is distributed on an                  } 
{ "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or               } 
{ implied. See the License for the specific language governing                 } 
{ rights and limitations under the License.                                    } 
{                                                                              } 
{ Description                                                                  } 
{ -----------                                                                  } 
{                                                                              } 
{                                                                              } 
{                                                                              } 
{                                                                              } 
{                                                                              } 
{                                                                              } 
{                                                                              } 
{ Requires                                                                     } 
{ --------                                                                     } 
{   DirectX Runtime libraris on Win32                                          } 
{   They are available from...                                                 } 
{   http://www.microsoft.com.                                                  } 
{                                                                              } 
{ Programming Notes                                                            } 
{ -----------------                                                            } 
{                                                                              } 
{                                                                              } 
{                                                                              } 
{                                                                              } 
{ Revision History                                                             } 
{ ----------------                                                             } 
{   July    13 2003 - DL : Initial Upload to CVS                               } 
{                                                                              } 
{******************************************************************************} 
 
interface 
 
uses 
  Windows, 
  SysUtils, 
  Classes, 
  LogFile; 
 
type 
  TSavBlocks = ( sbMap, sbMapKnown, sbCharacter, sbItem, sbTravel, sbJournal, scAbstract, scSoundPlayer, scPathCorner, scTrigger, scSpriteObject, 
    scItem, scWeapon, scQuiver, scBow, scContainer, scDoor, scCharacter, sbStartJournalIndex, sbQuest, siItem, sbAdventure, 
    sbDeathScreen, sbStartQuestIndex, sbStartAdventureIndex, scEventTimer, sbIndex, sbMaxPartyMembers ); 
 
  PBlockPointer = ^TBlockPointer; 
  TBlockPointer = record 
    DataPos : longint; 
    DataSize : longint; 
    MapKnownPos : longint; 
    MapKnownSize : longint; 
  end; 
 
  TSavFile = class( TObject ) 
  private 
    CurrentBlockPointer : PBlockPointer; 
    FCurrentMap : string; 
    MapIndex : TStringList; 
    MapStream : TmemoryStream; 
    CreatedPartyMembers : boolean; 
    CreatedTravelList : boolean; 
    CreatedJournalList : boolean; 
    CreatedAdventureList : boolean; 
    CreatedQuestList : boolean; 
    CreatedMapKnown : boolean; 
    CreatedProperties : boolean; 
    NewFormat : boolean; 
    NewFile : boolean; 
    NeedProperties : boolean; 
    NeedMapKnown : boolean; 
 
    FPartyMembers : TMemoryStream; 
    FTravelList : TStringList; 
    FJournalList : TStringList; 
    FAdventureList : TStringList; 
    FQuestList : TStringList; 
    FMapKnown : TMemoryStream; 
    FProperties : TMemoryStream; 
 
    FFilename : string; 
    NewFilename : string; 
 
    procedure SetCurrentMap( const Value : string ); 
 
    function GetPartyMembers : TMemoryStream; 
    function GetTravelList : TStringList; 
    function GetJournalList : TStringList; 
    function GetAdventureList : TStringList; 
    function GetQuestList : TStringList; 
    function GetMapKnown : TMemoryStream; 
    function GetProperties : TMemoryStream; 
 
    procedure SetPartyMembers( const Value : TMemoryStream ); 
    procedure SetTravelList( const Value : TStringList ); 
    procedure SetJournalList( const Value : TStringList ); 
    procedure SetAdventure( const Value : TStringList ); 
    procedure SetQuestList( const Value : TStringList ); 
    procedure SetMapKnown( const Value : TMemoryStream ); 
    procedure SetProperties( const Value : TMemoryStream ); 
  public 
    CurrentScene : string; 
    JournalIndex : integer; 
    QuestIndex : integer; 
    AdventureIndex : integer; 
    MaxPartyMembers : integer; 
    DeathScreen : string; 
    MapName : string; 
    SceneName : string; 
    constructor Create; 
    destructor Destroy; override; 
    procedure Open( const Filename : string ); 
    procedure Load; 
    procedure Save; 
    procedure SaveAs( const Filename : string ); 
    procedure Close; 
    property CurrentMap : string read FCurrentMap write SetCurrentMap; 
    property MapKnown : TMemoryStream read GetMapKnown write SetMapKnown; 
    property Properties : TMemoryStream read GetProperties write SetProperties; 
    property PartyMembers : TMemoryStream read GetPartyMembers write SetPartyMembers; 
    property TravelList : TStringList read GetTravelList write SetTravelList; 
    property JournalList : TStringList read GetJournalList write SetJournalList; 
    property AdventureList : TStringList read GetAdventureList write SetAdventure; 
    property QuestList : TStringList read GetQuestList write SetQuestList; 
    property Filename : string read FFilename; 
  end; 
 
const 
  EOBMarker = $4242; 
 
implementation 
 
{ TSavFile } 
 
procedure TSavFile.Close; 
var 
  i : integer; 
  BlockPointer : PBlockPointer; 
begin 
  for i := 0 to MapIndex.count - 1 do 
  begin 
    BlockPointer := pointer( MapIndex.objects[ i ] ); 
    Dispose( BlockPointer ); 
  end; 
  MapIndex.free; 
  MapIndex := nil; 
  if CreatedAdventureList then 
  begin 
    FAdventureList.free; 
    FAdventureList := nil; 
  end; 
  if CreatedJournalList then 
  begin 
    FJournalList.free; 
    FJournalList := nil; 
  end; 
  if CreatedPartyMembers then 
  begin 
    FPartyMembers.free; 
    FPartyMembers := nil; 
  end; 
  if CreatedQuestList then 
  begin 
    FQuestList.free; 
    FQuestList := nil; 
  end; 
  if CreatedTravelList then 
  begin 
    FTravelList.free; 
    FTravelList := nil; 
  end; 
  if CreatedMapKnown then 
  begin 
    FMapKnown.free; 
    FMapKnown := nil; 
  end; 
  if CreatedProperties then 
  begin 
    FProperties.free; 
    FProperties := nil; 
  end; 
  MapStream.free; 
  MapStream := nil; 
end; 
 
constructor TSavFile.Create; 
begin 
  inherited; 
  MapIndex := TStringList.create; 
  NeedProperties := true; 
  NeedMapKnown := true; 
end; 
 
destructor TSavFile.Destroy; 
begin 
  Close; 
  inherited; 
end; 
 
function TSavFile.GetAdventureList : TStringList; 
begin 
  if not assigned( FAdventureList ) then 
  begin 
    FAdventureList := TStringList.create; 
    CreatedAdventureList := true; 
  end; 
  result := FAdventureList; 
end; 
 
function TSavFile.GetJournalList : TStringList; 
begin 
  if not assigned( FJournalList ) then 
  begin 
    FJournalList := TStringList.create; 
    CreatedJournalList := true; 
  end; 
  result := FJournalList; 
end; 
 
function TSavFile.GetMapKnown : TMemoryStream; 
var 
  FileStream : TStream; 
begin 
  if not assigned( CurrentBlockPointer ) then 
  begin 
    result := nil; 
    exit; 
  end; 
 
  if not assigned( FMapKnown ) then 
  begin 
    FMapKnown := TMemoryStream.create; 
    CreatedMapKnown := true; 
  end; 
  result := FMapKnown; 
 
  if NeedMapKnown then 
  begin 
    result.Clear; 
    result.Size := CurrentBlockPointer.MapKnownSize; 
 
    if assigned( MapStream ) then 
    begin 
      try 
        MapStream.Position := CurrentBlockPointer.MapKnownPos; 
        MapStream.Read( result.Memory^, CurrentBlockPointer.MapKnownSize ); 
      except 
      end; 
    end 
    else 
    begin 
      FileStream := TFileStream.create( ChangeFileExt( FFilename, '.map' ), fmOpenRead or fmShareCompat ); 
      try 
        FileStream.Position := CurrentBlockPointer.MapKnownPos; 
        FileStream.Read( result.Memory^, CurrentBlockPointer.MapKnownSize ); 
      finally 
        FileStream.free; 
      end; 
    end; 
    NeedMapKnown := false; 
  end; 
end; 
 
function TSavFile.GetPartyMembers : TMemoryStream; 
begin 
  if not assigned( FPartyMembers ) then 
  begin 
    FPartyMembers := TMemoryStream.create; 
    CreatedPartyMembers := true; 
  end; 
  result := FPartyMembers; 
end; 
 
function TSavFile.GetProperties : TMemoryStream; 
var 
  FileStream : TFileStream; 
begin 
  if not assigned( CurrentBlockPointer ) then 
  begin 
    result := nil; 
    exit; 
  end; 
 
  if not assigned( FProperties ) then 
  begin 
    FProperties := TMemoryStream.create; 
    CreatedProperties := true; 
  end; 
  result := FProperties; 
 
  if NeedProperties then 
  begin 
    result.Size := CurrentBlockPointer.DataSize; 
 
    FileStream := TFileStream.create( Filename, fmOpenRead or fmShareCompat ); 
    try 
      FileStream.Position := CurrentBlockPointer.DataPos; 
      FileStream.Read( result.Memory^, CurrentBlockPointer.DataSize ); 
    finally 
      FileStream.free; 
    end; 
    NeedProperties := false; 
  end; 
end; 
 
function TSavFile.GetQuestList : TStringList; 
begin 
  if not assigned( FQuestList ) then 
  begin 
    FQuestList := TStringList.create; 
    CreatedQuestList := true; 
  end; 
  result := FQuestList; 
end; 
 
function TSavFile.GetTravelList : TStringList; 
begin 
  if not assigned( FTravelList ) then 
  begin 
    FTravelList := TStringList.create; 
    CreatedTravelList := true; 
  end; 
  result := FTravelList; 
end; 
 
procedure TSavFile.Load; 
begin 
 
end; 
 
procedure TSavFile.Open( const Filename : string ); 
var 
  Stream : TFileStream; 
  IdxFile : string; 
  BlockPos, BlockSize, L : longint; 
  EOB, BB : word; 
  Block : TSavBlocks; 
  S : string; 
  List : TStringList; 
  BlockPointer : PBlockPointer; 
  i : integer; 
  PrevLevel : string; 
  PlayerBlockOffset : longint; 
  PlayerBlockSize : longint; 
begin 
  FFilename := Filename; 
 
  if assigned( FPartyMembers ) then 
    FPartyMembers.Clear 
  else 
  begin 
    FPartyMembers := TMemoryStream.Create; 
    CreatedPartyMembers := true; 
  end; 
  if assigned( FTravelList ) then 
    FTravelList.Clear 
  else 
  begin 
    FTravelList := TStringList.Create; 
    CreatedTravelList := true; 
  end; 
  if assigned( FJournalList ) then 
    FJournalList.Clear 
  else 
  begin 
    FJournalList := TStringList.Create; 
    CreatedJournalList := true; 
  end; 
  if assigned( FAdventureList ) then 
    FAdventureList.Clear 
  else 
  begin 
    FAdventureList := TStringList.Create; 
    CreatedAdventureList := true; 
  end; 
  if assigned( FQuestList ) then 
    FQuestList.Clear 
  else 
  begin 
    FQuestList := TStringList.Create; 
    CreatedQuestList := true; 
  end; 
  if assigned( FMapKnown ) then 
  begin 
    if CreatedMapKnown then 
      FMapKnown.free; 
    FMapKnown := nil; 
    CreatedMapKnown := false; 
  end; 
  if assigned( FProperties ) then 
  begin 
    if CreatedProperties then 
      FProperties.free; 
    FProperties := nil; 
    CreatedProperties := false; 
  end; 
  MaxPartyMembers := 2; 
 
  for i := 0 to MapIndex.count - 1 do 
  begin 
    BlockPointer := pointer( MapIndex.objects[ i ] ); 
    Dispose( BlockPointer ); 
  end; 
  MapIndex.Clear; 
 
  if not FileExists( FFilename ) then 
  begin 
    NewFormat := true; 
    exit; 
  end; 
 
  PlayerBlockOffset := 0; 
  PlayerBlockSize := 0; 
  MapStream := nil; 
  BlockPointer := nil; 
  PrevLevel := ''; 
  EOB := EOBMarker; 
  List := TStringList.create; 
  try 
 
    IdxFile := ChangeFileExt( Filename, '.idx' ); 
    NewFormat := FileExists( IdxFile ); 
    if NewFormat then 
    begin 
      Stream := TFileStream.create( IdxFile, fmOpenRead or fmShareCompat ); 
      try 
        while Stream.Position < Stream.Size do 
        begin 
          Stream.Read( Block, sizeof( Block ) ); 
          Stream.Read( BlockSize, sizeof( BlockSize ) ); 
          BlockPos := Stream.Position; 
          case Block of 
            sbIndex : 
              begin 
                Stream.Read( L, sizeof( L ) ); 
                if L > 0 then 
                begin 
                  SetLength( S, L ); 
                  Stream.read( S[ 1 ], L ); 
                  MapIndex.text := S; 
                end; 
                for i := 0 to MapIndex.count - 1 do 
                begin 
                  New( BlockPointer ); 
                  MapIndex.Objects[ i ] := pointer( BlockPointer ); 
                  Stream.read( BlockPointer^, sizeof( TBlockPointer ) ); 
                end; 
              end; 
            sbMap : 
              begin 
                if BlockSize = 0 then 
                  S := '' 
                else 
                begin 
                  SetLength( S, BlockSize ); 
                  Stream.Read( S[ 1 ], BlockSize ); 
                end; 
                List.Text := S; 
                MapName := List.Values[ 'Map' ]; 
                SceneName := List.Values[ 'Scene' ]; 
              end; 
            sbTravel : 
              begin 
                if BlockSize > 0 then 
                begin 
                  SetLength( S, L ); 
                  Stream.Read( S[ 1 ], L ); 
                  TravelList.Text := S; 
                end; 
              end; 
            sbJournal : 
              begin 
                if BlockSize > 0 then 
                begin 
                  SetLength( S, BlockSize ); 
                  Stream.Read( S[ 1 ], BlockSize ); 
                  FJournalList.Text := S; 
                end; 
              end; 
            sbStartJournalIndex : 
              begin 
                Stream.Read( L, sizeof( L ) ); 
                JournalIndex := L; 
              end; 
            sbQuest : 
              begin 
                if BlockSize > 0 then 
                begin 
                  SetLength( S, BlockSize ); 
                  Stream.Read( S[ 1 ], BlockSize ); 
                  FQuestList.Text := S; 
                end; 
              end; 
            sbAdventure : 
              begin 
                if BlockSize > 0 then 
                begin 
                  SetLength( S, BlockSize ); 
                  Stream.Read( S[ 1 ], BlockSize ); 
                  FAdventureList.Text := S; 
                end; 
              end; 
            sbStartQuestIndex : 
              begin 
                Stream.Read( L, sizeof( L ) ); 
                QuestIndex := L; 
              end; 
            sbStartAdventureIndex : 
              begin 
                Stream.Read( L, sizeof( L ) ); 
                AdventureIndex := L; 
              end; 
            sbMaxPartyMembers : 
              begin 
                Stream.Read( L, sizeof( L ) ); 
                MaxPartyMembers := L; 
              end; 
            sbDeathScreen : 
              begin 
                DeathScreen := ''; 
                if BlockSize > 0 then 
                begin 
                  SetLength( S, BlockSize ); 
                  Stream.Read( S[ 1 ], BlockSize ); 
                  DeathScreen := S; 
                end; 
              end; 
            sbCharacter, sbItem : 
              begin 
                PlayerBlockOffset := BlockPos - sizeof( BlockSize ) - sizeof( TSavBlocks ); 
                PlayerBlockSize := Stream.Size - ( BlockPos - sizeof( BlockSize ) - sizeof( TSavBlocks ) ); 
                break; 
              end; 
          end; 
          Stream.Seek( BlockPos + BlockSize, soFromBeginning ); 
          Stream.Read( BB, sizeof( BB ) ); 
          if BB <> EOB then 
          begin 
            exit; 
          end; 
        end; 
        Stream.Position := PlayerBlockOffset; 
        FPartyMembers.position := 0; 
        FPartyMembers.CopyFrom( Stream, PlayerBlockSize ); 
        FPartyMembers.position := 0; 
      finally 
        Stream.free; 
      end; 
    end 
    else 
    begin 
      Stream := TFileStream.create( Filename, fmOpenRead or fmShareCompat ); 
      try 
        while Stream.Position < Stream.Size do 
        begin 
          Stream.Read( Block, sizeof( Block ) ); 
          Stream.Read( BlockSize, sizeof( BlockSize ) ); 
          BlockPos := Stream.Position; 
          case Block of 
            sbMap : 
              begin 
                if BlockSize = 0 then 
                  S := '' 
                else 
                begin 
                  SetLength( S, BlockSize ); 
                  Stream.Read( S[ 1 ], BlockSize ); 
                  List.Text := S; 
                end; 
                MapName := List.Values[ 'Map' ]; 
                SceneName := List.Values[ 'Scene' ]; 
              end; 
            sbMapKnown : 
              begin 
                //Extract and place in seperate file 
                //This block will only be found in old files 
                if not assigned( MapStream ) then 
                  MapStream := TMemoryStream.create; 
                Stream.read( L, sizeof( L ) ); 
                if L > 0 then 
                begin 
                  SetLength( S, L ); 
                  Stream.Read( S[ 1 ], L ); 
                  i := MapIndex.IndexOf( S ); 
                  if i >= 0 then 
                  begin 
                    BlockPointer := pointer( MapIndex.Objects[ i ] ); 
                  end 
                  else 
                  begin 
                    New( BlockPointer ); 
                    i := MapIndex.add( S ); 
                    MapIndex.Objects[ i ] := pointer( BlockPointer ); 
                  end; 
                  BlockPointer.MapKnownPos := MapStream.Position; 
                  BlockPointer.MapKnownSize := BlockSize - sizeof( L ) - L; 
                  MapStream.CopyFrom( Stream, BlockPointer.MapKnownSize ); 
                end; 
              end; 
            sbTravel : 
              begin 
                if BlockSize > 0 then 
                begin 
                  SetLength( S, L ); 
                  Stream.Read( S[ 1 ], L ); 
                  TravelList.Text := S; 
                end; 
              end; 
            sbJournal : 
              begin 
                if BlockSize > 0 then 
                begin 
                  SetLength( S, BlockSize ); 
                  Stream.Read( S[ 1 ], BlockSize ); 
                  FJournalList.Text := S; 
                end; 
              end; 
            sbStartJournalIndex : 
              begin 
                Stream.Read( L, sizeof( L ) ); 
                JournalIndex := L; 
              end; 
            sbQuest : 
              begin 
                if BlockSize > 0 then 
                begin 
                  SetLength( S, BlockSize ); 
                  Stream.Read( S[ 1 ], BlockSize ); 
                  FQuestList.Text := S; 
                end; 
              end; 
            sbAdventure : 
              begin 
                if BlockSize > 0 then 
                begin 
                  SetLength( S, BlockSize ); 
                  Stream.Read( S[ 1 ], BlockSize ); 
                  FAdventureList.Text := S; 
                end; 
              end; 
            sbStartQuestIndex : 
              begin 
                Stream.Read( L, sizeof( L ) ); 
                QuestIndex := L; 
              end; 
            sbStartAdventureIndex : 
              begin 
                Stream.Read( L, sizeof( L ) ); 
                AdventureIndex := L; 
              end; 
            sbMaxPartyMembers : 
              begin 
                Stream.Read( L, sizeof( L ) ); 
                MaxPartyMembers := L; 
              end; 
            sbDeathScreen : 
              begin 
                DeathScreen := ''; 
                if BlockSize > 0 then 
                begin 
                  SetLength( S, BlockSize ); 
                  Stream.Read( S[ 1 ], BlockSize ); 
                  DeathScreen := S; 
                end; 
              end; 
            sbCharacter, sbItem : 
              begin 
                if PlayerBlockOffset = 0 then 
                  PlayerBlockOffset := BlockPos - sizeof( BlockSize ) - sizeof( TSavBlocks ); 
                PlayerBlockSize := PlayerBlockSize + sizeof( EOB ) + sizeof( BlockSize ) + sizeof( TSavBlocks ) + BlockSize; 
              end; 
            scAbstract, scSoundPlayer, scPathCorner, scTrigger, scSpriteObject, 
              scItem, scWeapon, scQuiver, scBow, scContainer, scDoor, scCharacter, 
              scEventTimer : 
              begin 
                Stream.Read( L, sizeof( L ) ); 
                if L = 0 then 
                  S := '' 
                else 
                begin 
                  SetLength( S, L ); 
                  Stream.Read( S[ 1 ], L ); 
                end; 
                if S = PrevLevel then 
                begin 
                  inc( BlockPointer.DataSize, BlockSize + sizeof( EOB ) + sizeof( BlockSize ) + sizeof( TSavBlocks ) ); 
                end 
                else 
                begin 
                  i := MapIndex.IndexOf( S ); 
                  if i >= 0 then 
                  begin 
                    BlockPointer := pointer( MapIndex.Objects[ i ] ); 
                  end 
                  else 
                  begin 
                    New( BlockPointer ); 
                    i := MapIndex.add( S ); 
                    MapIndex.Objects[ i ] := pointer( BlockPointer ); 
                  end; 
                  BlockPointer.DataPos := BlockPos - sizeof( BlockSize ) - sizeof( TSavBlocks ); 
                  BlockPointer.DataSize := BlockSize + sizeof( EOB ) + sizeof( BlockSize ) + sizeof( TSavBlocks ); 
                  PrevLevel := S; 
                end; 
              end; 
          end; 
          Stream.Seek( BlockPos + BlockSize, soFromBeginning ); 
          Stream.Read( BB, sizeof( BB ) ); 
          if BB <> EOB then 
          begin 
            exit; 
          end; 
        end; 
        Stream.Position := PlayerBlockOffset; 
        FPartyMembers.position := 0; 
        FPartyMembers.CopyFrom( Stream, PlayerBlockSize ); 
        FPartyMembers.position := 0; 
      finally 
        Stream.free; 
      end; 
    end; 
  finally 
    List.free; 
  end; 
 
  i := MapIndex.IndexOf( FCurrentMap ); 
  if i >= 0 then 
  begin 
    CurrentBlockPointer := pointer( MapIndex.Objects[ i ] ); 
  end 
  else 
  begin 
    CurrentBlockPointer := nil; 
  end; 
end; 
 
procedure TSavFile.Save; 
var 
  Stream : TFileStream; 
  Mem : TmemoryStream; 
  S : string; 
  MapFilename : string; 
  List : TStringList; 
  EOB : word; 
  Block : TSavBlocks; 
  L : longint; 
  i : integer; 
  BlockPointer : PBlockPointer; 
  NewIndex : boolean; 
begin 
  EOB := EOBMarker; 
  List := TStringList.create; 
  try 
    if assigned( CurrentBlockPointer ) then 
      NewIndex := false 
    else 
    begin 
      NewIndex := true; 
      new( CurrentBlockPointer ); 
      i := MapIndex.add( FCurrentMap ); 
      MapIndex.Objects[ i ] := TObject( CurrentBlockPointer ); 
      CurrentBlockPointer.DataPos := 0; 
      CurrentBlockPointer.DataSize := 0; 
      CurrentBlockPointer.MapKnownPos := 0; 
      CurrentBlockPointer.MapKnownSize := 0; 
    end; 
 
    if not NewFormat or NewFile or not NeedProperties then 
    begin 
      if NewFormat and NewFile and NeedProperties then 
      begin 
        //if we're saveing as a new file, but haven't changed anything 
        try 
          if FileExists( FFilename ) then 
            CopyFile( PChar( FFilename ), PChar( NewFilename ), false ) 
          else 
            DeleteFile( NewFilename ); 
        except 
        end; 
      end 
      else if NewFormat and not NewFile and not NeedProperties then 
      begin 
        if FileExists( FFilename ) then 
        begin 
          //Save in place - dont touch unchanged data before current block 
          //Move current block to end 
          Mem := TMemoryStream.create; 
          try 
            Stream := TFileStream.create( FFilename, fmOpenReadWrite or fmShareCompat ); 
            try 
              if NewIndex then 
              begin 
                CurrentBlockPointer.DataPos := Stream.Size; 
                CurrentBlockPointer.DataSize := FProperties.Size; 
                if CurrentBlockPointer.DataSize > 0 then 
                begin 
                  Stream.Position := CurrentBlockPointer.DataPos; 
                  FProperties.position := 0; 
                  Stream.CopyFrom( FProperties, CurrentBlockPointer.DataSize ); 
                end; 
              end 
              else 
              begin 
                for i := 0 to MapIndex.count - 1 do 
                begin 
                  BlockPointer := pointer( MapIndex.objects[ i ] ); 
 
                  if BlockPointer <> CurrentBlockPointer then 
                  begin 
                    if ( BlockPointer.DataSize > 0 ) and ( BlockPointer.DataPos > CurrentBlockPointer.DataPos ) then 
                    begin 
                      Stream.Position := BlockPointer.DataPos; 
                      BlockPointer.DataPos := CurrentBlockPointer.DataPos + Mem.Position; 
                      Mem.CopyFrom( Stream, BlockPointer.DataSize ); 
                    end; 
                  end; 
                end; 
                Stream.Position := CurrentBlockPointer.DataPos; 
                Mem.Position := 0; 
                Stream.CopyFrom( Mem, Mem.Size ); 
                CurrentBlockPointer.DataPos := Stream.Position; 
                CurrentBlockPointer.DataSize := FProperties.Size; 
                if CurrentBlockPointer.DataSize > 0 then 
                begin 
                  FProperties.position := 0; 
                  Stream.CopyFrom( FProperties, CurrentBlockPointer.DataSize ); 
                end; 
              end; 
            finally 
              Stream.free; 
            end; 
          finally 
            Mem.free; 
          end; 
        end 
        else 
        begin 
          Stream := TFileStream.create( FFilename, fmCreate or fmShareCompat ); 
          try 
            if NewIndex then 
            begin 
              CurrentBlockPointer.DataPos := 0; 
              CurrentBlockPointer.DataSize := FProperties.Size; 
              if CurrentBlockPointer.DataSize > 0 then 
              begin 
                Stream.Position := CurrentBlockPointer.DataPos; 
                FProperties.position := 0; 
                Stream.CopyFrom( FProperties, CurrentBlockPointer.DataSize ); 
              end; 
            end 
          finally 
            Stream.free; 
          end; 
        end; 
      end 
      else 
      begin 
        if FileExists( FFilename ) then 
        begin 
          Mem := TMemoryStream.create; 
          try 
            Stream := TFileStream.create( FFilename, fmOpenRead or fmShareCompat ); 
            try 
              for i := 0 to MapIndex.count - 1 do 
              begin 
                BlockPointer := pointer( MapIndex.objects[ i ] ); 
 
                if BlockPointer <> CurrentBlockPointer then 
                begin 
                  if BlockPointer.DataSize > 0 then 
                  begin 
                    Stream.Position := BlockPointer.DataPos; 
                    BlockPointer.DataPos := Mem.Position; 
                    Mem.CopyFrom( Stream, BlockPointer.DataSize ); 
                  end; 
                end; 
              end; 
              //Save Current Block at end 
              if NeedProperties then 
              begin 
                if CurrentBlockPointer.DataSize > 0 then 
                begin 
                  Stream.Position := CurrentBlockPointer.DataPos; 
                  CurrentBlockPointer.DataPos := Mem.Position; 
                  Mem.CopyFrom( Stream, CurrentBlockPointer.DataSize ); 
                end; 
              end 
              else 
              begin 
                CurrentBlockPointer.DataSize := FProperties.Size; 
                if CurrentBlockPointer.DataSize > 0 then 
                begin 
                  FProperties.Position := 0; 
                  CurrentBlockPointer.DataPos := Mem.Position; 
                  Mem.CopyFrom( FProperties, CurrentBlockPointer.DataSize ); 
                end; 
              end; 
            finally 
              Stream.free; 
            end; 
 
            if NewFile then 
              Mem.SaveToFile( NewFilename ) 
            else 
              Mem.SaveToFile( FFilename ); 
          finally 
            Mem.free; 
          end; 
        end 
        else 
        begin 
          if NewFile then 
            Stream := TFileStream.create( NewFilename, fmCreate or fmShareCompat ) 
          else 
            Stream := TFileStream.create( FFilename, fmCreate or fmShareCompat ); 
          try 
            if NewIndex then 
            begin 
              CurrentBlockPointer.DataPos := 0; 
              CurrentBlockPointer.DataSize := FProperties.Size; 
              if CurrentBlockPointer.DataSize > 0 then 
              begin 
                Stream.Position := CurrentBlockPointer.DataPos; 
                FProperties.position := 0; 
                Stream.CopyFrom( FProperties, CurrentBlockPointer.DataSize ); 
              end; 
            end 
          finally 
            Stream.free; 
          end; 
        end; 
      end; 
    end; 
 
    //Save map known info if necessary 
    if NewFile then 
    begin 
      MapFilename := ChangeFileExt( NewFilename, '.map' ); 
      if not assigned( MapStream ) then 
      begin 
        try 
          S := ChangeFileExt( FFilename, '.map' ); 
          if FileExists( S ) then 
            CopyFile( PChar( S ), PChar( MapFilename ), false ); 
        except 
        end; 
      end; 
    end 
    else 
      MapFilename := ChangeFileExt( FFilename, '.map' ); 
    if not NeedMapKnown then 
    begin 
      if assigned( MapStream ) then 
      begin 
        if NewIndex then 
        begin 
          CurrentBlockPointer.MapKnownPos := MapStream.Size; 
          CurrentBlockPointer.MapKnownSize := FMapKnown.Size; 
        end; 
        MapStream.Position := CurrentBlockPointer.MapKnownPos; 
        FMapKnown.Position := 0; 
        MapStream.CopyFrom( FMapKnown, CurrentBlockPointer.MapKnownSize ); 
      end 
      else 
      begin 
        if FileExists( MapFilename ) then 
          Stream := TFileStream.create( MapFilename, fmOpenReadWrite or fmShareCompat ) 
        else 
          Stream := TFileStream.create( MapFilename, fmCreate or fmShareCompat ); 
        try 
          if NewIndex then 
          begin 
            CurrentBlockPointer.MapKnownPos := Stream.Size; 
            CurrentBlockPointer.MapKnownSize := FMapKnown.Size; 
          end; 
          Stream.Position := CurrentBlockPointer.MapKnownPos; 
          FMapKnown.Position := 0; 
          Stream.CopyFrom( FMapKnown, CurrentBlockPointer.MapKnownSize ); 
        finally 
          Stream.free; 
        end; 
      end; 
    end; 
    if assigned( MapStream ) then 
      MapStream.SaveToFile( MapFilename ); 
 
    if NewFile then 
      Stream := TFileStream.create( ChangeFileExt( NewFilename, '.idx' ), fmCreate or fmShareCompat ) 
    else 
      Stream := TFileStream.create( ChangeFileExt( FFilename, '.idx' ), fmCreate or fmShareCompat ); 
    try 
 
      Block := sbIndex; 
      Stream.Write( Block, sizeof( Block ) ); 
      S := MapIndex.Text; 
      L := Length( S ) + MapIndex.count * sizeof( TBlockPointer ) + sizeof( L ); 
      Stream.write( L, sizeof( L ) ); 
      L := Length( S ); 
      Stream.write( L, sizeof( L ) ); 
      if L > 0 then 
        Stream.write( S[ 1 ], L ); 
      for i := 0 to MapIndex.count - 1 do 
      begin 
        Stream.write( PBlockPointer( MapIndex.objects[ i ] )^, sizeof( TBlockPointer ) ); 
      end; 
      Stream.write( EOB, sizeof( EOB ) ); 
 
      Block := sbMap; 
      Stream.Write( Block, sizeof( Block ) ); 
      List.Clear; 
      S := 'Map=' + MapName; 
      List.add( S ); 
      S := 'Scene=' + SceneName; 
      List.add( S ); 
      S := List.Text; 
      L := Length( S ); 
      Stream.write( L, sizeof( L ) ); 
      if L > 0 then 
        Stream.write( S[ 1 ], L ); 
      Stream.write( EOB, sizeof( EOB ) ); 
 
      Block := sbTravel; 
      Stream.Write( Block, sizeof( Block ) ); 
      i := TravelList.IndexOf( FCurrentMap ); 
      if i < 0 then 
        TravelList.add( FCurrentMap ); 
      S := FCurrentMap + '|' + lowercase( CurrentScene ); 
      i := TravelList.IndexOf( S ); 
      if i < 0 then 
        TravelList.add( S ); 
      S := TravelList.Text; 
      L := Length( S ); 
      Stream.write( L, sizeof( L ) ); 
      if L > 0 then 
        Stream.write( S[ 1 ], L ); 
      Stream.write( EOB, sizeof( EOB ) ); 
 
      Block := sbJournal; 
      Stream.Write( Block, sizeof( Block ) ); 
      S := JournalList.Text; 
      L := Length( S ); 
      Stream.write( L, sizeof( L ) ); 
      if L > 0 then 
        Stream.write( S[ 1 ], L ); 
      Stream.write( EOB, sizeof( EOB ) ); 
 
      Block := sbStartJournalIndex; 
      Stream.Write( Block, sizeof( Block ) ); 
      L := sizeof( JournalIndex ); 
      Stream.write( L, sizeof( L ) ); 
      Stream.write( JournalIndex, sizeof( JournalIndex ) ); 
      Stream.write( EOB, sizeof( EOB ) ); 
 
      Block := sbQuest; 
      Stream.Write( Block, sizeof( Block ) ); 
      S := QuestList.Text; 
      L := Length( S ); 
      Stream.write( L, sizeof( L ) ); 
      if L > 0 then 
        Stream.write( S[ 1 ], L ); 
      Stream.write( EOB, sizeof( EOB ) ); 
 
      Block := sbAdventure; 
      Stream.Write( Block, sizeof( Block ) ); 
      S := AdventureList.Text; 
      L := Length( S ); 
      Stream.write( L, sizeof( L ) ); 
      if L > 0 then 
        Stream.write( S[ 1 ], L ); 
      Stream.write( EOB, sizeof( EOB ) ); 
 
      Block := sbStartQuestIndex; 
      Stream.Write( Block, sizeof( Block ) ); 
      L := sizeof( QuestIndex ); 
      Stream.write( L, sizeof( L ) ); 
      Stream.write( QuestIndex, sizeof( QuestIndex ) ); 
      Stream.write( EOB, sizeof( EOB ) ); 
 
      Block := sbStartAdventureIndex; 
      Stream.Write( Block, sizeof( Block ) ); 
      L := sizeof( AdventureIndex ); 
      Stream.write( L, sizeof( L ) ); 
      Stream.write( AdventureIndex, sizeof( AdventureIndex ) ); 
      Stream.write( EOB, sizeof( EOB ) ); 
 
      Block := sbMaxPartyMembers; 
      Stream.Write( Block, sizeof( Block ) ); 
      L := sizeof( MaxPartyMembers ); 
      Stream.write( L, sizeof( L ) ); 
      Stream.write( MaxPartyMembers, sizeof( MaxPartyMembers ) ); 
      Stream.write( EOB, sizeof( EOB ) ); 
 
      Block := sbDeathScreen; 
      Stream.Write( Block, sizeof( Block ) ); 
      S := DeathScreen; 
      L := Length( S ); 
      Stream.write( L, sizeof( L ) ); 
      if L > 0 then 
        Stream.write( S[ 1 ], L ); 
      Stream.write( EOB, sizeof( EOB ) ); 
 
      FPartyMembers.position := 0; 
      Stream.CopyFrom( FPartyMembers, FPartyMembers.Size ); 
    finally 
      Stream.free; 
    end; 
  finally 
    List.free; 
  end; 
 
  NewFormat := true; 
end; 
 
procedure TSavFile.SaveAs( const Filename : string ); 
begin 
  if lowercase( Filename ) = lowercase( FFileName ) then 
    Save 
  else 
  begin 
    NewFilename := Filename; 
    NewFile := true; 
    Save; 
    FFilename := NewFilename; 
    NewFilename := ''; 
    NewFile := false; 
  end; 
end; 
 
procedure TSavFile.SetAdventure( const Value : TStringList ); 
begin 
  if CreatedAdventureList then 
  begin 
    FAdventureList.free; 
    FAdventureList := nil; 
    CreatedAdventureList := false; 
  end; 
  FAdventureList := Value; 
end; 
 
procedure TSavFile.SetJournalList( const Value : TStringList ); 
begin 
  if CreatedJournalList then 
  begin 
    FJournalList.free; 
    FJournalList := nil; 
    CreatedJournalList := false; 
  end; 
  FJournalList := Value; 
end; 
 
procedure TSavFile.SetMapKnown( const Value : TMemoryStream ); 
begin 
  if CreatedMapKnown then 
  begin 
    FMapKnown.free; 
    FMapKnown := nil; 
    CreatedMapKnown := false; 
  end; 
  FMapKnown := Value; 
  NeedMapKnown := false; 
end; 
 
procedure TSavFile.SetCurrentMap( const Value : string ); 
var 
  i : integer; 
begin 
  FCurrentMap := Value; 
  i := MapIndex.IndexOf( FCurrentMap ); 
  if assigned( FMapKnown ) then 
  begin 
    if CreatedMapKnown then 
      FMapKnown.free; 
    FMapKnown := nil; 
    CreatedMapKnown := false; 
  end; 
  if assigned( FProperties ) then 
  begin 
    if CreatedProperties then 
      FProperties.free; 
    FProperties := nil; 
    CreatedProperties := false; 
  end; 
  NeedProperties := true; 
  NeedMapKnown := true; 
  if i >= 0 then 
  begin 
    CurrentBlockPointer := pointer( MapIndex.Objects[ i ] ); 
  end 
  else 
  begin 
    CurrentBlockPointer := nil; 
  end; 
end; 
 
procedure TSavFile.SetPartyMembers( const Value : TMemoryStream ); 
begin 
  if CreatedPartyMembers then 
  begin 
    FPartyMembers.free; 
    FPartyMembers := nil; 
    CreatedPartyMembers := false; 
  end; 
  FPartyMembers := Value; 
end; 
 
procedure TSavFile.SetProperties( const Value : TMemoryStream ); 
begin 
  if CreatedProperties then 
  begin 
    FProperties.free; 
    FProperties := nil; 
    CreatedProperties := false; 
  end; 
  FProperties := Value; 
  NeedProperties := false; 
end; 
 
procedure TSavFile.SetQuestList( const Value : TStringList ); 
begin 
  if CreatedQuestList then 
  begin 
    FQuestList.free; 
    FQuestList := nil; 
    CreatedQuestList := false; 
  end; 
  FQuestList := Value; 
end; 
 
procedure TSavFile.SetTravelList( const Value : TStringList ); 
begin 
  if CreatedTravelList then 
  begin 
    FTravelList.free; 
    FTravelList := nil; 
    CreatedTravelList := false; 
  end; 
  FTravelList := Value; 
end; 
 
end.