- Changed TAnimDataProvider to load as much animdata as the input files provides
- Added TBufferedStream.GetSize - Added TIndexedMulProvider.EntryCount - Added TAnimDataProvider.AnimCount - Added TLandscape.MaxStaticID - Some more code cleanups
This commit is contained in:
parent
bdc97b89f5
commit
73e704e5fa
|
@ -183,6 +183,7 @@ type
|
|||
FOnStaticHued: TStaticChangedEvent;
|
||||
FOpenRequests: TBits;
|
||||
FWriteMap: TBits;
|
||||
FMaxStaticID: Cardinal;
|
||||
{ Methods }
|
||||
function GetMapBlock(AX, AY: Word): TMapBlock;
|
||||
function GetMapCell(AX, AY: Word): TMapCell;
|
||||
|
@ -207,6 +208,7 @@ type
|
|||
property MapCell[X, Y: Word]: TMapCell read GetMapCell;
|
||||
property StaticList[X, Y: Word]: TStaticItemList read GetStaticList;
|
||||
property Normals[X, Y: Word]: TNormals read GetNormals;
|
||||
property MaxStaticID: Cardinal read FMaxStaticID;
|
||||
property OnChange: TLandscapeChangeEvent read FOnChange write FOnChange;
|
||||
property OnMapChanged: TMapChangedEvent read FOnMapChanged write FOnMapChanged;
|
||||
property OnNewBlock: TNewBlockEvent read FOnNewBlock write FOnNewBlock;
|
||||
|
@ -594,6 +596,11 @@ begin
|
|||
for i := 0 to FWriteMap.Size - 1 do
|
||||
FWriteMap[i] := True;
|
||||
|
||||
FMaxStaticID := Min(Min(ResMan.Animdata.AnimCount, ResMan.Tiledata.StaticCount),
|
||||
ResMan.Art.EntryCount - $4000);
|
||||
Logger.Send([lcClient, lcInfo], 'Landscape recognizes $%x StaticTile IDs.',
|
||||
[FMaxStaticId]);
|
||||
|
||||
RegisterPacketHandler($04, TPacketHandler.Create(0, @OnBlocksPacket));
|
||||
RegisterPacketHandler($06, TPacketHandler.Create(8, @OnDrawMapPacket));
|
||||
RegisterPacketHandler($07, TPacketHandler.Create(10, @OnInsertStaticPacket));
|
||||
|
|
|
@ -1,123 +1,133 @@
|
|||
(*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at
|
||||
* http://www.opensource.org/licenses/cddl1.php.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at
|
||||
* http://www.opensource.org/licenses/cddl1.php. If applicable,
|
||||
* add the following below this CDDL HEADER, with the fields enclosed
|
||||
* by brackets "[]" replaced with your own identifying * information:
|
||||
* Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2009 Andreas Schneider
|
||||
*)
|
||||
unit UAnimDataProvider;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, UMulProvider, UMulBlock, UAnimData;
|
||||
|
||||
type
|
||||
|
||||
TAnimDataArray = array[$0..$3FFF] of TAnimData;
|
||||
|
||||
{ TAnimDataProvider }
|
||||
|
||||
TAnimDataProvider = class(TMulProvider)
|
||||
constructor Create(AData: TStream; AReadOnly: Boolean = False); overload;
|
||||
override;
|
||||
constructor Create(AData: string; AReadOnly: Boolean = False); overload;
|
||||
override;
|
||||
destructor Destroy; override;
|
||||
protected
|
||||
FAnimData: TAnimDataArray;
|
||||
function CalculateOffset(AID: Integer): Integer; override;
|
||||
function GetData(AID, AOffset: Integer): TAnimData; override;
|
||||
procedure InitArray;
|
||||
procedure SetData(AID, AOffset: Integer; ABlock: TMulBlock); override;
|
||||
public
|
||||
property AnimData: TAnimDataArray read FAnimData;
|
||||
function GetBlock(AID: Integer): TAnimData; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TAnimDataProvider }
|
||||
|
||||
constructor TAnimDataProvider.Create(AData: TStream; AReadOnly: Boolean);
|
||||
begin
|
||||
inherited Create(AData, AReadOnly);
|
||||
InitArray;
|
||||
end;
|
||||
|
||||
constructor TAnimDataProvider.Create(AData: string; AReadOnly: Boolean);
|
||||
begin
|
||||
inherited Create(AData, AReadOnly);
|
||||
InitArray;
|
||||
end;
|
||||
|
||||
destructor TAnimDataProvider.Destroy;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
for i := 0 to Length(FAnimData) - 1 do
|
||||
FreeAndNil(FAnimData[i]);
|
||||
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TAnimDataProvider.CalculateOffset(AID: Integer): Integer;
|
||||
begin
|
||||
Result := GetAnimDataOffset(AID);
|
||||
end;
|
||||
|
||||
function TAnimDataProvider.GetData(AID, AOffset: Integer): TAnimData;
|
||||
begin
|
||||
Result := FAnimData[AID];
|
||||
end;
|
||||
|
||||
procedure TAnimDataProvider.InitArray;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
for i := 0 to Length(FAnimData) - 1 do
|
||||
begin
|
||||
FData.Position := GetAnimDataOffset(i);
|
||||
FAnimData[i] := TAnimData.Create(FData);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAnimDataProvider.SetData(AID, AOffset: Integer; ABlock: TMulBlock);
|
||||
begin
|
||||
FreeAndNil(FAnimData[AID]);
|
||||
FAnimData[AID] := TAnimData(ABlock.Clone);
|
||||
|
||||
if not FReadOnly then
|
||||
begin
|
||||
FData.Position := AOffset;
|
||||
ABlock.Write(FData);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAnimDataProvider.GetBlock(AID: Integer): TAnimData;
|
||||
begin
|
||||
Result := FAnimData[AID].Clone;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
(*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at
|
||||
* http://www.opensource.org/licenses/cddl1.php.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at
|
||||
* http://www.opensource.org/licenses/cddl1.php. If applicable,
|
||||
* add the following below this CDDL HEADER, with the fields enclosed
|
||||
* by brackets "[]" replaced with your own identifying * information:
|
||||
* Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2009 Andreas Schneider
|
||||
*)
|
||||
unit UAnimDataProvider;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, UMulProvider, UMulBlock, UAnimData;
|
||||
|
||||
type
|
||||
|
||||
TAnimDataArray = array of TAnimData;
|
||||
|
||||
{ TAnimDataProvider }
|
||||
|
||||
TAnimDataProvider = class(TMulProvider)
|
||||
constructor Create(AData: TStream; AReadOnly: Boolean = False); overload;
|
||||
override;
|
||||
constructor Create(AData: string; AReadOnly: Boolean = False); overload;
|
||||
override;
|
||||
destructor Destroy; override;
|
||||
protected
|
||||
FAnimData: TAnimDataArray;
|
||||
FAnimCount: Cardinal;
|
||||
function CalculateOffset(AID: Integer): Integer; override;
|
||||
function GetData(AID, AOffset: Integer): TAnimData; override;
|
||||
procedure InitArray;
|
||||
procedure SetData(AID, AOffset: Integer; ABlock: TMulBlock); override;
|
||||
public
|
||||
property AnimData: TAnimDataArray read FAnimData;
|
||||
property AnimCount: Cardinal read FAnimCount;
|
||||
function GetBlock(AID: Integer): TAnimData; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Logging;
|
||||
|
||||
{ TAnimDataProvider }
|
||||
|
||||
constructor TAnimDataProvider.Create(AData: TStream; AReadOnly: Boolean);
|
||||
begin
|
||||
inherited Create(AData, AReadOnly);
|
||||
InitArray;
|
||||
end;
|
||||
|
||||
constructor TAnimDataProvider.Create(AData: string; AReadOnly: Boolean);
|
||||
begin
|
||||
inherited Create(AData, AReadOnly);
|
||||
InitArray;
|
||||
end;
|
||||
|
||||
destructor TAnimDataProvider.Destroy;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
for i := 0 to Length(FAnimData) - 1 do
|
||||
FreeAndNil(FAnimData[i]);
|
||||
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TAnimDataProvider.CalculateOffset(AID: Integer): Integer;
|
||||
begin
|
||||
Result := GetAnimDataOffset(AID);
|
||||
end;
|
||||
|
||||
function TAnimDataProvider.GetData(AID, AOffset: Integer): TAnimData;
|
||||
begin
|
||||
Result := FAnimData[AID];
|
||||
end;
|
||||
|
||||
procedure TAnimDataProvider.InitArray;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
FData.Position := 0;
|
||||
FAnimCount := (FData.Size div AnimDataGroupSize) * 8;
|
||||
Logger.Send([lcInfo], 'Loading $%x AnimData entries.', [FAnimCount]);
|
||||
SetLength(FAnimData, FAnimCount);
|
||||
for i := 0 to FAnimCount - 1 do
|
||||
begin
|
||||
if i mod 8 = 0 then
|
||||
FData.Seek(4, soFromCurrent);
|
||||
FAnimData[i] := TAnimData.Create(FData);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAnimDataProvider.SetData(AID, AOffset: Integer; ABlock: TMulBlock);
|
||||
begin
|
||||
FreeAndNil(FAnimData[AID]);
|
||||
FAnimData[AID] := TAnimData(ABlock.Clone);
|
||||
|
||||
if not FReadOnly then
|
||||
begin
|
||||
FData.Position := AOffset;
|
||||
ABlock.Write(FData);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAnimDataProvider.GetBlock(AID: Integer): TAnimData;
|
||||
begin
|
||||
Result := FAnimData[AID].Clone;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
|
|
@ -1,387 +1,391 @@
|
|||
(*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at
|
||||
* http://www.opensource.org/licenses/cddl1.php.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at
|
||||
* http://www.opensource.org/licenses/cddl1.php. If applicable,
|
||||
* add the following below this CDDL HEADER, with the fields enclosed
|
||||
* by brackets "[]" replaced with your own identifying * information:
|
||||
* Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2009 Andreas Schneider
|
||||
*)
|
||||
unit UMulProvider;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
SysUtils, Classes, UBufferedStreams, UMulBlock, UGenericIndex;
|
||||
|
||||
type
|
||||
TOnProgressEvent = procedure(Total, Current: Integer) of object;
|
||||
|
||||
{ TMulEventHandler }
|
||||
|
||||
TMulEventHandler = class
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
protected
|
||||
FEvents: TList;
|
||||
public
|
||||
procedure RegisterEvent(AEvent: TMulBlockChanged);
|
||||
procedure UnregisterEvent(AEvent: TMulBlockChanged);
|
||||
procedure FireEvents(ABlock: TMulBlock);
|
||||
end;
|
||||
|
||||
{ TMulProvider }
|
||||
|
||||
TMulProvider = class
|
||||
constructor Create; overload; virtual;
|
||||
constructor Create(AData: TStream; AReadOnly: Boolean = False); overload; virtual;
|
||||
constructor Create(AData: string; AReadOnly: Boolean = False); overload; virtual;
|
||||
destructor Destroy; override;
|
||||
protected
|
||||
FData: TStream;
|
||||
FOwnsData: Boolean;
|
||||
FReadOnly: Boolean;
|
||||
FChangeEvents: TMulEventHandler;
|
||||
FFinishedEvents: TMulEventHandler;
|
||||
function CalculateOffset(AID: Integer): Integer; virtual; abstract;
|
||||
function GetData(AID, AOffset: Integer): TMulBlock; virtual; abstract;
|
||||
procedure SetData(AID, AOffset: Integer; ABlock: TMulBlock); virtual;
|
||||
procedure OnChanged(ABlock: TMulBlock);
|
||||
procedure OnFinished(ABlock: TMulBlock);
|
||||
public
|
||||
function GetBlock(AID: Integer): TMulBlock; virtual;
|
||||
procedure SetBlock(AID: Integer; ABlock: TMulBlock); virtual;
|
||||
procedure RegisterOnChangeEvent(AEvent: TMulBlockChanged);
|
||||
procedure UnregisterOnChangeEvent(AEvent: TMulBlockChanged);
|
||||
procedure RegisterOnFinishedEvent(AEvent: TMulBlockChanged);
|
||||
procedure UnregisterOnFinishedEvent(AEvent: TMulBlockChanged);
|
||||
property Block[ID: Integer]: TMulBlock read GetBlock write SetBlock;
|
||||
property Data: TStream read FData;
|
||||
end;
|
||||
|
||||
{ TIndexedMulProvider }
|
||||
|
||||
TIndexedMulProvider = class(TMulProvider)
|
||||
constructor Create(AData, AIndex: TStream; AReadOnly: Boolean = False); overload; virtual;
|
||||
constructor Create(AData, AIndex: string; AReadOnly: Boolean = False); overload; virtual;
|
||||
destructor Destroy; override;
|
||||
protected
|
||||
FIndex: TBufferedReader;
|
||||
function CalculateIndexOffset(AID: Integer): Integer; virtual;
|
||||
function GetData(AID: Integer; AIndex: TGenericIndex): TMulBlock; reintroduce; virtual; abstract;
|
||||
procedure SetData(AID: Integer; AIndex: TGenericIndex; ABlock: TMulBlock); reintroduce; virtual;
|
||||
function GetVarious(AID: Integer; ABlock: TMulBlock; ADefault: Integer): Integer; virtual;
|
||||
public
|
||||
function GetBlock(AID: Integer): TMulBlock; override;
|
||||
procedure GetBlockEx(AID: Integer; var ABlock: TMulBlock; var AIndex: TGenericIndex); virtual;
|
||||
procedure SetBlock(AID: Integer; ABlock: TMulBlock); override;
|
||||
function Exists(AID: Integer): Boolean; virtual;
|
||||
procedure Defragment(ATempStream: TStream; AOnProgress: TOnProgressEvent = nil); virtual;
|
||||
property Index: TBufferedReader read FIndex;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
type
|
||||
PMethod = ^TMethod;
|
||||
|
||||
{ TMulEventHandler }
|
||||
|
||||
constructor TMulEventHandler.Create;
|
||||
begin
|
||||
inherited;
|
||||
FEvents := TList.Create;
|
||||
end;
|
||||
|
||||
destructor TMulEventHandler.Destroy;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if Assigned(FEvents) then
|
||||
begin
|
||||
for i := 0 to FEvents.Count - 1 do
|
||||
Dispose(PMethod(FEvents.Items[i]));
|
||||
FreeAndNil(FEvents);
|
||||
end;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TMulEventHandler.FireEvents(ABlock: TMulBlock);
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
for i := 0 to FEvents.Count - 1 do
|
||||
TMulBlockChanged(FEvents.Items[i]^)(ABlock);
|
||||
end;
|
||||
|
||||
procedure TMulEventHandler.RegisterEvent(AEvent: TMulBlockChanged);
|
||||
var
|
||||
eventInfo: PMethod;
|
||||
begin
|
||||
UnregisterEvent(AEvent);
|
||||
New(eventInfo);
|
||||
eventInfo^.Code := TMethod(AEvent).Code;
|
||||
eventInfo^.Data := TMethod(AEvent).Data;
|
||||
FEvents.Add(eventInfo);
|
||||
end;
|
||||
|
||||
procedure TMulEventHandler.UnregisterEvent(AEvent: TMulBlockChanged);
|
||||
var
|
||||
i: Integer;
|
||||
|
||||
function RemoveEntry: Boolean;
|
||||
begin
|
||||
Dispose(PMethod(FEvents.Items[i]));
|
||||
FEvents.Delete(i);
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
begin
|
||||
i := 0;
|
||||
while (i < FEvents.Count) and ((TMethod(AEvent).Code <> TMethod(FEvents.Items[i]^).Code) or (TMethod(AEvent).Data <> TMethod(FEvents.Items[i]^).Data) or not RemoveEntry) do
|
||||
Inc(i);
|
||||
end;
|
||||
|
||||
{ TMulProvider }
|
||||
|
||||
constructor TMulProvider.Create(AData: TStream; AReadOnly: Boolean = False);
|
||||
begin
|
||||
Create;
|
||||
FData := AData;
|
||||
FOwnsData := False;
|
||||
FReadOnly := AReadOnly;
|
||||
end;
|
||||
|
||||
constructor TMulProvider.Create(AData: string; AReadOnly: Boolean = False);
|
||||
var
|
||||
mode: Word;
|
||||
begin
|
||||
Create;
|
||||
if AReadOnly then
|
||||
mode := fmOpenRead or fmShareDenyWrite
|
||||
else
|
||||
mode := fmOpenReadWrite or fmShareDenyWrite;
|
||||
FData := TFileStream.Create(AData, mode);
|
||||
FOwnsData := True;
|
||||
FReadOnly := AReadOnly;
|
||||
end;
|
||||
|
||||
constructor TMulProvider.Create;
|
||||
begin
|
||||
inherited;
|
||||
FChangeEvents := TMulEventHandler.Create;
|
||||
FFinishedEvents := TMulEventHandler.Create;
|
||||
end;
|
||||
|
||||
destructor TMulProvider.Destroy;
|
||||
begin
|
||||
if FOwnsData and Assigned(FData) then FreeAndNil(FData);
|
||||
if Assigned(FChangeEvents) then FreeAndNil(FChangeEvents);
|
||||
if Assigned(FFinishedEvents) then FreeAndNil(FFinishedEvents);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TMulProvider.GetBlock(AID: Integer): TMulBlock;
|
||||
begin
|
||||
Result := GetData(AID, CalculateOffset(AID));
|
||||
Result.OnChanged := @OnChanged;
|
||||
Result.OnFinished := @OnFinished;
|
||||
end;
|
||||
|
||||
procedure TMulProvider.OnChanged(ABlock: TMulBlock);
|
||||
begin
|
||||
SetBlock(ABlock.ID, ABlock);
|
||||
FChangeEvents.FireEvents(ABlock);
|
||||
end;
|
||||
|
||||
procedure TMulProvider.OnFinished(ABlock: TMulBlock);
|
||||
begin
|
||||
FFinishedEvents.FireEvents(ABlock);
|
||||
ABlock.Free;
|
||||
end;
|
||||
|
||||
procedure TMulProvider.RegisterOnChangeEvent(AEvent: TMulBlockChanged);
|
||||
begin
|
||||
FChangeEvents.RegisterEvent(AEvent);
|
||||
end;
|
||||
|
||||
procedure TMulProvider.RegisterOnFinishedEvent(AEvent: TMulBlockChanged);
|
||||
begin
|
||||
FFinishedEvents.RegisterEvent(AEvent);
|
||||
end;
|
||||
|
||||
procedure TMulProvider.SetBlock(AID: Integer; ABlock: TMulBlock);
|
||||
begin
|
||||
if FReadOnly then Exit;
|
||||
SetData(AID, CalculateOffset(AID), ABlock);
|
||||
end;
|
||||
|
||||
procedure TMulProvider.SetData(AID, AOffset: Integer; ABlock: TMulBlock);
|
||||
begin
|
||||
if FReadOnly then Exit;
|
||||
FData.Position := AOffset;
|
||||
ABlock.Write(FData);
|
||||
end;
|
||||
|
||||
procedure TMulProvider.UnregisterOnChangeEvent(AEvent: TMulBlockChanged);
|
||||
begin
|
||||
FChangeEvents.UnregisterEvent(AEvent);
|
||||
end;
|
||||
|
||||
procedure TMulProvider.UnregisterOnFinishedEvent(AEvent: TMulBlockChanged);
|
||||
begin
|
||||
FFinishedEvents.UnregisterEvent(AEvent);
|
||||
end;
|
||||
|
||||
{ TIndexedMulProvider }
|
||||
|
||||
function TIndexedMulProvider.CalculateIndexOffset(AID: Integer): Integer;
|
||||
begin
|
||||
Result := 12 * AID;
|
||||
end;
|
||||
|
||||
constructor TIndexedMulProvider.Create(AData, AIndex: TStream; AReadOnly: Boolean = False);
|
||||
begin
|
||||
inherited Create(AData, AReadOnly);
|
||||
FIndex := TBufferedReader.Create(AIndex);
|
||||
end;
|
||||
|
||||
constructor TIndexedMulProvider.Create(AData, AIndex: string; AReadOnly: Boolean = False);
|
||||
var
|
||||
mode: Word;
|
||||
begin
|
||||
inherited Create(AData, AReadOnly);
|
||||
if AReadOnly then
|
||||
mode := fmOpenRead or fmShareDenyWrite
|
||||
else
|
||||
mode := fmOpenReadWrite or fmShareDenyWrite;
|
||||
FIndex := TBufferedReader.Create(TFileStream.Create(AIndex, mode), True);
|
||||
end;
|
||||
|
||||
procedure TIndexedMulProvider.Defragment(ATempStream: TStream; AOnProgress: TOnProgressEvent = nil);
|
||||
var
|
||||
genericIndex: TGenericIndex;
|
||||
begin
|
||||
if FReadOnly then Exit;
|
||||
ATempStream.Size := FData.Size;
|
||||
ATempStream.Position := 0;
|
||||
FIndex.Position := 0;
|
||||
while FIndex.Position < FIndex.Size do
|
||||
begin
|
||||
genericIndex := TGenericIndex.Create(FIndex);
|
||||
if genericIndex.Lookup <> LongInt($FFFFFFFF) then
|
||||
begin
|
||||
FData.Position := genericIndex.Lookup;
|
||||
genericIndex.Lookup := ATempStream.Position;
|
||||
ATempStream.CopyFrom(FData, genericIndex.Size);
|
||||
FIndex.Seek(-12, soFromCurrent);
|
||||
genericIndex.Write(FIndex);
|
||||
end;
|
||||
genericIndex.Free;
|
||||
if Assigned(AOnProgress) and (FIndex.Position mod 1200 = 0) then
|
||||
AOnProgress(FIndex.Size, FIndex.Position);
|
||||
end;
|
||||
FData.Size := ATempStream.Position;
|
||||
FData.Position := 0;
|
||||
ATempStream.Position := 0;
|
||||
FData.CopyFrom(ATempStream, FData.Size);
|
||||
end;
|
||||
|
||||
destructor TIndexedMulProvider.Destroy;
|
||||
begin
|
||||
if Assigned(FIndex) then FreeAndNil(FIndex);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TIndexedMulProvider.Exists(AID: Integer): Boolean;
|
||||
var
|
||||
genericIndex: TGenericIndex;
|
||||
begin
|
||||
FIndex.Position := CalculateIndexOffset(AID);
|
||||
genericIndex := TGenericIndex.Create(FIndex);
|
||||
Result := (genericIndex.Lookup > -1) and (genericIndex.Size > 0);
|
||||
genericIndex.Free;
|
||||
end;
|
||||
|
||||
function TIndexedMulProvider.GetBlock(AID: Integer): TMulBlock;
|
||||
var
|
||||
genericIndex: TGenericIndex;
|
||||
begin
|
||||
GetBlockEx(AID, Result, genericIndex);
|
||||
genericIndex.Free;
|
||||
end;
|
||||
|
||||
procedure TIndexedMulProvider.GetBlockEx(AID: Integer;
|
||||
var ABlock: TMulBlock; var AIndex: TGenericIndex);
|
||||
begin
|
||||
FIndex.Position := CalculateIndexOffset(AID);
|
||||
AIndex := TGenericIndex.Create(FIndex);
|
||||
ABlock := GetData(AID, AIndex);
|
||||
ABlock.OnChanged := @OnChanged;
|
||||
ABlock.OnFinished := @OnFinished;
|
||||
end;
|
||||
|
||||
function TIndexedMulProvider.GetVarious(AID: Integer; ABlock: TMulBlock;
|
||||
ADefault: Integer): Integer;
|
||||
begin
|
||||
Result := ADefault;
|
||||
end;
|
||||
|
||||
procedure TIndexedMulProvider.SetBlock(AID: Integer; ABlock: TMulBlock);
|
||||
var
|
||||
genericIndex: TGenericIndex;
|
||||
begin
|
||||
if FReadOnly then Exit;
|
||||
FIndex.Position := CalculateIndexOffset(AID);
|
||||
genericIndex := TGenericIndex.Create(FIndex);
|
||||
SetData(AID, genericIndex, ABlock);
|
||||
FIndex.Position := CalculateIndexOffset(AID);
|
||||
genericIndex.Various := GetVarious(AID, ABlock, genericIndex.Various);
|
||||
genericIndex.Write(FIndex);
|
||||
genericIndex.Free;
|
||||
end;
|
||||
|
||||
procedure TIndexedMulProvider.SetData(AID: Integer; AIndex: TGenericIndex;
|
||||
ABlock: TMulBlock);
|
||||
var
|
||||
size: Integer;
|
||||
begin
|
||||
if FReadOnly then Exit;
|
||||
size := ABlock.GetSize;
|
||||
if size = 0 then
|
||||
begin
|
||||
AIndex.Lookup := LongInt($FFFFFFFF);
|
||||
AIndex.Various := LongInt($FFFFFFFF);
|
||||
end else if (size > AIndex.Size) or (AIndex.Lookup = LongInt($FFFFFFFF)) then
|
||||
begin
|
||||
FData.Position := FData.Size;
|
||||
AIndex.Lookup := FData.Position;
|
||||
ABlock.Write(FData);
|
||||
end else
|
||||
begin
|
||||
FData.Position := AIndex.Lookup;
|
||||
ABlock.Write(FData);
|
||||
end;
|
||||
AIndex.Size := size;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
(*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at
|
||||
* http://www.opensource.org/licenses/cddl1.php.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at
|
||||
* http://www.opensource.org/licenses/cddl1.php. If applicable,
|
||||
* add the following below this CDDL HEADER, with the fields enclosed
|
||||
* by brackets "[]" replaced with your own identifying * information:
|
||||
* Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*
|
||||
*
|
||||
* Portions Copyright 2009 Andreas Schneider
|
||||
*)
|
||||
unit UMulProvider;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
SysUtils, Classes, UBufferedStreams, UMulBlock, UGenericIndex;
|
||||
|
||||
type
|
||||
TOnProgressEvent = procedure(Total, Current: Integer) of object;
|
||||
|
||||
{ TMulEventHandler }
|
||||
|
||||
TMulEventHandler = class
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
protected
|
||||
FEvents: TList;
|
||||
public
|
||||
procedure RegisterEvent(AEvent: TMulBlockChanged);
|
||||
procedure UnregisterEvent(AEvent: TMulBlockChanged);
|
||||
procedure FireEvents(ABlock: TMulBlock);
|
||||
end;
|
||||
|
||||
{ TMulProvider }
|
||||
|
||||
TMulProvider = class
|
||||
constructor Create; overload; virtual;
|
||||
constructor Create(AData: TStream; AReadOnly: Boolean = False); overload; virtual;
|
||||
constructor Create(AData: string; AReadOnly: Boolean = False); overload; virtual;
|
||||
destructor Destroy; override;
|
||||
protected
|
||||
FData: TStream;
|
||||
FOwnsData: Boolean;
|
||||
FReadOnly: Boolean;
|
||||
FChangeEvents: TMulEventHandler;
|
||||
FFinishedEvents: TMulEventHandler;
|
||||
function CalculateOffset(AID: Integer): Integer; virtual; abstract;
|
||||
function GetData(AID, AOffset: Integer): TMulBlock; virtual; abstract;
|
||||
procedure SetData(AID, AOffset: Integer; ABlock: TMulBlock); virtual;
|
||||
procedure OnChanged(ABlock: TMulBlock);
|
||||
procedure OnFinished(ABlock: TMulBlock);
|
||||
public
|
||||
function GetBlock(AID: Integer): TMulBlock; virtual;
|
||||
procedure SetBlock(AID: Integer; ABlock: TMulBlock); virtual;
|
||||
procedure RegisterOnChangeEvent(AEvent: TMulBlockChanged);
|
||||
procedure UnregisterOnChangeEvent(AEvent: TMulBlockChanged);
|
||||
procedure RegisterOnFinishedEvent(AEvent: TMulBlockChanged);
|
||||
procedure UnregisterOnFinishedEvent(AEvent: TMulBlockChanged);
|
||||
property Block[ID: Integer]: TMulBlock read GetBlock write SetBlock;
|
||||
property Data: TStream read FData;
|
||||
end;
|
||||
|
||||
{ TIndexedMulProvider }
|
||||
|
||||
TIndexedMulProvider = class(TMulProvider)
|
||||
constructor Create(AData, AIndex: TStream; AReadOnly: Boolean = False); overload; virtual;
|
||||
constructor Create(AData, AIndex: string; AReadOnly: Boolean = False); overload; virtual;
|
||||
destructor Destroy; override;
|
||||
protected
|
||||
FIndex: TBufferedReader;
|
||||
FEntryCount: Cardinal;
|
||||
function CalculateIndexOffset(AID: Integer): Integer; virtual;
|
||||
function GetData(AID: Integer; AIndex: TGenericIndex): TMulBlock; reintroduce; virtual; abstract;
|
||||
procedure SetData(AID: Integer; AIndex: TGenericIndex; ABlock: TMulBlock); reintroduce; virtual;
|
||||
function GetVarious(AID: Integer; ABlock: TMulBlock; ADefault: Integer): Integer; virtual;
|
||||
public
|
||||
function GetBlock(AID: Integer): TMulBlock; override;
|
||||
procedure GetBlockEx(AID: Integer; var ABlock: TMulBlock; var AIndex: TGenericIndex); virtual;
|
||||
procedure SetBlock(AID: Integer; ABlock: TMulBlock); override;
|
||||
function Exists(AID: Integer): Boolean; virtual;
|
||||
procedure Defragment(ATempStream: TStream; AOnProgress: TOnProgressEvent = nil); virtual;
|
||||
property Index: TBufferedReader read FIndex;
|
||||
property EntryCount: Cardinal read FEntryCount;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
type
|
||||
PMethod = ^TMethod;
|
||||
|
||||
{ TMulEventHandler }
|
||||
|
||||
constructor TMulEventHandler.Create;
|
||||
begin
|
||||
inherited;
|
||||
FEvents := TList.Create;
|
||||
end;
|
||||
|
||||
destructor TMulEventHandler.Destroy;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if Assigned(FEvents) then
|
||||
begin
|
||||
for i := 0 to FEvents.Count - 1 do
|
||||
Dispose(PMethod(FEvents.Items[i]));
|
||||
FreeAndNil(FEvents);
|
||||
end;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TMulEventHandler.FireEvents(ABlock: TMulBlock);
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
for i := 0 to FEvents.Count - 1 do
|
||||
TMulBlockChanged(FEvents.Items[i]^)(ABlock);
|
||||
end;
|
||||
|
||||
procedure TMulEventHandler.RegisterEvent(AEvent: TMulBlockChanged);
|
||||
var
|
||||
eventInfo: PMethod;
|
||||
begin
|
||||
UnregisterEvent(AEvent);
|
||||
New(eventInfo);
|
||||
eventInfo^.Code := TMethod(AEvent).Code;
|
||||
eventInfo^.Data := TMethod(AEvent).Data;
|
||||
FEvents.Add(eventInfo);
|
||||
end;
|
||||
|
||||
procedure TMulEventHandler.UnregisterEvent(AEvent: TMulBlockChanged);
|
||||
var
|
||||
i: Integer;
|
||||
|
||||
function RemoveEntry: Boolean;
|
||||
begin
|
||||
Dispose(PMethod(FEvents.Items[i]));
|
||||
FEvents.Delete(i);
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
begin
|
||||
i := 0;
|
||||
while (i < FEvents.Count) and ((TMethod(AEvent).Code <> TMethod(FEvents.Items[i]^).Code) or (TMethod(AEvent).Data <> TMethod(FEvents.Items[i]^).Data) or not RemoveEntry) do
|
||||
Inc(i);
|
||||
end;
|
||||
|
||||
{ TMulProvider }
|
||||
|
||||
constructor TMulProvider.Create(AData: TStream; AReadOnly: Boolean = False);
|
||||
begin
|
||||
Create;
|
||||
FData := AData;
|
||||
FOwnsData := False;
|
||||
FReadOnly := AReadOnly;
|
||||
end;
|
||||
|
||||
constructor TMulProvider.Create(AData: string; AReadOnly: Boolean = False);
|
||||
var
|
||||
mode: Word;
|
||||
begin
|
||||
Create;
|
||||
if AReadOnly then
|
||||
mode := fmOpenRead or fmShareDenyWrite
|
||||
else
|
||||
mode := fmOpenReadWrite or fmShareDenyWrite;
|
||||
FData := TFileStream.Create(AData, mode);
|
||||
FOwnsData := True;
|
||||
FReadOnly := AReadOnly;
|
||||
end;
|
||||
|
||||
constructor TMulProvider.Create;
|
||||
begin
|
||||
inherited;
|
||||
FChangeEvents := TMulEventHandler.Create;
|
||||
FFinishedEvents := TMulEventHandler.Create;
|
||||
end;
|
||||
|
||||
destructor TMulProvider.Destroy;
|
||||
begin
|
||||
if FOwnsData and Assigned(FData) then FreeAndNil(FData);
|
||||
if Assigned(FChangeEvents) then FreeAndNil(FChangeEvents);
|
||||
if Assigned(FFinishedEvents) then FreeAndNil(FFinishedEvents);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TMulProvider.GetBlock(AID: Integer): TMulBlock;
|
||||
begin
|
||||
Result := GetData(AID, CalculateOffset(AID));
|
||||
Result.OnChanged := @OnChanged;
|
||||
Result.OnFinished := @OnFinished;
|
||||
end;
|
||||
|
||||
procedure TMulProvider.OnChanged(ABlock: TMulBlock);
|
||||
begin
|
||||
SetBlock(ABlock.ID, ABlock);
|
||||
FChangeEvents.FireEvents(ABlock);
|
||||
end;
|
||||
|
||||
procedure TMulProvider.OnFinished(ABlock: TMulBlock);
|
||||
begin
|
||||
FFinishedEvents.FireEvents(ABlock);
|
||||
ABlock.Free;
|
||||
end;
|
||||
|
||||
procedure TMulProvider.RegisterOnChangeEvent(AEvent: TMulBlockChanged);
|
||||
begin
|
||||
FChangeEvents.RegisterEvent(AEvent);
|
||||
end;
|
||||
|
||||
procedure TMulProvider.RegisterOnFinishedEvent(AEvent: TMulBlockChanged);
|
||||
begin
|
||||
FFinishedEvents.RegisterEvent(AEvent);
|
||||
end;
|
||||
|
||||
procedure TMulProvider.SetBlock(AID: Integer; ABlock: TMulBlock);
|
||||
begin
|
||||
if FReadOnly then Exit;
|
||||
SetData(AID, CalculateOffset(AID), ABlock);
|
||||
end;
|
||||
|
||||
procedure TMulProvider.SetData(AID, AOffset: Integer; ABlock: TMulBlock);
|
||||
begin
|
||||
if FReadOnly then Exit;
|
||||
FData.Position := AOffset;
|
||||
ABlock.Write(FData);
|
||||
end;
|
||||
|
||||
procedure TMulProvider.UnregisterOnChangeEvent(AEvent: TMulBlockChanged);
|
||||
begin
|
||||
FChangeEvents.UnregisterEvent(AEvent);
|
||||
end;
|
||||
|
||||
procedure TMulProvider.UnregisterOnFinishedEvent(AEvent: TMulBlockChanged);
|
||||
begin
|
||||
FFinishedEvents.UnregisterEvent(AEvent);
|
||||
end;
|
||||
|
||||
{ TIndexedMulProvider }
|
||||
|
||||
function TIndexedMulProvider.CalculateIndexOffset(AID: Integer): Integer;
|
||||
begin
|
||||
Result := 12 * AID;
|
||||
end;
|
||||
|
||||
constructor TIndexedMulProvider.Create(AData, AIndex: TStream; AReadOnly: Boolean = False);
|
||||
begin
|
||||
inherited Create(AData, AReadOnly);
|
||||
FIndex := TBufferedReader.Create(AIndex);
|
||||
FEntryCount := AIndex.Size div 12;
|
||||
end;
|
||||
|
||||
constructor TIndexedMulProvider.Create(AData, AIndex: string; AReadOnly: Boolean = False);
|
||||
var
|
||||
mode: Word;
|
||||
begin
|
||||
inherited Create(AData, AReadOnly);
|
||||
if AReadOnly then
|
||||
mode := fmOpenRead or fmShareDenyWrite
|
||||
else
|
||||
mode := fmOpenReadWrite or fmShareDenyWrite;
|
||||
FIndex := TBufferedReader.Create(TFileStream.Create(AIndex, mode), True);
|
||||
FEntryCount := FIndex.Size div 12;
|
||||
end;
|
||||
|
||||
procedure TIndexedMulProvider.Defragment(ATempStream: TStream; AOnProgress: TOnProgressEvent = nil);
|
||||
var
|
||||
genericIndex: TGenericIndex;
|
||||
begin
|
||||
if FReadOnly then Exit;
|
||||
ATempStream.Size := FData.Size;
|
||||
ATempStream.Position := 0;
|
||||
FIndex.Position := 0;
|
||||
while FIndex.Position < FIndex.Size do
|
||||
begin
|
||||
genericIndex := TGenericIndex.Create(FIndex);
|
||||
if genericIndex.Lookup > -1 then
|
||||
begin
|
||||
FData.Position := genericIndex.Lookup;
|
||||
genericIndex.Lookup := ATempStream.Position;
|
||||
ATempStream.CopyFrom(FData, genericIndex.Size);
|
||||
FIndex.Seek(-12, soFromCurrent);
|
||||
genericIndex.Write(FIndex);
|
||||
end;
|
||||
genericIndex.Free;
|
||||
if Assigned(AOnProgress) and (FIndex.Position mod 1200 = 0) then
|
||||
AOnProgress(FIndex.Size, FIndex.Position);
|
||||
end;
|
||||
FData.Size := ATempStream.Position;
|
||||
FData.Position := 0;
|
||||
ATempStream.Position := 0;
|
||||
FData.CopyFrom(ATempStream, FData.Size);
|
||||
end;
|
||||
|
||||
destructor TIndexedMulProvider.Destroy;
|
||||
begin
|
||||
FreeAndNil(FIndex);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TIndexedMulProvider.Exists(AID: Integer): Boolean;
|
||||
var
|
||||
genericIndex: TGenericIndex;
|
||||
begin
|
||||
FIndex.Position := CalculateIndexOffset(AID);
|
||||
genericIndex := TGenericIndex.Create(FIndex);
|
||||
Result := (genericIndex.Lookup > -1) and (genericIndex.Size > 0);
|
||||
genericIndex.Free;
|
||||
end;
|
||||
|
||||
function TIndexedMulProvider.GetBlock(AID: Integer): TMulBlock;
|
||||
var
|
||||
genericIndex: TGenericIndex;
|
||||
begin
|
||||
GetBlockEx(AID, Result, genericIndex);
|
||||
genericIndex.Free;
|
||||
end;
|
||||
|
||||
procedure TIndexedMulProvider.GetBlockEx(AID: Integer;
|
||||
var ABlock: TMulBlock; var AIndex: TGenericIndex);
|
||||
begin
|
||||
FIndex.Position := CalculateIndexOffset(AID);
|
||||
AIndex := TGenericIndex.Create(FIndex);
|
||||
ABlock := GetData(AID, AIndex);
|
||||
ABlock.OnChanged := @OnChanged;
|
||||
ABlock.OnFinished := @OnFinished;
|
||||
end;
|
||||
|
||||
function TIndexedMulProvider.GetVarious(AID: Integer; ABlock: TMulBlock;
|
||||
ADefault: Integer): Integer;
|
||||
begin
|
||||
Result := ADefault;
|
||||
end;
|
||||
|
||||
procedure TIndexedMulProvider.SetBlock(AID: Integer; ABlock: TMulBlock);
|
||||
var
|
||||
genericIndex: TGenericIndex;
|
||||
begin
|
||||
if FReadOnly then Exit;
|
||||
FIndex.Position := CalculateIndexOffset(AID);
|
||||
genericIndex := TGenericIndex.Create(FIndex);
|
||||
SetData(AID, genericIndex, ABlock);
|
||||
FIndex.Position := CalculateIndexOffset(AID);
|
||||
genericIndex.Various := GetVarious(AID, ABlock, genericIndex.Various);
|
||||
genericIndex.Write(FIndex);
|
||||
genericIndex.Free;
|
||||
end;
|
||||
|
||||
procedure TIndexedMulProvider.SetData(AID: Integer; AIndex: TGenericIndex;
|
||||
ABlock: TMulBlock);
|
||||
var
|
||||
size: Integer;
|
||||
begin
|
||||
if FReadOnly then Exit;
|
||||
size := ABlock.GetSize;
|
||||
if size = 0 then
|
||||
begin
|
||||
AIndex.Lookup := -1;
|
||||
AIndex.Various := -1;
|
||||
end else if (size > AIndex.Size) or (AIndex.Lookup < 0) then
|
||||
begin
|
||||
FData.Position := FData.Size;
|
||||
AIndex.Lookup := FData.Position;
|
||||
ABlock.Write(FData);
|
||||
end else
|
||||
begin
|
||||
FData.Position := AIndex.Lookup;
|
||||
ABlock.Write(FData);
|
||||
end;
|
||||
AIndex.Size := size;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@
|
|||
</Linking>
|
||||
<Other>
|
||||
<CustomOptions Value="-FE../bin/
|
||||
"/>
|
||||
-dNoLogging"/>
|
||||
<CompilerPath Value="$(CompPath)"/>
|
||||
</Other>
|
||||
</CompilerOptions>
|
||||
|
|
|
@ -33,6 +33,9 @@ uses
|
|||
SysUtils, Classes, UEnhancedMemoryStream;
|
||||
|
||||
type
|
||||
|
||||
{ TBufferedStream }
|
||||
|
||||
TBufferedStream = class(TEnhancedMemoryStream)
|
||||
constructor Create(ABaseStream: TStream; AOwnsBaseStream: Boolean = false); virtual;
|
||||
destructor Destroy; override;
|
||||
|
@ -42,6 +45,7 @@ type
|
|||
public
|
||||
procedure Refresh; virtual;
|
||||
procedure Flush; virtual;
|
||||
function GetSize: Int64; override;
|
||||
end;
|
||||
TBufferedReader = class(TBufferedStream)
|
||||
constructor Create(ABaseStream: TStream; AOwnsBaseStream: Boolean = false); override;
|
||||
|
@ -82,6 +86,11 @@ begin
|
|||
FBaseStream.CopyFrom(Self, 0);
|
||||
end;
|
||||
|
||||
function TBufferedStream.GetSize: Int64;
|
||||
begin
|
||||
Result := FBaseStream.Size;
|
||||
end;
|
||||
|
||||
procedure TBufferedStream.Refresh;
|
||||
begin
|
||||
Size := FBaseStream.Size;
|
||||
|
|
Loading…
Reference in New Issue