BSOne.SFC/Tocsg.Module/Bs1Flt/MTPMon/MTPControl/ObexParserUnit.pas

213 lines
4.9 KiB
Plaintext

unit ObexParserUnit;
interface
uses
System.SysUtils, System.Classes, System.Generics.Collections,
Winapi.WinSock2; // ntohs, ntohl 함수 사용
// OBEX 헤더 ID 상수
const
OBEX_HID_NAME = $01;
OBEX_HID_BODY = $48;
OBEX_HID_BODY_FINAL = $49;
OBEX_HID_LENGTH = $C3;
type
{ TObexHeader: 파싱된 개별 헤더 (ID, Value)를 저장 }
TObexHeader = class
public
HeaderID: Byte;
Value: TBytes;
end;
{ TObexPacket: OBEX 패킷 전체를 파싱하고 데이터를 보관 }
TObexPacket = class
private
FHeaders: TList<TObexHeader>;
FOpcode: Byte;
FPacketLength: Word;
function GetHeader(HeaderID: Byte): TObexHeader;
function GetBody: TBytes;
function GetName: string;
function GetTotalLength: Cardinal;
public
constructor Create;
destructor Destroy; override;
// 이 함수가 사용자가 요청한 "캐스팅 함수"입니다.
procedure Parse(Buffer: PByte; Size: Integer);
// 주요 속성
property Opcode: Byte read FOpcode;
property PacketLength: Word read FPacketLength;
property Name: string read GetName; // 0x01 헤더
property TotalLength: Cardinal read GetTotalLength; // 0xC3 헤더
property Body: TBytes read GetBody; // 0x48 또는 0x49 헤더
end;
implementation
{ TObexPacket }
constructor TObexPacket.Create;
begin
inherited;
FHeaders := TList<TObexHeader>.Create;
end;
destructor TObexPacket.Destroy;
var
Header: TObexHeader;
begin
for Header in FHeaders do
Header.Free;
FHeaders.Free;
inherited;
end;
function TObexPacket.GetHeader(HeaderID: Byte): TObexHeader;
var
Header: TObexHeader;
begin
Result := nil;
for Header in FHeaders do
begin
if Header.HeaderID = HeaderID then
Exit(Header);
end;
end;
function TObexPacket.GetBody: TBytes;
var
Header: TObexHeader;
begin
// Body-Final (0x49)을 먼저 찾고, 없으면 Body (0x48)를 찾음
Header := GetHeader(OBEX_HID_BODY_FINAL);
if Header = nil then
Header := GetHeader(OBEX_HID_BODY);
if Header <> nil then
Result := Header.Value
else
Result := nil;
end;
function TObexPacket.GetName: string;
var
Header: TObexHeader;
begin
Result := '';
Header := GetHeader(OBEX_HID_NAME);
if (Header <> nil) and (Length(Header.Value) > 0) then
begin
// OBEX 이름은 UTF-16 Big Endian, 마지막 2바이트는 널 종결
Result := TEncoding.BigEndianUnicode.GetString(Header.Value, 0, Length(Header.Value) - 2);
end;
end;
function TObexPacket.GetTotalLength: Cardinal;
var
Header: TObexHeader;
begin
Result := 0;
Header := GetHeader(OBEX_HID_LENGTH);
if (Header <> nil) and (Length(Header.Value) = 4) then
begin
// 32비트 Big Endian (Network) 값을 Host (Little Endian)로 변환
Result := ntohl(PCardinal(Header.Value)^);
end;
end;
procedure TObexPacket.Parse(Buffer: PByte; Size: Integer);
var
CurrentOffset: Integer;
Header: TObexHeader;
HeaderID: Byte;
HeaderLen: Word;
ValueLen: Integer;
begin
// 기존 헤더 클리어
for Header in FHeaders do
Header.Free;
FHeaders.Clear;
if (Buffer = nil) or (Size < 3) then
Exit; // 최소 3바이트 (Opcode + PacketLength)
FOpcode := Buffer[0];
// 16비트 Big Endian (Network) 값을 Host (Little Endian)로 변환
FPacketLength := ntohs(PWord(@Buffer[1])^);
// 패킷 크기 검증
if FPacketLength > Size then
Exit; // 실제 버퍼 크기보다 패킷 길이가 더 큼 (잘못된 데이터)
CurrentOffset := 3; // 헤더 시작 위치
// 헤더 파싱 루프
while CurrentOffset < FPacketLength do
begin
Header := TObexHeader.Create;
HeaderID := Buffer[CurrentOffset];
Header.HeaderID := HeaderID;
Inc(CurrentOffset);
// 헤더 ID의 상위 2비트로 타입을 구분 (01=유니코드, 10=바이트시퀀스, 11=4바이트정수)
case (HeaderID and $C0) of
$00, // Name (0x01)
$40: // Body (0x48, 0x49)
begin
// 헤더 길이 (2바이트 Big Endian)
if (CurrentOffset + 2) > FPacketLength then
begin
Header.Free;
Break;
end;
HeaderLen := ntohs(PWord(@Buffer[CurrentOffset])^);
Inc(CurrentOffset, 2);
// [수정] 헤더 길이가 최소 3바이트인지 확인
if HeaderLen < 3 then
begin
Header.Free;
Break;
end;
// 실제 값 길이 (전체 헤더 길이 - ID(1) - Length(2))
ValueLen := HeaderLen - 3;
if (CurrentOffset + ValueLen) > FPacketLength then
begin
Header.Free;
Break; // 패킷이 거짓말을 하고 있음
end;
SetLength(Header.Value, ValueLen);
Move(Buffer[CurrentOffset], Header.Value[0], ValueLen);
Inc(CurrentOffset, ValueLen);
end;
$C0: // Length (0xC3)
begin
// 4바이트 정수 (값 자체가 4바이트)
ValueLen := 4;
SetLength(Header.Value, ValueLen);
Move(Buffer[CurrentOffset], Header.Value[0], ValueLen);
Inc(CurrentOffset, ValueLen);
end
else
// 알 수 없는 헤더 타입
Break;
end;
FHeaders.Add(Header);
end;
end;
end.