Source file: /~heha/hsn/esptool.zip/image.cpp

#include "image.h"
#include "info.h"	// esp::xor8

ESPFirmwareImage::ESPFirmwareImage(char chip,const char*filename) {
 memset(this,0,sizeof*this);
 hdr.magic = MAGIC;
 m_chip = chip;
 if (filename) load(filename);
}

const ESPFirmwareImage::SEGHDR*ESPFirmwareImage::IMGHDR::next(bool kurz) const {
 return reinterpret_cast<const SEGHDR*>(kurz||wp_pin!=0xEE?&wp_pin:(const byte*)(this+1));
}

// Lader
void ESPFirmwareImage::load(const char*filename) {
 FILE*f = fopen(filename,"rb");
 if (!f) Fatal("Cannot open %s for reading",filename);
 load(f);
 fclose(f);
}

// Checks header only, not the checksum and not the length
bool ESPFirmwareImage::valid(const void*fdata,size_t flen) {
 const IMGHDR&h = *reinterpret_cast<const IMGHDR*>(fdata);
 return flen>=sizeof h && h.magic==MAGIC && h.nseg && h.nseg<=16 && h.fmode<4;
}

char ESPFirmwareImage::IMGHDR::detectchip() const{
 if (wp_pin!=0xEE) return 66;	// kann RAM-Image sein!
 if (chipH || chipL==32 || chipL==66) return 0;
 return chipL?chipL:32;
}

void ESPFirmwareImage::checkchip() {
 char chip=hdr.detectchip();
 const esp::Info1*info1=esp::find1(chip);
 if (chip) {
  if (info1) {
   printf("Chip ESP%s encoded in image",info1->name,chip,chip==m_chip?"as expected.":"Unexpected!");
   printf(m_chip?chip==m_chip?" as expected.":chip==66?", sure?":": Unexpected!":".");
   putchar('\n');
   if (chip!=66 || !m_chip)
	m_chip=chip;	// Für nachfolgende Dekodierung setzen
  }else printf("Unknown chip (%d) encoded in image!\n",chip);
 }else printf("Unknown chip encoded in image!\n");
}

void ESPFirmwareImage::load(FILE*f) {
 size_t hdrlen = m_chip==66 ? sizeof(IMGHDR) : sizeof hdr;
 memset(&hdr,0,sizeof hdr);
// some sanity check
 if (fread(&hdr,1,hdrlen,f)!=hdrlen
  || hdr.magic!=MAGIC
  || !hdr.nseg
  || hdr.nseg > 16) Fatal("Invalid firmware image");
// Was für ein Bullshit:
// "After the 8-byte header comes the extended header for chips others than ESP8266."
// Aber woher den Chiptyp kennen?? Von der Kommandozeile? Dankeschön!!
 if (m_chip!=66 && hdr.wp_pin!=0xEE) {	// Kurzer Header für ESP8266 - oder für RAM-Images!
  fseek(f,-16,1);
//  m_chip=66;	// must be 8266
  hdrlen=sizeof(IMGHDR);
 }
 checkchip();
 Sha256 sha;
 sha.update(&hdr.magic,hdrlen);

 segments = new SEG[hdr.nseg];
 for (int i=0; i<hdr.nseg; i++) {
  SEG&cur = segments[i];
  if (fread(&cur,1,8,f)!=8
//   || cur.offset > 0x40200000
//   || cur.offset < 0x3ffe0000
   || cur.size > 1024*1024)
	Fatal("Suspicious segment #%u@0x%XL%u",i,cur.offset,cur.size);
  size_t blocksize=(cur.size+3)&~3;
  cur.data=new byte[blocksize];
  size_t br=fread(cur.data,1,blocksize,f);
  if (br!=blocksize)
	Fatal("End of file reading segment #%u@0x%XL%u (actual length %u)",i,cur.offset,blocksize,br);
  sha.update((byte*)&cur,8);
  sha.update(cur.data,cur.size);
 }
 byte padding[16];
// Skip the padding. The checksum is stored in the last byte so that the
// file is a multiple of 16 bytes.
 size_t padlen = 16 - ftell(f)%16U;
 size_t br = fread(padding,1,padlen,f);
 check_avail=!!br;
 if (!br) printf("No CRC. Looks like a RAM image.\n");
 else{
  if (br!=padlen) Fatal("Missing CRC");
  sha.update(padding,br);
 }
 calchash = sha.final();
 check=padding[padlen-1];
// Just after the CRC byte, a 32-byte SHA256 hash follows (ESP32+ only)
 if (hdr.hash_append==1 && fread(&filehash,1,32,f)!=32) Fatal("Missing SHA-256 hash");
 if (fread(&padlen,1,sizeof padlen,f)) Fatal("Extra data after end of image");
}

// Parsen mit segmentweisem Callback. Beim Laden aus Ressource (stets 3 Segmente) ist flen==0
// Die Startadresse wird früh gesetzt, d.h. auch bei return false verändert.
bool ESPFirmwareImage::parse(const Dumpfile&fd,cb2_t cb,void*cbd,uint32*sadr) {
 const IMGHDR&h = *reinterpret_cast<const IMGHDR*>(fd.data());
 if (sadr) *sadr=h.entry;
 const SEGHDR*sh = h.next();
 for (int i=0; i<h.nseg; i++) {
  Dumpfile sub(const_cast<byte*>(sh->data()),sh->size,sh->offset);
  if (!cb(cbd,sub)) return false;
  sh=sh->next();
 }
 return true;
}

// While reading flash, check whether it's a firmware image,
// and, if so, return a reduced <fsize> value.
// When sure it is NOT a firmware image, return 0 to indicate failure.
// Otherwise, this function returns <fsize> unchanged, i.e. continue reading
uint32 ESPFirmwareImage::detectSize(const void*fdata,uint32 fsize) {
 if (!valid(fdata,fsize)) return 0;	// kein Firmware-Image
 const IMGHDR&h=*reinterpret_cast<const IMGHDR*>(fdata);
 if (fsize<sizeof h) return 1;	// unsicher
 const SEGHDR*sh=h.next();
 uint32 o=uint32(size_t(sh)-size_t(fdata));
 for (unsigned i=0; i<h.nseg; i++) {
  if (fsize < o+8) return 1;	// unvollständig, kann next() nicht ausführen
  sh=sh->next();
  o=uint32(size_t(sh)-size_t(fdata));	// In der letzten Runde nicht testen!
 }
// Checksum must be present here! No idea how to handle other case.
 o=(o+16)&~15;	// align to next 16-byte boundary
 if (h.wp_pin==0xEE && h.hash_append==1) o+=32;
 return o<=fsize ? o : 1;
}


static const char*nstr(const char*s,unsigned n) {
 for(;n;n--) s+=strlen(s)+1;
 return s;
}

const char*ESPFirmwareImage::fmode_str() const{
 const char*modes="QIO\0QOUT\0DIO\0DOUT";
 return nstr(modes,hdr.fmode&3);
}

String<8>ESPFirmwareImage::ffreq_str() const{
 MAKEPRINTBUF(8)
 static const byte
  f   [4]={40,26,20,80},
  f_c2[4]={30,20,15,60},
  f_c6[4]={80,40,20},
  f_h2[4]={24,16,12,48};
 const byte*pf=f;		// ESP8266 und alle übrigen ESP32
 switch (m_chip) {
  case 12: pf=f_c2; break;	// ESP32C2e0, ESP32C2e1
  case  7:			// ESP32C6b
  case 13: pf=f_c6; break;	// ESP32C6
  case 10:			// ESP32H2b1
  case 14:			// ESP32H2b2
  case 16: pf=f_h2; break;	// ESP32H2
 }
 print("%u MHz",pf[hdr.ffreq&3]);
 return buf;
}

String<16>ESPFirmwareImage::fsize_str() const{
 MAKEPRINTBUF(16)
 byte b=hdr.fsize;
 const char*suffix="";
 if (m_chip==66) {
  if (b<2) print("%u KByte",1<<(9-b));
  else{
   if (byte(b-5)<2) 
   b-=2;
   if (b>=3) {
    b-=2;
    if (b>3) --b;
    else suffix="-c1";
   }
  }
 }
 print("%u MByte%s",1<<b,suffix);
 return buf;
}

// <data> wird „wegkopiert“ und nicht weiter referenziert
bool ESPFirmwareImage::add_segment(const Dumpfile&df) {
 if (!df.size) return true;
 if (df.size>65536) Fatal("add_segment: Zu große Datenlänge (%u)",df.size);
// Data should be aligned on word boundary
 int nseg = hdr.nseg;
 if (nseg>=16) Fatal("add_segment: Zu viele Segmente");
 segments = (SEG*)realloc(segments,++hdr.nseg*sizeof*segments);
 SEG&cur=segments[nseg];	// Neu erstelltes Segment
 uint32 alen = (df.size+3)&~3;	// auf DWORD aufrunden
 cur.offset = df.addr;
 cur.size = alen;		// aufgerundet ablegen
 cur.data = new byte[alen];
 memcpy(cur.data,df.data(),df.size);
 memset(cur.data+df.size,0,alen-df.size);	// Mit 0 auffüllen, falls alen>dlen
 return true;
}

// Speichert die per add_segment() abgelegten Speicherblöcke in eine Datei
// und generiert die Prüfsumme
void ESPFirmwareImage::save(const char*filename) const{
 FILE*f = fopen(filename,"wb");
 if (!f) Fatal("Cannot create file %s",filename);
 fwrite(&hdr,1,sizeof hdr,f);

 byte ck = esp::XOR8_INIT;
 for (int i=0; i<hdr.nseg; i++) {
  SEG&cur=segments[i];
  fwrite(&cur,1,8,f);
  fwrite(cur.data,1,cur.size,f);
  ck = esp::xor8(cur.data,cur.size,ck);
 }
 int align = 15 - ftell(f)%16U;
 fseek(f,align,1);
 fwrite(&ck,1,1,f);
 fclose(f);
}

ESPFirmwareImage::~ESPFirmwareImage() {
 if (!segments) return;
 for (int i=0; i<hdr.nseg; i++) {
  delete[] segments[i].data;
 }
 delete[] segments;
 segments=0;
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded