Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 当我对SMS PDU(GSM 7位)用户数据进行编码/解码时,是否需要先预编UDH?_Delphi_Sms_Concatenation_Gsm_Pdu - Fatal编程技术网

Delphi 当我对SMS PDU(GSM 7位)用户数据进行编码/解码时,是否需要先预编UDH?

Delphi 当我对SMS PDU(GSM 7位)用户数据进行编码/解码时,是否需要先预编UDH?,delphi,sms,concatenation,gsm,pdu,Delphi,Sms,Concatenation,Gsm,Pdu,当UDH不存在时,我可以成功地对SMS消息的用户数据部分进行编码和解码,但当UDH存在时(在本例中,对于串联SMS),我在编码和解码SMS消息时遇到困难 当我解码或编码用户数据时,我是否需要在这样做之前将UDH预加到文本中 本文提供了一个编码例程示例,它使用填充位(我仍然不完全理解)来补偿UDH,但它没有给出数据传递到例程的示例,因此我没有明确的用例(并且我在站点上找不到解码示例): 到目前为止,如果我在解码用户数据之前将UDH预加到用户数据中,我已经能够得到一些结果,但我怀疑这只是巧合 例如

当UDH不存在时,我可以成功地对SMS消息的用户数据部分进行编码和解码,但当UDH存在时(在本例中,对于串联SMS),我在编码和解码SMS消息时遇到困难

当我解码或编码用户数据时,我是否需要在这样做之前将UDH预加到文本中

本文提供了一个编码例程示例,它使用填充位(我仍然不完全理解)来补偿UDH,但它没有给出数据传递到例程的示例,因此我没有明确的用例(并且我在站点上找不到解码示例):

到目前为止,如果我在解码用户数据之前将UDH预加到用户数据中,我已经能够得到一些结果,但我怀疑这只是巧合

例如(使用来自的值):

输出:“你好,世界”

同样的输出:“你好,世界”

没有预先设置UDH,我会得到垃圾:

DecodedUserData := Decode7Bit(ENCODED_USER_DATA_PART);
Writeln(DecodedUserData);
输出:“PKYY§An§eYI”

正确的处理方法是什么

在对用户数据进行编码时,是否应该在文本中包含UDH

我是应该在解码后去掉垃圾字符,还是(正如我所怀疑的)完全偏离了这个假设

虽然这里的解码算法似乎在没有UDH的情况下工作,但它似乎没有考虑任何UDH信息:

如果有人能让我走上正确的道路,我将永远感激。任何清晰的示例/代码示例都将不胜感激。;-)

我还将提供一个小样本应用程序,其中包括算法,如果有人认为它将有助于解决这个谜

编辑1:

我正在使用Delphi XE2更新4修补程序1

编辑2:

多亏了@whosrdaddy的帮助,我成功地完成了编码/解码程序

作为旁注,我很好奇为什么当UDH没有编码时,用户数据需要在7位边界上,但是@whosrdaddy引用的ETSI规范中的最后一句话回答说:

如果使用7位数据,且TP UD报头未在septet边界上完成,则在最后一个边界后插入填充位 信息元素数据八位组,因此有整数个 整个TP-UD集管的七分之一这是为了确保SM 它本身从八位组边界开始,所以早期阶段是流动的 将能够显示SM本身,尽管TP-UD报头 在TP-UD字段中,可能无法理解

我的代码部分基于以下资源中的示例:

这是其他任何有短信编码/解码问题的人的代码。我确信它可以被简化/优化(并且欢迎评论),但是我已经用几种不同的排列和UDH头长度成功地测试了它。我希望有帮助

unit SmsUtils;

interface

uses Windows, Classes, Math;

function Encode7Bit(const AText: string; AUdhLen: Byte;
  out ATextLen: Byte): string;

function Decode7Bit(const APduData: string; AUdhLen: Integer): string;

implementation

var
  g7BitToAsciiTable: array [0 .. 127] of Byte;
  gAsciiTo7BitTable: array [0 .. 255] of Byte;

procedure InitializeTables;
var
  AsciiValue: Integer;
  i: Integer;
begin
  // create 7-bit to ascii table
  g7BitToAsciiTable[0] := 64; // @
  g7BitToAsciiTable[1] := 163;
  g7BitToAsciiTable[2] := 36;
  g7BitToAsciiTable[3] := 165;
  g7BitToAsciiTable[4] := 232;
  g7BitToAsciiTable[5] := 223;
  g7BitToAsciiTable[6] := 249;
  g7BitToAsciiTable[7] := 236;
  g7BitToAsciiTable[8] := 242;
  g7BitToAsciiTable[9] := 199;
  g7BitToAsciiTable[10] := 10;
  g7BitToAsciiTable[11] := 216;
  g7BitToAsciiTable[12] := 248;
  g7BitToAsciiTable[13] := 13;
  g7BitToAsciiTable[14] := 197;
  g7BitToAsciiTable[15] := 229;
  g7BitToAsciiTable[16] := 0;
  g7BitToAsciiTable[17] := 95;
  g7BitToAsciiTable[18] := 0;
  g7BitToAsciiTable[19] := 0;
  g7BitToAsciiTable[20] := 0;
  g7BitToAsciiTable[21] := 0;
  g7BitToAsciiTable[22] := 0;
  g7BitToAsciiTable[23] := 0;
  g7BitToAsciiTable[24] := 0;
  g7BitToAsciiTable[25] := 0;
  g7BitToAsciiTable[26] := 0;
  g7BitToAsciiTable[27] := 0;
  g7BitToAsciiTable[28] := 198;
  g7BitToAsciiTable[29] := 230;
  g7BitToAsciiTable[30] := 223;
  g7BitToAsciiTable[31] := 201;
  g7BitToAsciiTable[32] := 32;
  g7BitToAsciiTable[33] := 33;
  g7BitToAsciiTable[34] := 34;
  g7BitToAsciiTable[35] := 35;
  g7BitToAsciiTable[36] := 164;
  g7BitToAsciiTable[37] := 37;
  g7BitToAsciiTable[38] := 38;
  g7BitToAsciiTable[39] := 39;
  g7BitToAsciiTable[40] := 40;
  g7BitToAsciiTable[41] := 41;
  g7BitToAsciiTable[42] := 42;
  g7BitToAsciiTable[43] := 43;
  g7BitToAsciiTable[44] := 44;
  g7BitToAsciiTable[45] := 45;
  g7BitToAsciiTable[46] := 46;
  g7BitToAsciiTable[47] := 47;
  g7BitToAsciiTable[48] := 48;
  g7BitToAsciiTable[49] := 49;
  g7BitToAsciiTable[50] := 50;
  g7BitToAsciiTable[51] := 51;
  g7BitToAsciiTable[52] := 52;
  g7BitToAsciiTable[53] := 53;
  g7BitToAsciiTable[54] := 54;
  g7BitToAsciiTable[55] := 55;
  g7BitToAsciiTable[56] := 56;
  g7BitToAsciiTable[57] := 57;
  g7BitToAsciiTable[58] := 58;
  g7BitToAsciiTable[59] := 59;
  g7BitToAsciiTable[60] := 60;
  g7BitToAsciiTable[61] := 61;
  g7BitToAsciiTable[62] := 62;
  g7BitToAsciiTable[63] := 63;
  g7BitToAsciiTable[64] := 161;
  g7BitToAsciiTable[65] := 65;
  g7BitToAsciiTable[66] := 66;
  g7BitToAsciiTable[67] := 67;
  g7BitToAsciiTable[68] := 68;
  g7BitToAsciiTable[69] := 69;
  g7BitToAsciiTable[70] := 70;
  g7BitToAsciiTable[71] := 71;
  g7BitToAsciiTable[72] := 72;
  g7BitToAsciiTable[73] := 73;
  g7BitToAsciiTable[74] := 74;
  g7BitToAsciiTable[75] := 75;
  g7BitToAsciiTable[76] := 76;
  g7BitToAsciiTable[77] := 77;
  g7BitToAsciiTable[78] := 78;
  g7BitToAsciiTable[79] := 79;
  g7BitToAsciiTable[80] := 80;
  g7BitToAsciiTable[81] := 81;
  g7BitToAsciiTable[82] := 82;
  g7BitToAsciiTable[83] := 83;
  g7BitToAsciiTable[84] := 84;
  g7BitToAsciiTable[85] := 85;
  g7BitToAsciiTable[86] := 86;
  g7BitToAsciiTable[87] := 87;
  g7BitToAsciiTable[88] := 88;
  g7BitToAsciiTable[89] := 89;
  g7BitToAsciiTable[90] := 90;
  g7BitToAsciiTable[91] := 196;
  g7BitToAsciiTable[92] := 204;
  g7BitToAsciiTable[93] := 209;
  g7BitToAsciiTable[94] := 220;
  g7BitToAsciiTable[95] := 167;
  g7BitToAsciiTable[96] := 191;
  g7BitToAsciiTable[97] := 97;
  g7BitToAsciiTable[98] := 98;
  g7BitToAsciiTable[99] := 99;
  g7BitToAsciiTable[100] := 100;
  g7BitToAsciiTable[101] := 101;
  g7BitToAsciiTable[102] := 102;
  g7BitToAsciiTable[103] := 103;
  g7BitToAsciiTable[104] := 104;
  g7BitToAsciiTable[105] := 105;
  g7BitToAsciiTable[106] := 106;
  g7BitToAsciiTable[107] := 107;
  g7BitToAsciiTable[108] := 108;
  g7BitToAsciiTable[109] := 109;
  g7BitToAsciiTable[110] := 110;
  g7BitToAsciiTable[111] := 111;
  g7BitToAsciiTable[112] := 112;
  g7BitToAsciiTable[113] := 113;
  g7BitToAsciiTable[114] := 114;
  g7BitToAsciiTable[115] := 115;
  g7BitToAsciiTable[116] := 116;
  g7BitToAsciiTable[117] := 117;
  g7BitToAsciiTable[118] := 118;
  g7BitToAsciiTable[119] := 119;
  g7BitToAsciiTable[120] := 120;
  g7BitToAsciiTable[121] := 121;
  g7BitToAsciiTable[122] := 122;
  g7BitToAsciiTable[123] := 228;
  g7BitToAsciiTable[124] := 246;
  g7BitToAsciiTable[125] := 241;
  g7BitToAsciiTable[126] := 252;
  g7BitToAsciiTable[127] := 224;

  // create ascii to 7-bit table
  ZeroMemory(@gAsciiTo7BitTable, SizeOf(gAsciiTo7BitTable));
  for i := 0 to High(g7BitToAsciiTable) do
  begin
    AsciiValue := g7BitToAsciiTable[i];
    gAsciiTo7BitTable[AsciiValue] := i;
  end;
end;

function ConvertAsciiTo7Bit(const AText: string; AUdhLen: Byte): AnsiString;
const
  ESC = #27;
  ESCAPED_ASCII_CODES = [#94, #123, #125, #92, #91, #126, #93, #124, #164];
var
  Septet: Byte;
  Ch: AnsiChar;
  i: Integer;
begin
  for i := 1 to Length(AText) do
  begin
    Ch := AnsiChar(AText[i]);
    if not(Ch in ESCAPED_ASCII_CODES) then
      Septet := gAsciiTo7BitTable[Byte(Ch)]
    else
    begin
      Result := Result + ESC;
      case (Ch) of
        #12: Septet := 10;
        #94: Septet := 20;
        #123: Septet := 40;
        #125: Septet := 41;
        #92: Septet := 47;
        #91: Septet := 60;
        #126: Septet := 61;
        #93: Septet := 62;
        #124: Septet := 64;
        #164: Septet := 101;
      else Septet := 0;
      end;
    end;
    Result := Result + AnsiChar(Septet);
  end;
end;

function Convert7BitToAscii(const AText: AnsiString): string;
const
  ESC = #27;
var
  TextLen: Integer;
  Ch: Char;
  i: Integer;
begin
  Result := '';
  TextLen := Length(AText);
  i := 1;
  while (i <= TextLen) do
  begin
    Ch := Char(AText[i]);
    if (Ch <> ESC) then
      Result := Result + Char(g7BitToAsciiTable[Ord(Ch)])
    else
    begin
      Inc(i); // skip ESC
      if (i <= TextLen) then
      begin
        Ch := Char(AText[i]);
        case (Ch) of
          #10: Ch := #12;
          #20: Ch := #94;
          #40: Ch := #123;
          #41: Ch := #125;
          #47: Ch := #92;
          #60: Ch := #91;
          #61: Ch := #126;
          #62: Ch := #93;
          #64: Ch := #124;
          #101: Ch := #164;
        end;
        Result := Result + Ch;
      end;
    end;
    Inc(i);
  end;
end;

function StrToHex(const AText: AnsiString): AnsiString; overload;
var
  TextLen: Integer;
begin
  // set the text buffer size
  TextLen := Length(AText);
  // set the length of the result to double the string length
  SetLength(Result, TextLen * 2);
  // convert the string to hex
  BinToHex(PAnsiChar(AText), PAnsiChar(Result), TextLen);
end;

function StrToHex(const AText: string): string; overload;
begin
  Result := string(StrToHex(AnsiString(AText)));
end;

function HexToStr(const AText: AnsiString): AnsiString; overload;
var
  ResultLen: Integer;
begin
  // set the length of the result to half the Text length
  ResultLen := Length(AText) div 2;
  SetLength(Result, ResultLen);
  // convert the hex back into a string
  if (HexToBin(PAnsiChar(AText), PAnsiChar(Result), ResultLen) <> ResultLen) then
    Result := 'Error Converting Hex To String: ' + AText;
end;

function HexToStr(const AText: string): string; overload;
begin
  Result := string(HexToStr(AnsiString(AText)));
end;

function Encode7Bit(const AText: string; AUdhLen: Byte;
  out ATextLen: Byte): string;
// AText: Ascii text
// AUdhLen: Length of UDH including UDH Len byte (e.g. '050003CC0101' = 6 bytes)
// ATextLen: returns length of text that was encoded.  This can be different
// than Length(AText) due to escape characters
// Returns text as encoded PDU hex string
var
  Text7Bit: AnsiString;
  Pdu: AnsiString;
  PduIdx: Integer;
  PduLen: Byte;
  PaddingBits: Byte;
  BitsToMove: Byte;
  Septet: Byte;
  Octet: Byte;
  PrevOctet: Byte;
  ShiftedOctet: Byte;
  i: Integer;
begin
  Result := '';
  Text7Bit := ConvertAsciiTo7Bit(AText, AUdhLen);
  ATextLen := Length(Text7Bit);
  BitsToMove := 0;
  // determine how many padding bits needed based on the UDH
  if (AUdhLen > 0) then
    PaddingBits := 7 - ((AUdhLen * 8) mod 7)
  else
    PaddingBits := 0;
  // calculate the number of bytes needed to store the 7-bit text
  // along with any padding bits that are required
  PduLen := Ceil(((ATextLen * 7) + PaddingBits) / 8);
  // reserve space for the PDU bytes
  Pdu := AnsiString(StringOfChar(#0, PduLen));
  PduIdx := 1;
  for i := 1 to ATextLen do
  begin
    if (BitsToMove = 7) then
      BitsToMove := 0
    else
    begin
      // convert the current character to a septet (7-bits) and make room for
      // the bits from the next one
      Septet := (Byte(Text7Bit[i]) shr BitsToMove);
      if (i = ATextLen) then
        Octet := Septet
      else
      begin
        // convert the next character to a septet and copy the bits from it
        // to the octet (PDU byte)
        Octet := Septet or
          Byte((Byte(Text7Bit[i + 1]) shl Byte(7 - BitsToMove)));
      end;
      Byte(Pdu[PduIdx]) := Octet;
      Inc(PduIdx);
      Inc(BitsToMove);
    end;
  end;
  // The following code pads the pdu on the *right* by shifting it to the *left*
  // by <PaddingBits>. It does this by using the same bit storage convention as
  // the 7-bit compression routine above, by taking the most significant
  // <PaddingBits> from each PDU byte and moving them to the least significant
  // bits of the next PDU byte. If there is no room in the last PDU byte for the
  // high bits of the previous byte that were removed, then those bits are
  // placed into an additional byte reserved for this purpose.
  // Note: <PduLen> has already been set to account for the reserved byte if
  // it is required.
  if (PaddingBits > 0) then
  begin
    SetLength(Result, (PduLen * 2));
    PrevOctet := 0;
    for PduIdx := 1 to PduLen do
    begin
      Octet := Byte(Pdu[PduIdx]);
      if (PduIdx = 1) then
        ShiftedOctet := Byte(Octet shl PaddingBits)
      else
        ShiftedOctet := Byte(Octet shl PaddingBits) or
          Byte(PrevOctet shr (8 - PaddingBits));
      Byte(Pdu[PduIdx]) := ShiftedOctet;
      PrevOctet := Octet;
    end;
  end;
  Result := string(StrToHex(Pdu));
end;

function Decode7Bit(const APduData: string; AUdhLen: Integer): string;
// APduData: Hex string representation of PDU data
// AUdhLen: Length of UDH including UDH Len (e.g. '050003CC0101' = 6 bytes)
// Returns decoded Ascii text
var
  Pdu: AnsiString;
  NumSeptets: Byte;
  Septets: AnsiString;
  PduIdx: Integer;
  PduLen: Integer;
  by: Byte;
  currBy: Byte;
  left: Byte;
  mask: Byte;
  nextBy: Byte;
  Octet: Byte;
  NextOctet: Byte;
  PaddingBits: Byte;
  ShiftedOctet: Byte;
  i: Integer;
begin
  Result := '';
  PaddingBits := 0;
  // convert hex string to bytes
  Pdu := AnsiString(HexToStr(APduData));
  PduLen := Length(Pdu);
  // The following code removes padding at the end of the PDU by shifting it
  // *right* by <PaddingBits>. It does this by taking the least significant
  // <PaddingBits> from the following PDU byte and moving them to the most
  // significant the current PDU byte.
  if (AUdhLen > 0) then
  begin
    PaddingBits := 7 - ((AUdhLen * 8) mod 7);
    for PduIdx := 1 to PduLen do
    begin
      Octet := Byte(Pdu[PduIdx]);
      if (PduIdx = PduLen) then
        ShiftedOctet := Byte(Octet shr PaddingBits)
      else
      begin
        NextOctet := Byte(Pdu[PduIdx + 1]);
        ShiftedOctet := Byte(Octet shr PaddingBits) or
          Byte(NextOctet shl (8 - PaddingBits));
      end;
      Byte(Pdu[PduIdx]) := ShiftedOctet;
    end;
  end;
  // decode
  // number of septets in PDU after excluding the padding bits
  NumSeptets := ((PduLen * 8) - PaddingBits) div 7;
  Septets := AnsiString(StringOfChar(#0, NumSeptets));
  left := 7;
  mask := $7F;
  nextBy := 0;
  PduIdx := 1;
  for i := 1 to NumSeptets do
  begin
    if mask = 0 then
    begin
      Septets[i] := AnsiChar(nextBy);
      left := 7;
      mask := $7F;
      nextBy := 0;
    end
    else
    begin
      if (PduIdx > PduLen) then
        Break;
      by := Byte(Pdu[PduIdx]);
      Inc(PduIdx);
      currBy := ((by AND mask) SHL (7 - left)) OR nextBy;
      nextBy := (by AND (NOT mask)) SHR left;
      Septets[i] := AnsiChar(currBy);
      mask := mask SHR 1;
      left := left - 1;
    end;
  end; // for
  // remove last character if unused
  // this is kind of a hack, but frankly I don't know how else to compensate
  // for it.
  if (Septets[NumSeptets] = #0) then
    SetLength(Septets, NumSeptets - 1);
  // convert 7-bit alphabet to ascii
  Result := Convert7BitToAscii(Septets);
end;

initialization
  InitializeTables;
end.
单位符号;
接口
使用窗口、类、数学;
函数Encode7Bit(const-AText:string;AUdhLen:Byte;
out-ATextLen:Byte):字符串;
函数Decode7Bit(const APduData:string;AUdhLen:Integer):string;
实施
变量
G7BITTOASITABLE:字节数组[0..127];
GasciTo7BitTable:字节数组[0..255];
程序初始化表;
变量
ascivalue:整数;
i:整数;
开始
//创建7位到ascii表
G7BITTOASITABLE[0]:=64;//@
G7BITTOASITABLE[1]:=163;
G7BITTOASITABLE[2]:=36;
G7BITTOASITABLE[3]:=165;
G7BITTOASITABLE[4]:=232;
G7BITTOASITABLE[5]:=223;
G7BITTOASITABLE[6]:=249;
G7BITTOASITABLE[7]:=236;
G7BITTOASITABLE[8]:=242;
G7BITTOASITABLE[9]:=199;
G7BITTOASITABLE[10]:=10;
G7BITTOASITABLE[11]:=216;
G7BITTOASITABLE[12]:=248;
G7BITTOASITABLE[13]:=13;
G7BITTOASITABLE[14]:=197;
G7BITTOASITABLE[15]:=229;
G7BITTOASITABLE[16]:=0;
G7BITTOASITABLE[17]:=95;
G7BITTOASITABLE[18]:=0;
G7BITTOASITABLE[19]:=0;
G7BITTOASITABLE[20]:=0;
G7BITTOASITABLE[21]:=0;
G7BITTOASITABLE[22]:=0;
G7BITTOASITABLE[23]:=0;
G7BITTOASITABLE[24]:=0;
G7BITTOASITABLE[25]:=0;
G7BITTOASITABLE[26]:=0;
G7BITTOASITABLE[27]:=0;
G7BITTOASITABLE[28]:=198;
G7BITTOASITABLE[29]:=230;
G7BITTOASITABLE[30]:=223;
G7BITTOASITABLE[31]:=201;
G7BITTOASITABLE[32]:=32;
G7BITTOASITABLE[33]:=33;
G7BITTOASITABLE[34]:=34;
G7BITTOASITABLE[35]:=35;
G7BITTOASITABLE[36]:=164;
G7BITTOASITABLE[37]:=37;
G7BITTOASITABLE[38]:=38;
G7BITTOASITABLE[39]:=39;
G7BITTOASITABLE[40]:=40;
G7BITTOASITABLE[41]:=41;
G7BITTOASITABLE[42]:=42;
G7BITTOASITABLE[43]:=43;
G7BITTOASITABLE[44]:=44;
G7BITTOASITABLE[45]:=45;
G7BITTOASITABLE[46]:=46;
G7BITTOASITABLE[47]:=47;
G7BITTOASITABLE[48]:=48;
G7BITTOASITABLE[49]:=49;
G7BITTOASITABLE[50]:=50;
G7BITTOASITABLE[51]:=51;
G7BITTOASITABLE[52]:=52;
G7BITTOASITABLE[53]:=53;
G7BITTOASITABLE[54]:=54;
G7BITTOASITABLE[55]:=55;
G7BITTOASITABLE[56]:=56;
G7BITTOASITABLE[57]:=57;
G7BITTOASITABLE[58]:=58;
G7BITTOASITABLE[59]:=59;
G7BITTOASITABLE[60]:=60;
G7BITTOASITABLE[61]:=61;
G7BITTOASITABLE[62]:=62;
G7BITTOASITABLE[63]:=63;
G7BITTOASITABLE[64]:=161;
G7BITTOASITABLE[65]:=65;
G7BITTOASITABLE[66]:=66;
G7BITTOASITABLE[67]:=67;
G7BITTOASITABLE[68]:=68;
G7BITTOASITABLE[69]:=69;
G7BITTOASITABLE[70]:=70;
G7BITTOASITABLE[71]:=71;
G7BITTOASITABLE[72]:=72;
G7BITTOASITABLE[73]:=73;
G7BITTOASITABLE[74]:=74;
G7BITTOASITABLE[75]:=75;
G7BITTOASITABLE[76]:=76;
G7BITTOASITABLE[77]:=77;
g7BitToAsci
DecodedUserData := Decode7Bit(ENCODED_USER_DATA_PART);
Writeln(DecodedUserData);
unit SmsUtils;

interface

uses Windows, Classes, Math;

function Encode7Bit(const AText: string; AUdhLen: Byte;
  out ATextLen: Byte): string;

function Decode7Bit(const APduData: string; AUdhLen: Integer): string;

implementation

var
  g7BitToAsciiTable: array [0 .. 127] of Byte;
  gAsciiTo7BitTable: array [0 .. 255] of Byte;

procedure InitializeTables;
var
  AsciiValue: Integer;
  i: Integer;
begin
  // create 7-bit to ascii table
  g7BitToAsciiTable[0] := 64; // @
  g7BitToAsciiTable[1] := 163;
  g7BitToAsciiTable[2] := 36;
  g7BitToAsciiTable[3] := 165;
  g7BitToAsciiTable[4] := 232;
  g7BitToAsciiTable[5] := 223;
  g7BitToAsciiTable[6] := 249;
  g7BitToAsciiTable[7] := 236;
  g7BitToAsciiTable[8] := 242;
  g7BitToAsciiTable[9] := 199;
  g7BitToAsciiTable[10] := 10;
  g7BitToAsciiTable[11] := 216;
  g7BitToAsciiTable[12] := 248;
  g7BitToAsciiTable[13] := 13;
  g7BitToAsciiTable[14] := 197;
  g7BitToAsciiTable[15] := 229;
  g7BitToAsciiTable[16] := 0;
  g7BitToAsciiTable[17] := 95;
  g7BitToAsciiTable[18] := 0;
  g7BitToAsciiTable[19] := 0;
  g7BitToAsciiTable[20] := 0;
  g7BitToAsciiTable[21] := 0;
  g7BitToAsciiTable[22] := 0;
  g7BitToAsciiTable[23] := 0;
  g7BitToAsciiTable[24] := 0;
  g7BitToAsciiTable[25] := 0;
  g7BitToAsciiTable[26] := 0;
  g7BitToAsciiTable[27] := 0;
  g7BitToAsciiTable[28] := 198;
  g7BitToAsciiTable[29] := 230;
  g7BitToAsciiTable[30] := 223;
  g7BitToAsciiTable[31] := 201;
  g7BitToAsciiTable[32] := 32;
  g7BitToAsciiTable[33] := 33;
  g7BitToAsciiTable[34] := 34;
  g7BitToAsciiTable[35] := 35;
  g7BitToAsciiTable[36] := 164;
  g7BitToAsciiTable[37] := 37;
  g7BitToAsciiTable[38] := 38;
  g7BitToAsciiTable[39] := 39;
  g7BitToAsciiTable[40] := 40;
  g7BitToAsciiTable[41] := 41;
  g7BitToAsciiTable[42] := 42;
  g7BitToAsciiTable[43] := 43;
  g7BitToAsciiTable[44] := 44;
  g7BitToAsciiTable[45] := 45;
  g7BitToAsciiTable[46] := 46;
  g7BitToAsciiTable[47] := 47;
  g7BitToAsciiTable[48] := 48;
  g7BitToAsciiTable[49] := 49;
  g7BitToAsciiTable[50] := 50;
  g7BitToAsciiTable[51] := 51;
  g7BitToAsciiTable[52] := 52;
  g7BitToAsciiTable[53] := 53;
  g7BitToAsciiTable[54] := 54;
  g7BitToAsciiTable[55] := 55;
  g7BitToAsciiTable[56] := 56;
  g7BitToAsciiTable[57] := 57;
  g7BitToAsciiTable[58] := 58;
  g7BitToAsciiTable[59] := 59;
  g7BitToAsciiTable[60] := 60;
  g7BitToAsciiTable[61] := 61;
  g7BitToAsciiTable[62] := 62;
  g7BitToAsciiTable[63] := 63;
  g7BitToAsciiTable[64] := 161;
  g7BitToAsciiTable[65] := 65;
  g7BitToAsciiTable[66] := 66;
  g7BitToAsciiTable[67] := 67;
  g7BitToAsciiTable[68] := 68;
  g7BitToAsciiTable[69] := 69;
  g7BitToAsciiTable[70] := 70;
  g7BitToAsciiTable[71] := 71;
  g7BitToAsciiTable[72] := 72;
  g7BitToAsciiTable[73] := 73;
  g7BitToAsciiTable[74] := 74;
  g7BitToAsciiTable[75] := 75;
  g7BitToAsciiTable[76] := 76;
  g7BitToAsciiTable[77] := 77;
  g7BitToAsciiTable[78] := 78;
  g7BitToAsciiTable[79] := 79;
  g7BitToAsciiTable[80] := 80;
  g7BitToAsciiTable[81] := 81;
  g7BitToAsciiTable[82] := 82;
  g7BitToAsciiTable[83] := 83;
  g7BitToAsciiTable[84] := 84;
  g7BitToAsciiTable[85] := 85;
  g7BitToAsciiTable[86] := 86;
  g7BitToAsciiTable[87] := 87;
  g7BitToAsciiTable[88] := 88;
  g7BitToAsciiTable[89] := 89;
  g7BitToAsciiTable[90] := 90;
  g7BitToAsciiTable[91] := 196;
  g7BitToAsciiTable[92] := 204;
  g7BitToAsciiTable[93] := 209;
  g7BitToAsciiTable[94] := 220;
  g7BitToAsciiTable[95] := 167;
  g7BitToAsciiTable[96] := 191;
  g7BitToAsciiTable[97] := 97;
  g7BitToAsciiTable[98] := 98;
  g7BitToAsciiTable[99] := 99;
  g7BitToAsciiTable[100] := 100;
  g7BitToAsciiTable[101] := 101;
  g7BitToAsciiTable[102] := 102;
  g7BitToAsciiTable[103] := 103;
  g7BitToAsciiTable[104] := 104;
  g7BitToAsciiTable[105] := 105;
  g7BitToAsciiTable[106] := 106;
  g7BitToAsciiTable[107] := 107;
  g7BitToAsciiTable[108] := 108;
  g7BitToAsciiTable[109] := 109;
  g7BitToAsciiTable[110] := 110;
  g7BitToAsciiTable[111] := 111;
  g7BitToAsciiTable[112] := 112;
  g7BitToAsciiTable[113] := 113;
  g7BitToAsciiTable[114] := 114;
  g7BitToAsciiTable[115] := 115;
  g7BitToAsciiTable[116] := 116;
  g7BitToAsciiTable[117] := 117;
  g7BitToAsciiTable[118] := 118;
  g7BitToAsciiTable[119] := 119;
  g7BitToAsciiTable[120] := 120;
  g7BitToAsciiTable[121] := 121;
  g7BitToAsciiTable[122] := 122;
  g7BitToAsciiTable[123] := 228;
  g7BitToAsciiTable[124] := 246;
  g7BitToAsciiTable[125] := 241;
  g7BitToAsciiTable[126] := 252;
  g7BitToAsciiTable[127] := 224;

  // create ascii to 7-bit table
  ZeroMemory(@gAsciiTo7BitTable, SizeOf(gAsciiTo7BitTable));
  for i := 0 to High(g7BitToAsciiTable) do
  begin
    AsciiValue := g7BitToAsciiTable[i];
    gAsciiTo7BitTable[AsciiValue] := i;
  end;
end;

function ConvertAsciiTo7Bit(const AText: string; AUdhLen: Byte): AnsiString;
const
  ESC = #27;
  ESCAPED_ASCII_CODES = [#94, #123, #125, #92, #91, #126, #93, #124, #164];
var
  Septet: Byte;
  Ch: AnsiChar;
  i: Integer;
begin
  for i := 1 to Length(AText) do
  begin
    Ch := AnsiChar(AText[i]);
    if not(Ch in ESCAPED_ASCII_CODES) then
      Septet := gAsciiTo7BitTable[Byte(Ch)]
    else
    begin
      Result := Result + ESC;
      case (Ch) of
        #12: Septet := 10;
        #94: Septet := 20;
        #123: Septet := 40;
        #125: Septet := 41;
        #92: Septet := 47;
        #91: Septet := 60;
        #126: Septet := 61;
        #93: Septet := 62;
        #124: Septet := 64;
        #164: Septet := 101;
      else Septet := 0;
      end;
    end;
    Result := Result + AnsiChar(Septet);
  end;
end;

function Convert7BitToAscii(const AText: AnsiString): string;
const
  ESC = #27;
var
  TextLen: Integer;
  Ch: Char;
  i: Integer;
begin
  Result := '';
  TextLen := Length(AText);
  i := 1;
  while (i <= TextLen) do
  begin
    Ch := Char(AText[i]);
    if (Ch <> ESC) then
      Result := Result + Char(g7BitToAsciiTable[Ord(Ch)])
    else
    begin
      Inc(i); // skip ESC
      if (i <= TextLen) then
      begin
        Ch := Char(AText[i]);
        case (Ch) of
          #10: Ch := #12;
          #20: Ch := #94;
          #40: Ch := #123;
          #41: Ch := #125;
          #47: Ch := #92;
          #60: Ch := #91;
          #61: Ch := #126;
          #62: Ch := #93;
          #64: Ch := #124;
          #101: Ch := #164;
        end;
        Result := Result + Ch;
      end;
    end;
    Inc(i);
  end;
end;

function StrToHex(const AText: AnsiString): AnsiString; overload;
var
  TextLen: Integer;
begin
  // set the text buffer size
  TextLen := Length(AText);
  // set the length of the result to double the string length
  SetLength(Result, TextLen * 2);
  // convert the string to hex
  BinToHex(PAnsiChar(AText), PAnsiChar(Result), TextLen);
end;

function StrToHex(const AText: string): string; overload;
begin
  Result := string(StrToHex(AnsiString(AText)));
end;

function HexToStr(const AText: AnsiString): AnsiString; overload;
var
  ResultLen: Integer;
begin
  // set the length of the result to half the Text length
  ResultLen := Length(AText) div 2;
  SetLength(Result, ResultLen);
  // convert the hex back into a string
  if (HexToBin(PAnsiChar(AText), PAnsiChar(Result), ResultLen) <> ResultLen) then
    Result := 'Error Converting Hex To String: ' + AText;
end;

function HexToStr(const AText: string): string; overload;
begin
  Result := string(HexToStr(AnsiString(AText)));
end;

function Encode7Bit(const AText: string; AUdhLen: Byte;
  out ATextLen: Byte): string;
// AText: Ascii text
// AUdhLen: Length of UDH including UDH Len byte (e.g. '050003CC0101' = 6 bytes)
// ATextLen: returns length of text that was encoded.  This can be different
// than Length(AText) due to escape characters
// Returns text as encoded PDU hex string
var
  Text7Bit: AnsiString;
  Pdu: AnsiString;
  PduIdx: Integer;
  PduLen: Byte;
  PaddingBits: Byte;
  BitsToMove: Byte;
  Septet: Byte;
  Octet: Byte;
  PrevOctet: Byte;
  ShiftedOctet: Byte;
  i: Integer;
begin
  Result := '';
  Text7Bit := ConvertAsciiTo7Bit(AText, AUdhLen);
  ATextLen := Length(Text7Bit);
  BitsToMove := 0;
  // determine how many padding bits needed based on the UDH
  if (AUdhLen > 0) then
    PaddingBits := 7 - ((AUdhLen * 8) mod 7)
  else
    PaddingBits := 0;
  // calculate the number of bytes needed to store the 7-bit text
  // along with any padding bits that are required
  PduLen := Ceil(((ATextLen * 7) + PaddingBits) / 8);
  // reserve space for the PDU bytes
  Pdu := AnsiString(StringOfChar(#0, PduLen));
  PduIdx := 1;
  for i := 1 to ATextLen do
  begin
    if (BitsToMove = 7) then
      BitsToMove := 0
    else
    begin
      // convert the current character to a septet (7-bits) and make room for
      // the bits from the next one
      Septet := (Byte(Text7Bit[i]) shr BitsToMove);
      if (i = ATextLen) then
        Octet := Septet
      else
      begin
        // convert the next character to a septet and copy the bits from it
        // to the octet (PDU byte)
        Octet := Septet or
          Byte((Byte(Text7Bit[i + 1]) shl Byte(7 - BitsToMove)));
      end;
      Byte(Pdu[PduIdx]) := Octet;
      Inc(PduIdx);
      Inc(BitsToMove);
    end;
  end;
  // The following code pads the pdu on the *right* by shifting it to the *left*
  // by <PaddingBits>. It does this by using the same bit storage convention as
  // the 7-bit compression routine above, by taking the most significant
  // <PaddingBits> from each PDU byte and moving them to the least significant
  // bits of the next PDU byte. If there is no room in the last PDU byte for the
  // high bits of the previous byte that were removed, then those bits are
  // placed into an additional byte reserved for this purpose.
  // Note: <PduLen> has already been set to account for the reserved byte if
  // it is required.
  if (PaddingBits > 0) then
  begin
    SetLength(Result, (PduLen * 2));
    PrevOctet := 0;
    for PduIdx := 1 to PduLen do
    begin
      Octet := Byte(Pdu[PduIdx]);
      if (PduIdx = 1) then
        ShiftedOctet := Byte(Octet shl PaddingBits)
      else
        ShiftedOctet := Byte(Octet shl PaddingBits) or
          Byte(PrevOctet shr (8 - PaddingBits));
      Byte(Pdu[PduIdx]) := ShiftedOctet;
      PrevOctet := Octet;
    end;
  end;
  Result := string(StrToHex(Pdu));
end;

function Decode7Bit(const APduData: string; AUdhLen: Integer): string;
// APduData: Hex string representation of PDU data
// AUdhLen: Length of UDH including UDH Len (e.g. '050003CC0101' = 6 bytes)
// Returns decoded Ascii text
var
  Pdu: AnsiString;
  NumSeptets: Byte;
  Septets: AnsiString;
  PduIdx: Integer;
  PduLen: Integer;
  by: Byte;
  currBy: Byte;
  left: Byte;
  mask: Byte;
  nextBy: Byte;
  Octet: Byte;
  NextOctet: Byte;
  PaddingBits: Byte;
  ShiftedOctet: Byte;
  i: Integer;
begin
  Result := '';
  PaddingBits := 0;
  // convert hex string to bytes
  Pdu := AnsiString(HexToStr(APduData));
  PduLen := Length(Pdu);
  // The following code removes padding at the end of the PDU by shifting it
  // *right* by <PaddingBits>. It does this by taking the least significant
  // <PaddingBits> from the following PDU byte and moving them to the most
  // significant the current PDU byte.
  if (AUdhLen > 0) then
  begin
    PaddingBits := 7 - ((AUdhLen * 8) mod 7);
    for PduIdx := 1 to PduLen do
    begin
      Octet := Byte(Pdu[PduIdx]);
      if (PduIdx = PduLen) then
        ShiftedOctet := Byte(Octet shr PaddingBits)
      else
      begin
        NextOctet := Byte(Pdu[PduIdx + 1]);
        ShiftedOctet := Byte(Octet shr PaddingBits) or
          Byte(NextOctet shl (8 - PaddingBits));
      end;
      Byte(Pdu[PduIdx]) := ShiftedOctet;
    end;
  end;
  // decode
  // number of septets in PDU after excluding the padding bits
  NumSeptets := ((PduLen * 8) - PaddingBits) div 7;
  Septets := AnsiString(StringOfChar(#0, NumSeptets));
  left := 7;
  mask := $7F;
  nextBy := 0;
  PduIdx := 1;
  for i := 1 to NumSeptets do
  begin
    if mask = 0 then
    begin
      Septets[i] := AnsiChar(nextBy);
      left := 7;
      mask := $7F;
      nextBy := 0;
    end
    else
    begin
      if (PduIdx > PduLen) then
        Break;
      by := Byte(Pdu[PduIdx]);
      Inc(PduIdx);
      currBy := ((by AND mask) SHL (7 - left)) OR nextBy;
      nextBy := (by AND (NOT mask)) SHR left;
      Septets[i] := AnsiChar(currBy);
      mask := mask SHR 1;
      left := left - 1;
    end;
  end; // for
  // remove last character if unused
  // this is kind of a hack, but frankly I don't know how else to compensate
  // for it.
  if (Septets[NumSeptets] = #0) then
    SetLength(Septets, NumSeptets - 1);
  // convert 7-bit alphabet to ascii
  Result := Convert7BitToAscii(Septets);
end;

initialization
  InitializeTables;
end.
Len := Length(UDHPart) shr 1;
Offset := 7 - ((Len * 8) mod 7);  // fill bits
 // fill bits
 if Offset > 0 then
  begin
   v := Result;
   Len := Length(v);
   BytesRemain := ceil(((Len * 7)+Offset) / 8);       
   Result := StringOfChar(#0, BytesRemain);
   for InPos := 1 to BytesRemain do
    begin
     if InPos = 1 then
      Byte(Result[InPos]) := Byte(v[InPos]) shl offset
     else
      Byte(Result[InPos]) := (Byte(v[InPos]) shl offset) or (Byte(v[InPos-1]) shr (8 - offset));
    end;
  end;