
struct ExifReader {
	String app1;
	bool   le;
	
	const byte *Begin() const   { return (const byte *)~app1 + 6; }
	const byte *End() const     { return (const byte *)app1.End(); }
	operator bool() const       { return app1.GetCount(); }
	
	uint32 Peek32(const byte *ptr) const { return (le ? Peek32le : Peek32be)(ptr); }
	uint16 Peek16(const byte *ptr) const { return (le ? Peek16le : Peek16be)(ptr); }
	
	String GetThumbnail() const;
	
	ExifReader(const String& app1);
};

ExifReader::ExifReader(const String& q)
{
	app1 = q;
	if(app1.GetCount() > 30 && memcmp(~app1, "ExifReader", 4) == 0)
		if(app1[6] == 'I')
			le = true;
		else
		if(app1[6] == 'M')
			le = false;
		else
			app1.Clear();
	else
		app1.Clear();
}

String ExifReader::GetThumbnail() const
{
	if(!app1.GetCount())
		return Null;
	const byte *begin = Begin();
	const byte *end = End();
	const byte *p = begin + Peek32(begin + 4);
	if(p + 2 >= end) return Null;
	p += Peek16(p) * 12 + 2;
	if(p + 4 >= end) return Null;
	p = begin + Peek32(p);
	if(p <= begin || p + 2 >= end) return Null;
	word count = Peek16(p);
	p += 2;
	dword offset = 0;
	dword len = 0;
	for(int n = 0; n < count; n++) {
		if(p + 12 >= end) Null;
		if(Peek32(p + 4) == 1) {
			dword val = 0;
			switch(Peek16(p + 2)) {
			case 4:
			case 9:
				val = Peek32(p + 8);
				break;
			case 3:
			case 8:
				val = Peek16(p + 8);
				break;
			}
			if(val)
				switch(Peek16(p)) {
				case 0x201:
					offset = val;
					break;
				case 0x202:
					len = val;
					break;
				}
		}
		p += 12;
	}
	return offset && len && begin + offset + len < end ? String(begin + offset, len) : String();
}
