edbored
edbored

Reputation: 274

Convert hexadecimal strings from TBytes data in Delphi

I'm looking for a simple way of taking lines of hexadecimal data from a TStringList (always "Windows-1252" text files) and chopping them into record blocks (every line can be different length).

In Delphi 7 I used:

procedure DecodeLineAddr(const aLine: AnsiString; var ByteCount: integer; var Address:Cardinal; var RecType: Integer);
begin
//123 4567 89 0
//:10 4640 00 0000 0600 0200 fa00 004f 7800 1e00 fb00 88
 ByteCount:= StrToInt('$' + copy(aLine, 2, 2));
 Address  := StrToInt('$' + copy(aLine, 4, 4));
 RecType  := StrToInt('$' + copy(aLine, 8, 2));
end;

That is, just copy the chars from the correct positions in the initial "block info" in the line, then prepend a '$' so StrToInt would interpret the string as hex.

I process line-by-line - so it's easy enough to do something like:

aLineAsTBytes:= TEncoding.ASCII.GetBytes(aStringLst[ndx]);

Then pass aLineAsTBytes into DecodeLineAddr as TBytes instead of AnsiString.

It isn't clear to me of how I should decode the various bytes (or how to carve them up appropriately) in order to return the correct results with code that will work on desktop and mobile.

That is, if using aLine:TBytes (instead of AnsiString), what's the equivalent of:

ByteCount:= StrToInt('$' + copy(aLine, 2, 2));

(and is there a better/faster way of handling this?)

TIA.

EdB

Upvotes: 0

Views: 2106

Answers (1)

Jason Nelson
Jason Nelson

Reputation: 454

What you're already doing will work but you'll need to make a few tweaks. Most Importantly, make your function into a "string" type instead of an "AnsiString" type, which means that you'll have to convert it.

Mobile strings are 0-based, so on mobile you'll need to subtract 1 from your indexes. Or you can use my ocopy() or zcopy() functions which both perform the same on all platforms. Use ocopy() if you're dealing with old windows code, it will treat your 0-based strings as 1-based strings essentially, making it easier to port.

const
{$IFNDEF MSWINDOWS}
  STRZ = 1;
{$ELSE}
  STRZ = 0;
function zcopy(sString: string; iStartZeroBased: nativeint; iLength: nativeint):    string;
begin
  result := '';
  setlength(result, lesserof(iLength, length(sString)-iStartZerobased));
  movemem32(@result[strz], @sString[(strz+iStartZeroBased)],    length(result)*sizeof(char));
end;

function ocopy(sString: string; iStartOneBased: nativeint; iLength:     nativeint): string;
begin
  result := zcopy(sString, iStartOneBased-1, iLength);
end;

Next, take this code which isn't a totally complete solution, but will give you most of your ansi-string support on mobile (with some slight caveats around pointers). But basically you can essentially convert strings by simply assigning an ansistring to a string type or vice verse. I had to hack this away from a couple of dependencies so I don't guarantee that it will compile out of the box, but it should be pretty close.

unit iosbytestring;

interface

uses
  sysutils, classes;

{$IFNDEF MSWINDOWS}
const STRZERO = 0;
{$ELSE}
const STRZERO = 1;
{$ENDIF}

type
  Tiosansichar = packed record
  private
    b: byte;
    class function AnsiFromChar(c: char): byte;static;
    class function CharFromAnsi(b: byte): char;static;

  public
    function ToChar: char;
    function ToOrd: byte;
    class operator Implicit(const s: Tiosansichar): string;
    class operator Implicit(const s: Tiosansichar): char;
    class operator Implicit(const s: Tiosansichar): byte;
    class operator Implicit(const s: Tiosansichar): pointer;
  end;

  Tiosbytestring = record
  private
    Fbytes: TBytes;
    function GetChar(idx: nativeint): char;
    procedure SetChar(idx: nativeint; const Value: char);
    function GetAddrOf(idx: nativeint): pbyte;
    function getbyte(idx: nativeint): byte;
    procedure setbyte(idx: nativeint; const Value: byte);
  public
    property chars[idx: nativeint]: char read GetChar write SetChar;
    property bytes[idx: nativeint]: byte read getbyte write setbyte;
    property addrof[idx: nativeint]: pbyte read GetAddrOf;
    class operator Implicit(const s: TIOSByteString): string;
    class operator Implicit(const s: string): TIOSByteString;
    class operator Add(const s1,s2: TIOSByteString): TIOSByteSTring;
    class operator Add(const s1: string; const s2: TIOSByteString):     TIOSByteSTring;
    class operator Add(const s1: TIOSByteString; const s2: string): TIOSByteSTring;
    procedure FromString(s: string);
    function ToString: string;
    procedure SetLength(i: nativeint);
  end;
  TIOSAnsiString = TIOSByteString;
{$IFNDEF MSWINDOWS}
  ansistring = TIOSByteString;
  utf8string = TIOSByteString;
  widestring = string;
{$ENDIF}


implementation

{ iosbytestring }

class operator Tiosbytestring.Add(const s1: string;
const s2: TIOSByteString): TIOSByteSTring;
var
  ss2,ss3: string;
begin
  ss2 := s2.ToString;
  ss3 := s1+ss2;
  result.FromString(ss3);

end;

class operator Tiosbytestring.Add(const s1: TIOSByteString;
const  s2: string): TIOSByteSTring;
var
  ss1,ss3: string;
begin
  ss1 := s1.ToString;
  ss3 := ss1+s2;
  result.FromString(ss3);
end;




procedure Tiosbytestring.FromString(s: string);
begin
  Fbytes := TEncoding.ANSI.GetBytes(s);

end;

function Tiosbytestring.GetAddrOf(idx: nativeint): pbyte;
begin
  result := @Fbytes[idx];
end;

function Tiosbytestring.getbyte(idx: nativeint): byte;
begin
  result := Fbytes[idx-strzero];
end;

function Tiosbytestring.GetChar(idx: nativeint): char;
begin
  result := Tiosansichar.CharFromAnsi(Fbytes[idx-strzero]);
end;

class operator Tiosbytestring.Implicit(const s: TIOSByteString): string;
begin
  result := s.ToString;

end;

class operator Tiosbytestring.Implicit(const s: string): TIOSByteString;
begin
  result.FromString(s);
end;

procedure Tiosbytestring.setbyte(idx: nativeint; const Value: byte);
begin
  Fbytes[idx-strzero] := value;
end;

class operator Tiosbytestring.Add(const s1,
  s2: TIOSByteString): TIOSByteSTring;
var
  ss1,ss2,ss3: string;
begin
  ss1 := s1.ToString;
  ss2 := s2.ToString;
  ss3 := ss1+ss2;
  result.FromString(ss3);

end;

procedure Tiosbytestring.SetChar(idx: nativeint; const Value: char);
begin
  Fbytes[idx-strzero] := Tiosansichar.AnsiFromChar(value);
end;

procedure Tiosbytestring.SetLength(i: nativeint);
begin
  system.setlength(Fbytes,i);
end;

function Tiosbytestring.ToString: string;
begin
  result := TEncoding.ANSI.GetString(Fbytes);
end;

{ Tiosansichar }


class function Tiosansichar.AnsiFromChar(c: char): byte;
var
  s: string;
  te: TEncoding;
  b: TBytes;
begin
  s := c;

  b := TEncoding.ANSI.GetBytes(c);
  result := b[0];


end;


class function Tiosansichar.CharFromAnsi(b: byte): char;
var
  s: string;
  bytes: TBytes;
begin
  system.setlength(bytes, 1);
  bytes[0] := b;
  s := TEncoding.ANSI.GetString(bytes, 0, 1);
  result := s[low(s)];
end;

class operator Tiosansichar.Implicit(const s: Tiosansichar): char;
begin
  result := s.ToChar;
end;

class operator Tiosansichar.Implicit(const s: Tiosansichar): string;
begin
  result := s.ToChar;
end;

class operator Tiosansichar.Implicit(const s: Tiosansichar): pointer;
begin
  result := @s.b;
end;

class operator Tiosansichar.Implicit(const s: Tiosansichar): byte;
begin
  result := s.b;
end;

function Tiosansichar.ToChar: char;
begin
  result := CharFromAnsi(b);
end;

function Tiosansichar.ToOrd: byte;
begin
  result := b;
end;

end.

So just add the above unit, add it to your uses clause, and magically, you'll have an ansistring type on your mobile platforms. Continue using the standard ansistring type on windows.

If all is well... this is how your code snippet might end up looking.

procedure DecodeLineAddr(const aLine: AnsiString; var ByteCount: integer; var     Address:Cardinal; var RecType: Integer);
var
  aLineWide: string;
begin
  aLineWide = aLine;
  //123 4567 89 0
  //:10 4640 00 0000 0600 0200 fa00 004f 7800 1e00 fb00 88
  ByteCount:= StrToInt('$' + ocopy(aLineWide, 2, 2));
  Address  := StrToInt('$' + ocopy(aLineWide, 4, 4));
  RecType  := StrToInt('$' + ocopy(aLineWide, 8, 2));
end;

Upvotes: 1

Related Questions