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.