Files
kam-mobile/code/GameDiagnosticsFileIO.pas
admin_d c2127c3f31
Sync changed files to Google Drive / sync (push) Successful in 3m7s
Complete BigTask
2026-09-15 10:16:31 +05:00

266 lines
7.7 KiB
ObjectPascal

unit GameDiagnosticsFileIO;
{$mode objfpc}{$H+}
interface
procedure RunFileIODiagnostic;
implementation
uses
Classes, SysUtils, GameSelectedMap, GameDiagnosticsReport, KaMResourceInput,
KM_CommonClasses;
procedure InitializeResult(out AResult: TFileIODiagnosticResult);
var
Stage: TDiagnosticStageId;
begin
FillChar(AResult, SizeOf(AResult), 0);
AResult.Status := dsNotRun;
for Stage := Low(TDiagnosticStageId) to High(TDiagnosticStageId) do
AResult.Stages[Stage] := dsNotRun;
end;
procedure FailStage(var AResult: TFileIODiagnosticResult;
const AStage: TDiagnosticStageId; const AMessage: string;
const AException: Exception = nil);
begin
AResult.Stages[AStage] := dsFail;
AResult.Status := dsFail;
AResult.FailureStage.HasValue := True;
AResult.FailureStage.Value := AStage;
AResult.Error.Present := True;
AResult.Error.MessageText.HasValue := True;
AResult.Error.MessageText.Value := AMessage;
if AException <> nil then
begin
AResult.Error.ClassName.HasValue := True;
AResult.Error.ClassName.Value := AException.ClassName;
end;
end;
procedure FailException(var AResult: TFileIODiagnosticResult;
const AStage: TDiagnosticStageId; const AException: Exception);
begin
FailStage(AResult, AStage, AException.Message, AException);
end;
function RunResourceExists(const AMapUrl: string;
var AResult: TFileIODiagnosticResult): Boolean;
begin
try
Result := KaMResourceExists(AMapUrl);
if Result then
AResult.Stages[dsiResourceExists] := dsPass
else
FailStage(AResult, dsiResourceExists,
'Selected resource is not present');
except
on E: Exception do
begin
FailException(AResult, dsiResourceExists, E);
Result := False;
end;
end;
end;
function RunResourceOpen(const AMapUrl: string;
var AResult: TFileIODiagnosticResult): Boolean;
var
Source: TStream;
begin
Result := False;
Source := nil;
try
try
Source := KaMOpenResourceStream(AMapUrl);
if Source = nil then
begin
FailStage(AResult, dsiResourceOpen, 'Resource open returned no stream');
Exit;
end;
Source.Free;
Source := nil;
AResult.Stages[dsiResourceOpen] := dsPass;
Result := True;
except
on E: Exception do
FailException(AResult, dsiResourceOpen, E);
end;
finally
if Source <> nil then
try
Source.Free;
except
on E: Exception do
if AResult.Status <> dsFail then
FailException(AResult, dsiResourceOpen, E);
end;
end;
end;
function RunCallerStreamLoad(const AMapUrl: string;
var AStream: TKMemoryStreamBinary; var AResult: TFileIODiagnosticResult): Boolean;
begin
Result := False;
try
AStream := TKMemoryStreamBinary.Create;
AStream.LoadFromFile(AMapUrl);
AResult.Stages[dsiCallerStreamLoad] := dsPass;
Result := True;
except
on E: Exception do
FailException(AResult, dsiCallerStreamLoad, E);
end;
end;
function RunStreamState(const AStream: TKMemoryStreamBinary;
var AResult: TFileIODiagnosticResult): Boolean;
begin
Result := False;
try
AResult.Stream.Size.Value := AStream.Size;
AResult.Stream.Size.HasValue := True;
AResult.Stream.PositionAfterLoad.Value := AStream.Position;
AResult.Stream.PositionAfterLoad.HasValue := True;
if AStream.Size <= 0 then
FailStage(AResult, dsiStreamState, 'Loaded stream is empty')
else if AStream.Position <> 0 then
FailStage(AResult, dsiStreamState, 'Loaded stream position is not zero')
else
begin
AResult.Stages[dsiStreamState] := dsPass;
Result := True;
end;
except
on E: Exception do
FailException(AResult, dsiStreamState, E);
end;
end;
function RunSeekReadConsistency(const AStream: TKMemoryStreamBinary;
var AResult: TFileIODiagnosticResult): Boolean;
var
Requested, FirstCount, SecondCount, ExpectedPosition, RestoredPosition: LongInt;
FirstBytes, SecondBytes: array of Byte;
I: Integer;
begin
Result := False;
try
try
AResult.Stream.ReadOffset.Value := 0;
AResult.Stream.ReadOffset.HasValue := True;
Requested := 32;
if AStream.Size < Requested then Requested := LongInt(AStream.Size);
AResult.Stream.RequestedReadCount.Value := Requested;
AResult.Stream.RequestedReadCount.HasValue := True;
SetLength(FirstBytes, Requested);
SetLength(SecondBytes, Requested);
FirstCount := AStream.Read(FirstBytes[0], Requested);
AResult.Stream.FirstReadCount.Value := FirstCount;
AResult.Stream.FirstReadCount.HasValue := True;
AResult.Stream.PositionAfterFirstRead.Value := AStream.Position;
AResult.Stream.PositionAfterFirstRead.HasValue := True;
ExpectedPosition := FirstCount;
if (FirstCount <> Requested) or (AStream.Position <> ExpectedPosition) then
begin
FailStage(AResult, dsiSeekReadConsistency, 'First read did not advance as required');
Exit;
end;
AResult.Stream.SeekBackPosition.Value := AStream.Seek(0, soBeginning);
AResult.Stream.SeekBackPosition.HasValue := True;
if (AResult.Stream.SeekBackPosition.Value <> 0) or (AStream.Position <> 0) then
begin
FailStage(AResult, dsiSeekReadConsistency, 'Seek back did not return to zero');
Exit;
end;
SecondCount := AStream.Read(SecondBytes[0], Requested);
AResult.Stream.SecondReadCount.Value := SecondCount;
AResult.Stream.SecondReadCount.HasValue := True;
AResult.Stream.PositionAfterSecondRead.Value := AStream.Position;
AResult.Stream.PositionAfterSecondRead.HasValue := True;
ExpectedPosition := SecondCount;
if (SecondCount <> Requested) or (AStream.Position <> ExpectedPosition) then
begin
FailStage(AResult, dsiSeekReadConsistency, 'Second read did not advance as required');
Exit;
end;
AResult.Stream.RepeatEqual.Value := True;
AResult.Stream.RepeatEqual.HasValue := True;
for I := 0 to Requested - 1 do
if FirstBytes[I] <> SecondBytes[I] then
begin
AResult.Stream.RepeatEqual.Value := False;
Break;
end;
if not AResult.Stream.RepeatEqual.Value then
begin
FailStage(AResult, dsiSeekReadConsistency, 'Repeated reads differ');
Exit;
end;
except
on E: Exception do
FailException(AResult, dsiSeekReadConsistency, E);
end;
finally
try
RestoredPosition := AStream.Seek(AResult.Stream.PositionAfterLoad.Value, soBeginning);
if ((RestoredPosition <> AResult.Stream.PositionAfterLoad.Value) or
(AStream.Position <> AResult.Stream.PositionAfterLoad.Value)) and
(AResult.Status <> dsFail) then
begin
FailStage(AResult, dsiSeekReadConsistency, 'Could not restore stream position');
Result := False;
end;
except
on E: Exception do
if AResult.Status <> dsFail then
begin
FailException(AResult, dsiSeekReadConsistency, E);
Result := False;
end;
end;
end;
if AResult.Status <> dsFail then
begin
AResult.Stages[dsiSeekReadConsistency] := dsPass;
AResult.Status := dsPass;
Result := True;
end;
end;
procedure RunFileIODiagnostic;
var
ResultData: TFileIODiagnosticResult;
MapUrl: string;
Stream: TKMemoryStreamBinary;
begin
InitializeResult(ResultData);
Stream := nil;
MapUrl := SelectedMapUrl;
try
if RunResourceExists(MapUrl, ResultData) and
RunResourceOpen(MapUrl, ResultData) and
RunCallerStreamLoad(MapUrl, Stream, ResultData) and
RunStreamState(Stream, ResultData) then
RunSeekReadConsistency(Stream, ResultData);
finally
try
Stream.Free;
except
on E: Exception do
if ResultData.Status <> dsFail then
FailException(ResultData, dsiSeekReadConsistency, E);
end;
end;
ReplaceFileIODiagnosticResult(ResultData);
end;
end.