unit uContent;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                                                             /
//                        Structure of Tokens                                                                                                                  /
//                                                                                                                                                             /
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

{---------------------------------------------------------------------------------------------------------------------------------------------------------------
       BB5 Flash-token:

Type 1
54  01 17  0E  00 00 00  0D BA  01  00 00 40 00  01 6E 00 00  62
TH  ST ST  HL   DEVICE   DC DC  UN  LL LL LL LL  SS SS SS SS  HC

Type 2
54  01 30  15  00 03 00  A2 72  00 00 00 08 FF FF FF FF 00 04 00 00 00 00 00 00 9A
TH  ST ST  HL  FI FI FI  DC DC  ?? ?? ?? ?? ?? ?? ?? ?? LL LL LL LL SS SS SS SS HC

TH - Token Header
ST - SubType (type of token)
HL - Header Length
FF - Flash ID
DC - Data Checksum
LL - Length of data between two tokens
SS - Start adress in flash
HC - Header Checksum

---------------------------------------------------------------------------------------------------------------------------------------------------------------
       BB5 Cert-token

Type 1
5D 01 27 2D ............... .............. 00 00 00 23 0C 00 00 04 00 00 00 06 C0 AE
5D 01 27 2D ............... .............. 00 00 00 8D B6 00 00 04 00 00 00 06 C0 D6
5D 01 27 2D ............... .............. 01 01 00 C1 26 00 00 04 00 00 07 00 00 48
5D 01 27 2D 38F312750F686F9FC9B1B3778774A19550F16CA3 4E4F4C4F0000000000000000000000 55B9 00000400 000402C0 3E
                   Root Key Hash                              NOLO
TH ST ST HL          [20 byte SHA1?]                     [12 byte name]      FFFFFF DCDC LLLLLLLL SSSSSSSS HC

Type 2
5D 01 28 43 38F312750F686F9FC9B1B3778774A195 50F16CA3 50415055424B455953000000000000 DFD0 00000600 000582C0 3D744C60D026865F8B384F71E83AA7A06FC122770100 99
5D 01 28 43 BAF3A9C3DBFA8454937DB77F2B8852B1 24CD5A6C 50415055424B455953000000000000 1E0C 00000600 000582C0 3D744C60D026865F8B384F71E83AA7A06FC122770100 01
5D 01 28 43 E9EFF4BFAA5393217CA6B17755FC3E14 15658E9A 50415055424B455953000000000000 1D26 00000600 FFFFFFFF 643B2F33E3E6EEFC2D6F45213419C8E368FCB9960100 6D
                   Root Key Hash                         PAPUBKEYS
TH ST ST HL                          [20 byte SHA1?]        [12 byte name]    FFFFFF DCDC LLLLLLLL SSSSSSSS             [20 bytes SHA1?]                 HC

TH      HLEN                                        NAME/DESC                      DCHK BLKSZ             LAST_BYTE_IS_H_CHECKSUM

---------------------------------------------------------------------------------------------------------------------------------------------------------------

       BB5 Image-token

allways applied in starting image-token
F0F0 0001 FF00 0000 FFF0 FFFF 00000000
[some identifier]   [ident?]  [ part ]

part is starting with 0

every 0x208 bytes (including this 8 bytes):
FFF0 FFFF [ part ]

every 0x20000 bytes this sequence is starting again but part is still continuing

letzter 0x20000 part:
F0F0 0001 FFC0 0000 FFF0 FFFF 0000 xxxx
            yy <- $C0 marks end

---------------------------------------------------------------------------------------------------------------------------------------------------------------}
interface

uses Classes, SysUtils, Windows, Hexutils;

type
    TEraseArea = record
    BeginErase    : Cardinal;
    EndErase      : Cardinal;
    FlashingArea  : Boolean;
end;

var
    buf        : array[0..$3FFFF] of byte;
    EraseArea  : array of TEraseArea;


procedure GetPageSize(img: string; var PageSize: byte; var res:Boolean);
procedure GetFlashArea(img: string; {EraseArea: array of TEraseArea;} var res:Boolean);
procedure Image64to128(img: string; dest: TMemoryStream;var Res:boolean);
procedure DeletePhoenixTokens(var ms:TMemoryStream; {var EraseArea: array of TEraseArea;}var res:Boolean; var FirstTokenType:Word; var SizeOfDataInToken: Cardinal);
procedure InsertPhoenixTokens(sc:TMemoryStream; var res:Boolean; var TokenType:Word; var Offset, SizeOfDataInToken: Cardinal);
function  FlashTokenChekSum(Buf:Pointer; BufLen: integer): Word;
function  FlashTokenCRC(Buf:Pointer; BufLen: integer): byte;

implementation

procedure GetPageSize(img: string; var PageSize: byte; var res:Boolean);
var
     HdrLen,
     HdrSeems,
     Count,
     BlockCount,
     CurrBlock,
     CurrBlockLen     : Integer;
     ConvBlockPresent : Boolean;
     ImagePageSize    : Word;
     ms               : TMemoryStream;
begin
     res:=false;
     ms:=TMemoryStream.Create;
     ms.LoadFromFile(img);
     currblock:=0;
     CurrBlockLen:=0;
     ms.Read(buf[0] ,1);
     if buf[0]<>$B2 then
     begin
          ms.Free;
          exit;
     end;

     ms.ReadBuffer(buf[0], 4);//Длина Flash-header'a
     HdrLen    := HexToInt(IntToHex(buf[0], 2)+IntToHex(buf[1], 2)+IntToHex(buf[2], 2)+IntToHex(buf[3], 2));

     ms.ReadBuffer(buf[0], 4);//Количество блоков в Flash-header'е
     BlockCount:= HexToInt(IntToHex(buf[0], 2)+IntToHex(buf[1], 2)+IntToHex(buf[2], 2)+IntToHex(buf[3], 2));
     HdrSeems := HdrLen+5;

     Count:=0; ConvBlockPresent:=false;
     while (ms.Position<HdrSeems) and (Count<BlockCount) do
     begin
          ms.ReadBuffer(CurrBlock, 1);
          case CurrBlock of
               $E4,
               $E5,
               $EA,
               $EE,
               $F3: begin
                         ms.ReadBuffer(buf[0], 2);
                         CurrBlockLen:=(buf[0]*$100)+buf[1];
                         ms.Seek(CurrBlockLen, sofromCurrent);
                         inc(Count);
                    end;

               $F5: begin //CNT_PN_Sector
                         ConvBlockPresent:=true;
                         ms.ReadBuffer(CurrBlockLen, 1);
                         if CurrBlockLen<>2 then
                         begin
                              ms.Free;
                              exit;
                         end;
                         ms.ReadBuffer(ImagePageSize, CurrBlockLen);
                         inc(Count);

                         case ImagePageSize of
                              $1100: begin
                                          PageSize:=128;
                                          res:=true;
                                     end;
                              $1000: begin
                                          PageSize:=64;
                                          res:=true;
                                     end;
                         end;//end of case ImagePageSize
                    end;
                    else
                         begin
                              ms.ReadBuffer(CurrBlockLen, 1);
                              ms.Seek(CurrBlockLen, sofromCurrent);
                              inc(Count);
                         end;
          end;//end of case CurrBlock
     end;//end of while
     ms.Free;
     //Если блок F5 отсутствует, то контент имеет 128K-формат
     if Not ConvBlockPresent then
     begin
          PageSize:=128;
          res:=true;
     end;
end;

procedure GetFlashArea(img: string; {EraseArea: array of TEraseArea;} var res:Boolean);
var
     HdrLen,
     HdrSeems,
     CurrSeemLen,
     CurrEraseLen,
     Count, i, j,
     BlockCount,
     CurrBlock,
     CurrBlockLen,
     EraseCnt        : Integer;
     FlashCFG, Byte1,
     Byte2,    Byte3 : Byte;
     ms              : TMemoryStream;
begin
     res:=false;
     ms:=TMemoryStream.Create;
     ms.LoadFromFile(img);
     ms.Read(buf[0] ,1);
     if buf[0]<>$B2 then
     begin
          ms.Free;
          exit;
     end;

     ms.ReadBuffer(buf[0], 4);//Длина Flash-header'a
     HdrLen    := HexToInt(IntToHex(buf[0], 2)+IntToHex(buf[1], 2)+IntToHex(buf[2], 2)+IntToHex(buf[3], 2));

     ms.ReadBuffer(buf[0], 4);//Количество блоков в Flash-header'е
     BlockCount:= HexToInt(IntToHex(buf[0], 2)+IntToHex(buf[1], 2)+IntToHex(buf[2], 2)+IntToHex(buf[3], 2));
     HdrSeems := HdrLen+5;

     Count:=0;
     while (ms.Position<HdrSeems) and (Count<BlockCount) do
     begin
          ms.ReadBuffer(CurrBlock, 1);
          case CurrBlock of
               $E4,
               $E5,
               $EA,
               $EE,
               $F3: begin
                         ms.ReadBuffer(buf[0], 2);
                         CurrBlockLen:=(buf[0]*$100)+buf[1];
                         ms.Seek(CurrBlockLen, sofromCurrent);
                         inc(Count);
                    end;

               $C8: begin//Erease Area
                         ms.ReadBuffer(CurrBlockLen, 1);
                         ms.ReadBuffer(CurrSeemLen, 1);

                         for i:=1 to CurrSeemLen do
                         begin
                              ms.ReadBuffer(FlashCfg, 1);
                              ms.ReadBuffer(CurrEraseLen, 1);
                              Dec(CurrEraseLen, 3);

                              ms.ReadBuffer(Byte1, 1);
                              ms.ReadBuffer(Byte2, 1);
                              ms.ReadBuffer(Byte3, 1);

                              EraseCnt:=CurrEraseLen div 8;
                              SetLength(EraseArea, EraseCnt);

                              for j := 0 to EraseCnt-1 do
                              begin
                                   ms.ReadBuffer(buf[0], 4);
                                   EraseArea[j].BeginErase:= HexToInt(IntToHex(buf[0], 2)+IntToHex(buf[1], 2)+IntToHex(buf[2], 2)+IntToHex(buf[3], 2));
                                   ms.ReadBuffer(buf[0], 4);
                                   EraseArea[j].EndErase  := HexToInt(IntToHex(buf[0], 2)+IntToHex(buf[1], 2)+IntToHex(buf[2], 2)+IntToHex(buf[3], 2));
                                   EraseArea[j].FlashingArea:=false;
                                   res:=true;
                              end;
                         end;
                         inc(Count);
                    end;
               else
               begin
                    ms.ReadBuffer(CurrSeemLen, 1);
                    ms.Seek(CurrSeemLen, sofromCurrent);
                    inc(Count);
               end;

          end;//end of case
     end;
     ms.Free;
end;

procedure Image64to128(img: string; dest: TMemoryStream;var Res:boolean);
var  i              : integer;
     buf            : array[0..$3FFFF] of byte;
     HdrLen,
     HdrSeems       : Dword;
     CurrSeem,
     CurrSeemLen,
     DataLen        : LongInt;
     fs             : TFileStream;
     head, ms,
     tmp, sc        : TMemoryStream;
     ImagePageSize  : Word;
     TokenType      : Word;
     SizeDataTok,
     FlashStart,
     FlashEnd,
     FlashLen       : Cardinal;
     b              : Boolean;
begin
     Res:=false;
     fs:=TFileStream.Create(img,fmOpenRead);
     fs.Read(buf[0] ,1);
     if buf[0]<>$B2 then
     begin
          fs.Free;
          exit;
     end;

     fs.ReadBuffer(buf[0], 4);//Длина Flash-header'a
     HdrLen   := HexToInt(IntToHex(buf[0], 2)+IntToHex(buf[1], 2)+IntToHex(buf[2], 2)+IntToHex(buf[3], 2));
     HdrSeems := HdrLen+5;

     head:=TMemoryStream.Create;
     ms:=TMemoryStream.Create;
     fs.Seek(0, sofrombeginning);
     head.CopyFrom(fs, HdrSeems);
     ms.CopyFrom(fs, fs.Size-HdrSeems);
     fs.Free;
     ms.Seek(0, sofrombeginning);
     //убираем flash-tokens
     DeletePhoenixTokens(ms, {EraseArea,} b, TokenType, SizeDataTok);
     if Not b then
     begin
          ms.free;
          head.Free;
          exit;
     end;

     //Убираем разметку токенами в 64K
     dest.CopyFrom(ms, $0FFF8);
     ms.ReadBuffer(buf[0], $10);

     b:=true; DataLen:=$0FFF0;
     while (ms.Position<ms.Size) and b do
     begin
          dest.CopyFrom(ms, DataLen);
          //Имеется ли возможность прочитать еще 16 байт, которые необходимо пропустить
          if ms.size-ms.position<$10 then
          begin
               DataLen:= ms.size-ms.position;
               b:=false;
          end
          else ms.ReadBuffer(buf[0], $10)//ms1.Seek($10, soFromCurrent); //возможность есть, пропускаем $10 байт
     end;
     ms.Clear;
     //dest.SavetoFile('G:\Coding\My Project\!!!64to128\Image\manual\_my_prog_64_without_header_flash_tokens');

     //Переразметка токенами в 128K
     dest.Seek(0, soFromBeginning);
     for i:= $11 to $20 do buf[i]:=$FF;
     ms.CopyFrom(dest, $1FFE8);
     buf[12]:=$00;

     b:=true; DataLen:=$1FFE0;

     while (dest.Position<dest.Size) and b do
     begin
          if (dest.size-dest.position)>DataLen then
          begin
               ms.WriteBuffer(buf[$11], $10);
               ms.WriteBuffer(buf[0]  , $10);
               ms.CopyFrom(dest, DataLen);
          end else
          begin
               DataLen:= dest.size-dest.position;
               buf[12]:=$C0;//это последний токен в данном имаже, его признак - значение $C0
               ms.WriteBuffer(buf[$11], $10);
               ms.WriteBuffer(buf[0]  , $10);
               //Скопируем остаток данных
               ms.CopyFrom(dest, DataLen);
               //Пересчет свободного места, необходимо добавить "пустые" данные
               //необходимо для сохранения структуры/размера данных, входящих в последний токен
               DataLen:=$1FFF8-DataLen;
               for i:=0 to DataLen-1 do buf[i]:=$FF;
               buf[DataLen-2]:=$F0;
               buf[DataLen-1]:=$F0;
               ms.WriteBuffer(buf[0], DataLen);
               b:=false;
               continue;
          end;
     end;
     dest.clear;
     //ms.SavetoFile('G:\Coding\My Project\!!!64to128\Image\manual\_my_prog_128_without_header_flash_tokens');
     ms.seek(0, sofrombeginning);

     head.Seek(9, sofrombeginning);

     while head.Position<HdrSeems do
     begin
          head.ReadBuffer(CurrSeem, 1);

          if (CurrSeem=$E4) or (CurrSeem=$E5) or (CurrSeem=$EA) or
             (CurrSeem=$EE) or (CurrSeem=$F3) then
          begin
               head.ReadBuffer(buf[0], 2);
               CurrSeemLen:=(buf[0]*$100)+buf[1];
               head.Seek(CurrSeemLen, sofromCurrent);
          end else

          begin
               head.ReadBuffer(CurrSeemLen, 1);
               if CurrSeemLen<1 then head.ReadBuffer(CurrSeemLen, 1);

               //Изменение данных о разметке image-файла
               if CurrSeem = $F5 then
               begin
                    ImagePageSize:=$1100;
                    head.Write(ImagePageSize, 2);
                    continue;
               end
               else
               begin
                    head.ReadBuffer(CurrSeemLen, 1);
                    head.Seek(CurrSeemLen, sofromCurrent);
               end;
          end;
     end;

     head.Seek(0, sofrombeginning);
     dest.CopyFrom(head, head.Size);
     head.Free;

     for i:=0 to Length(EraseArea)-1 do
     if EraseArea[i].FlashingArea then //ищем Image Area
     begin
          FlashStart:=EraseArea[i].BeginErase;
          FlashEnd  :=EraseArea[i].EndErase;
          FlashLen  :=FlashEnd-FlashStart;//длина текущего сегмента памяти
     end;

     res:=false; b:=true;
     sc:=TMemoryStream.Create;

     while (ms.Position<ms.Size) and b do
     begin
          tmp:=TMemoryStream.Create;

          if (ms.size-ms.position)<FlashLen then
          begin
               FlashLen:=ms.size-ms.position;
               b:=false;
          end;

          tmp.CopyFrom(ms, FlashLen);//кусочек без flash-токенов
          InsertPhoenixTokens(tmp, res, TokenType, FlashStart, SizeDataTok);
          if Not Res then
          begin
               tmp.Free;
               sc.Free;
               ms.free;
               res:=false;
               exit;
          end;

          sc.CopyFrom(tmp, tmp.Size);
          tmp.Free;
     end;//end while
     ms.Free;
     sc.Seek(0, sofrombeginning);
     dest.CopyFrom(sc, sc.Size);
     sc.free;
     Res:=true;
end;

procedure DeletePhoenixTokens(var ms:TMemoryStream; {var EraseArea: array of TEraseArea;}var res:Boolean; var FirstTokenType:Word; var SizeOfDataInToken: Cardinal);
var
     b              : boolean;
     i              : integer;
     TokenBuf       : array of byte;
     Data           : Byte;
     CurrTokenType  : Word;
     CurrTokenSize  : Cardinal;
     FlashAddress   : Cardinal;
     dest           : TMemoryStream;
begin
     res:=false;
     b:=true;
     dest:=TMemoryStream.Create;
     while ms.Position<ms.Size do
     Begin
          ms.ReadBuffer(Data, 1);
          if Data=$54 then
          begin
               ms.ReadBuffer(CurrTokenType, 2);
               ms.ReadBuffer(Data, 1);
               SetLength(TokenBuf, Data+1);//length of header flash-token
               ms.ReadBuffer(TokenBuf[0],Data+1);// на единицу больше с учетом CRC
               case CurrTokenType of
               $1701:
                     begin
                          CurrTokenSize:=HexToInt(IntToHex(TokenBuf[6], 2)+IntToHex(TokenBuf[7], 2)+IntToHex(TokenBuf[8], 2)+IntToHex(TokenBuf[9], 2));
                          FlashAddress :=HexToInt(IntToHex(TokenBuf[10], 2)+IntToHex(TokenBuf[11], 2)+IntToHex(TokenBuf[12], 2)+IntToHex(TokenBuf[13], 2));
                          if b then
                          begin
                               FirstTokentype:=CurrTokenType;//Копирование первого значения о типе применяемого токена для дальнейшей "переразметки"
                               SizeOfDataInToken:=CurrTokenSize;//Копирование первого значения размера токен блока для дальнейшей "переразметки"
                               b:=false;
                          end;
                          for i:=0 to Length(EraseArea)-1 do
                          if (FlashAddress >= EraseArea[i].BeginErase) and (FlashAddress <= EraseArea[i].EndErase) then EraseArea[i].FlashingArea:=true;
                     end;
               $3001:
                     begin
                          CurrTokenSize:=HexToInt(IntToHex(TokenBuf[17], 2)+IntToHex(TokenBuf[18], 2)+IntToHex(TokenBuf[19], 2)+IntToHex(TokenBuf[20], 2));
                          FlashAddress :=HexToInt(IntToHex(TokenBuf[21], 2)+IntToHex(TokenBuf[22], 2)+IntToHex(TokenBuf[23], 2)+IntToHex(TokenBuf[24], 2));
                          if b then
                          begin
                               FirstTokentype:=CurrTokenType;//Копирование первого значения о типе применяемого токена для дальнейшей "переразметки"
                               SizeOfDataInToken:=CurrTokenSize;//Копирование первого значения размера токен блока для дальнейшей "переразметки"
                               b:=false;
                          end;
                          for i:=0 to Length(EraseArea)-1 do
                          if (FlashAddress >= EraseArea[i].BeginErase) and (FlashAddress <= EraseArea[i].EndErase) then EraseArea[i].FlashingArea:=true;
                     end else
                     begin
                          //Если текущее значение не равно $54, значит нарушена структурность разметки flash-token, либо новый тип разметки -> Error
                          dest.Free;
                          exit;
                     end;
               end;//end of case

               //Если имеются различия между данными о длине блока в токене и реальным значением -> Bad Image File
               if (ms.size-ms.position)<CurrTokenSize then
               begin
                    dest.Free;
                    exit;
               end;
               dest.CopyFrom(ms, CurrTokenSize);
          end;
     end;
     ms.Clear;
     dest.Seek(0,soFromBeginning);
     ms.CopyFrom(dest, dest.Size);
     dest.free;
     ms.Seek(0, soFromBeginning);
     res:=true;
end;

procedure InsertPhoenixTokens(sc:TMemoryStream; var res:Boolean; var TokenType:Word; var Offset, SizeOfDataInToken: Cardinal);
var
     b                  : boolean;
     TokenBuf           : array of byte;
     DataBuf            : array of byte;
     CurrTokenDataCheck : Word;
     dest               : TMemoryStream;
begin
     res:=false;
     dest:=TMemoryStream.Create;
     sc.Seek(0, sofromBeginning);
     b:=true;//признак последнего блока данные меньшего по размерам, чем стандартный

     if TokenType=$1701 then SetLength(TokenBuf, $13) else
     if TokenType=$3001 then SetLength(TokenBuf, $19) else
     begin
          dest.Free;
          exit;
     end;
     SetLength(DataBuf, SizeOfDataInToken);

     while (sc.Position<sc.Size) and b do
     Begin
          //Возможно ли прочитать блок данных длиной SizeOfDataInToken
          //Необходимо для чтения остатка (для последнего блока данных)
          if (sc.size-sc.position)<SizeOfDataInToken then
               begin
                    SizeOfDataInToken:=sc.size-sc.position;
                    b:=false;
               end;

          sc.ReadBuffer(DataBuf[0], SizeOfDataInToken);
          CurrTokenDataCheck:=FlashTokenChekSum(@DataBuf[0], SizeOfDataInToken);
          case TokenType of
          $1701:
                begin
                     TokenBuf[0]:=$54;//TH - Token Header
                     //ST - SubType (type of token)
                     TokenBuf[1]:=$01;
                     TokenBuf[2]:=$17;
                     TokenBuf[3]:=$0E;//HL - Header Length
                     //May be Flash ID
                     TokenBuf[4]:=$00;
                     TokenBuf[5]:=$00;
                     TokenBuf[6]:=$00;
                     Word((@TokenBuf[7])^) :=CurrTokenDataCheck;//DC - Data Checksum
                     TokenBuf[9]:=$01;//Unknow
                     //LL - Length of data between two tokens
                     Word((@TokenBuf[10])^):=Swap((SizeOfDataInToken and $FFFF0000) shr 16);
                     Word((@TokenBuf[12])^):=Swap((SizeOfDataInToken and $0000FFFF));
                     //SS - Start adress in flash
                     Word((@TokenBuf[14])^):=Swap((Offset and $FFFF0000) shr 16);
                     Word((@TokenBuf[16])^):=Swap(Offset and $0000FFFF);
                     Byte((@TokenBuf[18])^):=FlashTokenCRC(@TokenBuf[1], Length(TokenBuf)-2);//HC - Header Checksum
                end;
          $3001:
                begin
                     TokenBuf[0] :=$54;//TH - Token Header
                     //ST - SubType (type of token)
                     TokenBuf[1] :=$01;
                     TokenBuf[2] :=$30;
                     TokenBuf[3] :=$15;//HL - Header Length
                     //May be Flash ID
                     TokenBuf[4] :=$00;
                     TokenBuf[5] :=$03;
                     TokenBuf[6] :=$00;
                     Word((@TokenBuf[7])^) :=CurrTokenDataCheck;//DC - Data Checksum
                     //Unknow
                     TokenBuf[9] :=$00;
                     TokenBuf[10]:=$00;
                     TokenBuf[11]:=$00;
                     TokenBuf[12]:=$08;
                     TokenBuf[13]:=$FF;
                     TokenBuf[14]:=$FF;
                     TokenBuf[15]:=$FF;
                     TokenBuf[16]:=$FF;
                     //LL - Length of data between two tokens
                     Word((@TokenBuf[17])^):=Swap((SizeOfDataInToken and $FFFF0000) shr 16);
                     Word((@TokenBuf[19])^):=Swap((SizeOfDataInToken and $0000FFFF));
                     //SS - Start adress in flash
                     Word((@TokenBuf[21])^):=Swap((Offset and $FFFF0000) shr 16);
                     Word((@TokenBuf[23])^):=Swap(Offset and $0000FFFF);
                     Byte((@TokenBuf[25])^):=FlashTokenCRC(@TokenBuf[1], Length(TokenBuf)-2);//HC - Header Checksum
                end;
          end;//end of case
          Offset:=Offset+SizeOfDataInToken;
          dest.WriteBuffer(TokenBuf[0], Length(TokenBuf));
          dest.WriteBuffer(DataBuf[0], SizeOfDataInToken);
    end;
    sc.clear;
    dest.Seek(0, sofrombeginning);
    sc.CopyFrom(dest, dest.Size);
    sc.Seek(0, sofrombeginning);
    dest.Free;
    res:=true;
end;

//Расчет контрольной суммы блока данных (DC - Data Checksum)
function  FlashTokenChekSum(Buf:Pointer; BufLen: integer): Word;
begin
     Result:=Word(Buf^);
     inc(Integer(Buf));dec(BufLen);
     inc(Integer(Buf));dec(BufLen);
     while BufLen > 0 do begin
     Result:=Result xor Word(Buf^);
     inc(Integer(Buf));dec(BufLen);
     inc(Integer(Buf));dec(BufLen);
     end;
end;

//Расчет контрольной суммы flash-tokens (последний байт - HC - Header Checksum)
function  FlashTokenCRC(Buf:Pointer; BufLen: integer): byte;
begin
     Result:=0;
     while BufLen > 0 do begin
     Result:=Result+Byte(Buf^);
     inc(Integer(Buf));
     dec(BufLen);
     end;
     Result:=Result xor $FF;
end;
end.
