BSOne.SFC/Tocsg.Lib/VCL/Tocsg.Serializer.pas

499 lines
11 KiB
Plaintext

{*******************************************************}
{ }
{ Tocsg.Serializer }
{ }
{ Copyright (C) 2022 sunk }
{ }
{*******************************************************}
unit Tocsg.Serializer;
interface
uses
Tocsg.Obj, System.Classes, System.SysUtils, Winapi.Windows,
Tocsg.Exception;
const
SER_SIG: AnsiString = 'KzSer.';
SER_VER = 1;
type
TTgSerHeader = packed record
sSig: array [0..11] of AnsiChar;
nVer: Integer;
end;
ETgSerializer = class(ETgException);
TTgSerializerBase = class(TTgObject)
private
bStreamFree_: Boolean;
protected
Header_: TTgSerHeader;
Stream_: TStream;
function GetPosition: LONGLONG;
function GetSize: LONGLONG;
public
Constructor Create(aStream: TStream; bAutoStreamFree: Boolean = false); overload;
Constructor Create; overload;
Destructor Destroy; override;
procedure SeekFront;
procedure SeekEnd;
property Stream: TStream read Stream_;
property Header: TTgSerHeader read Header_;
end;
TTgSerializerSave = class(TTgSerializerBase)
public
procedure SaveHeader(nVer: Integer = SER_VER);
procedure S_WideString(sVal: WideString);
procedure S_AnsiString(sVal: AnsiString);
procedure S_UTF8String(sVal: UTF8String);
procedure S_Integer(nVal: Integer);
procedure S_DWORD(dwVal: DWORD);
procedure S_WORD(wVal: WORD);
procedure S_LONGLONG(llVal: LONGLONG);
procedure S_ULONGLONG(ullVal: ULONGLONG);
procedure S_DateTime(dtVal: TDateTime);
procedure S_Strings(aList: TStrings);
procedure S_Boolean(bVal: Boolean);
procedure S_Stream(aStream: TStream);
procedure SaveToFile(sPath: String);
end;
TTgSerializerLoad = class(TTgSerializerSave)
public
function LoadHeader: Boolean;
function L_WideString: WideString;
function L_AnsiString: AnsiString;
function L_UTF8String: UTF8String;
function L_Integer: Integer;
function L_DWORD: DWORD;
function L_WORD: WORD;
function L_LONGLONG: LONGLONG;
function L_ULONGLONG: ULONGLONG;
function L_DateTime: TDateTime;
function L_Strings(aList: TStrings): Integer;
function L_Boolean: Boolean;
function L_Stream(aStream: TStream): LONGLONG;
function IsEndOfFile: Boolean;
function GetRemainSize: LONGLONG;
function GetReadPercent: WORD;
procedure LoadFromFile(sPath: String);
end;
implementation
{ TTgSerializerBase }
Constructor TTgSerializerBase.Create(aStream: TStream; bAutoStreamFree: Boolean = false);
begin
Inherited Create;
ZeroMemory(@Header_, SizeOf(Header_));
bStreamFree_ := bAutoStreamFree;
Stream_ := aStream;
if Stream_ = nil then
raise ETgSerializer.Create('스트림이 지정되지 않았습니다.');
end;
Constructor TTgSerializerBase.Create;
begin
Inherited Create;
ZeroMemory(@Header_, SizeOf(Header_));
Stream_ := TMemoryStream.Create;
bStreamFree_ := true;
end;
Destructor TTgSerializerBase.Destroy;
begin
if (Stream_ <> nil) and bStreamFree_ then
FreeAndNil(Stream_);
Inherited;
end;
function TTgSerializerBase.GetPosition: LONGLONG;
begin
Result := Stream_.Position;
end;
function TTgSerializerBase.GetSize: LONGLONG;
begin
Result := Stream_.Size;
end;
procedure TTgSerializerBase.SeekFront;
begin
Stream_.Seek(0, soBeginning);
end;
procedure TTgSerializerBase.SeekEnd;
begin
Stream_.Seek(0, soEnd)
end;
{ TTgSerializerSave }
procedure TTgSerializerSave.SaveHeader(nVer: Integer = SER_VER);
begin
if Stream_.Position > 0 then
raise Exception.Create('offset 값이 0보다 큽니다.');
if Length(SER_SIG) > SizeOf(Header_.sSig) then
raise Exception.Create('시그너처 길이가 너무 큽니다.');
ZeroMemory(@Header_.sSig, SizeOf(Header_.sSig));
CopyMemory(@Header_.sSig[0], @SER_SIG[1], Length(SER_SIG));
Header_.nVer := nVer;
Stream_.Write(Header_, SizeOf(Header_));
end;
procedure TTgSerializerSave.S_WideString(sVal: WideString);
var
nLen: Integer;
begin
nLen := Length(sVal) * 2;
S_Integer(nLen);
Stream_.Write(PWideChar(sVal)^, nLen);
end;
procedure TTgSerializerSave.S_AnsiString(sVal: AnsiString);
var
nLen: Integer;
begin
nLen := Length(sVal);
S_Integer(nLen);
Stream_.Write(PAnsiChar(sVal)^, nLen);
end;
procedure TTgSerializerSave.S_UTF8String(sVal: UTF8String);
var
nLen: Integer;
begin
nLen := Length(sVal);
S_Integer(nLen);
Stream_.Write(PUTF8String(sVal)^, nLen);
end;
procedure TTgSerializerSave.S_Integer(nVal: Integer);
begin
try
Stream_.Write(nVal, SizeOf(nVal));
except
_Trace('Fail .. S_Integer()');
end;
end;
procedure TTgSerializerSave.S_DWORD(dwVal: DWORD);
begin
try
Stream_.Write(dwVal, SizeOf(dwVal));
except
_Trace('Fail .. S_DWORD()');
end;
end;
procedure TTgSerializerSave.S_WORD(wVal: WORD);
begin
try
Stream_.Write(wVal, SizeOf(wVal));
except
_Trace('Fail .. S_WORD()');
end;
end;
procedure TTgSerializerSave.S_LONGLONG(llVal: LONGLONG);
begin
try
Stream_.Write(llVal, SizeOf(llVal));
except
_Trace('Fail .. S_LONGLONG()');
end;
end;
procedure TTgSerializerSave.S_ULONGLONG(ullVal: ULONGLONG);
begin
try
Stream_.Write(ullVal, SizeOf(ullVal));
except
_Trace('Fail .. S_ULONGLONG()');
end;
end;
procedure TTgSerializerSave.S_DateTime(dtVal: TDateTime);
begin
try
Stream_.Write(dtVal, SizeOf(dtVal));
except
_Trace('Fail .. S_DateTime()');
end;
end;
procedure TTgSerializerSave.S_Strings(aList: TStrings);
var
i: Integer;
begin
try
S_Integer(aList.Count);
for i := 0 to aList.Count - 1 do
S_UTF8String(aList[i]);
except
_Trace('Fail .. S_Strings()');
end;
end;
procedure TTgSerializerSave.S_Boolean(bVal: Boolean);
begin
try
Stream_.Write(bVal, SizeOf(bVal));
except
_Trace('Fail .. S_Boolean()');
end;
end;
procedure TTgSerializerSave.S_Stream(aStream: TStream);
begin
try
aStream.Position := 0;
S_LONGLONG(aStream.Size);
Stream_.CopyFrom(aStream, aStream.Size);
except
_Trace('Fail .. S_Stream()');
end;
end;
procedure TTgSerializerSave.SaveToFile(sPath: String);
begin
if not (Stream_ is TMemoryStream) then
raise ETgSerializer.Create('Stream이 TMemoryStream가 아닙니다.');
try
TMemoryStream(Stream_).SaveToFile(sPath);
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. SaveToFile()');
end;
end;
{ TTgSerializerLoad }
function TTgSerializerLoad.LoadHeader: Boolean;
begin
Result := false;
ZeroMemory(@Header_, SizeOf(Header_));
if Stream_.Position > 0 then
raise Exception.Create('offset 값이 0보다 큽니다.');
try
if Stream_.Read(Header_, SizeOf(Header_)) <> SizeOf(Header_) then
exit;
// 크기 체크 추가 19_0710 17:01:52 kku
Result := (Stream_.Size > Stream_.Position) and
CompareMem(@Header_.sSig[0], @SER_SIG[1], Length(SER_SIG));
finally
if not Result then
Stream_.Position := 0;
end;
end;
function TTgSerializerLoad.L_WideString: WideString;
var
nLen: Integer;
begin
Result := '';
try
nLen := L_Integer;
if nLen = 0 then
exit;
if nLen > GetRemainSize then
exit;
// raise ETgSerializer.Create('남은 데이터가 부족합니다.');
SetLength(Result, nLen div 2);
Stream_.Read(Result[1], nLen);
except
_Trace('Fail .. L_WideString()');
end;
end;
function TTgSerializerLoad.L_AnsiString: AnsiString;
var
nLen: Integer;
begin
Result := '';
try
nLen := L_Integer;
if nLen = 0 then
exit;
if nLen > GetRemainSize then
exit;
// raise ETgSerializer.Create('남은 데이터가 부족합니다.');
SetLength(Result, nLen);
Stream_.Read(Result[1], nLen);
except
_Trace('Fail .. L_AnsiString()');
end;
end;
function TTgSerializerLoad.L_UTF8String: UTF8String;
var
nLen: Integer;
begin
Result := '';
try
nLen := L_Integer;
if nLen = 0 then
exit;
if nLen > GetRemainSize then
exit;
// raise ETgSerializer.Create('남은 데이터가 부족합니다.');
SetLength(Result, nLen);
Stream_.Read(Result[1], nLen);
except
_Trace('Fail .. L_UTF8String()');
end;
end;
function TTgSerializerLoad.L_Integer: Integer;
begin
try
Stream_.Read(Result, SizeOf(Result));
except
_Trace('Fail .. L_Integer()');
end;
end;
function TTgSerializerLoad.L_DWORD: DWORD;
begin
try
Stream_.Read(Result, SizeOf(Result));
except
_Trace('Fail .. L_DWORD()');
end;
end;
function TTgSerializerLoad.L_WORD: WORD;
begin
try
Stream_.Read(Result, SizeOf(Result));
except
_Trace('Fail .. L_WORD()');
end;
end;
function TTgSerializerLoad.L_LONGLONG: LONGLONG;
begin
try
Stream_.Read(Result, SizeOf(Result));
except
_Trace('Fail .. L_LONGLONG()');
end;
end;
function TTgSerializerLoad.L_ULONGLONG: ULONGLONG;
begin
try
Stream_.Read(Result, SizeOf(Result));
except
_Trace('Fail .. L_ULONGLONG()');
end;
end;
function TTgSerializerLoad.L_DateTime: TDateTime;
begin
try
Stream_.Read(Result, SizeOf(Result));
except
_Trace('Fail .. L_DateTime()');
end;
end;
function TTgSerializerLoad.L_Strings(aList: TStrings): Integer;
var
i, nCnt: Integer;
begin
try
nCnt := L_Integer;
for i := 0 to nCnt - 1 do
aList.Add(L_UTF8String);
except
_Trace('Fail .. L_Strings()');
end;
end;
function TTgSerializerLoad.L_Boolean: Boolean;
begin
try
Stream_.Read(Result, SizeOf(Result));
except
_Trace('Fail .. L_Boolean()');
end;
end;
function TTgSerializerLoad.L_Stream(aStream: TStream): LONGLONG;
var
llSize: LONGLONG;
begin
try
llSize := L_LONGLONG;
if llSize > 0 then
begin
Result := aStream.CopyFrom(Stream_, llSize);
if Result <> llSize then
raise ETgSerializer.Create('남은 데이터가 부족합니다.');
end else
Result := 0;
except
_Trace('Fail .. L_Stream()');
end;
end;
function TTgSerializerLoad.IsEndOfFile: Boolean;
begin
Result := Stream_.Position >= Stream_.Size;
end;
function TTgSerializerLoad.GetRemainSize: LONGLONG;
begin
Result := Stream_.Size - Stream_.Position;
end;
function TTgSerializerLoad.GetReadPercent: WORD;
begin
if Stream_.Size > 0 then
Result := (Stream_.Position * 100) div Stream_.Size
else
Result := 0;
end;
procedure TTgSerializerLoad.LoadFromFile(sPath: String);
begin
if not (Stream_ is TMemoryStream) then
raise ETgSerializer.Create('Stream이 TMemoryStream가 아닙니다.');
try
TMemoryStream(Stream_).LoadFromFile(sPath);
Stream_.Position := 0;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. LoadFromFile()');
end;
end;
end.