File is literally filled with JPEGs.

App works really simple:
- Any JPEG starts with FF D8 FF byte sequence
- JPEG does not care about file size being precise as long as not too small
- JPEG ends with FF D9, most decoders (viewers) simply stop decoding at that point
So app scans for FF D8 FF > opens new file and copies all data from that byte sequence to new file without worrying about FF D9 marker. This is the dumb way to do this.
IOW the source file:
... FF D8 FF ... FF D9 .. FF D8 FF .. FF D9 .. etc. .. END
Whenever you encounter byte sequence FF D8 FF you copy that AND everything that follows to new file. This is dumb way.
Smart way:
We need to learn about JPEG:
JPEG file is organized around so called markers. A marker is FF nn. EVERY time a JPEG decoder sees FF nn (nn = some value) it will think, this is a JPEG marker. Here's a list of possible markers that I maintain on my website.
JPEG start with FF D8 and then is immediate followed by first marker, FF nn. Next two bytes are size of this section. So offset + size gets us next marker. This is true for all markers except FF D8 (start file), FF DA (start image data) and FF D9 (end of image data).
So:
FF D8 - FF nn xx xx - FF nn xx xx - FF DA .......... JPEG image data ....... FF D9.
So smart way to carve JPEG is follow chain of markers starting at FF D8 till we find FF DA, then everything is JPEG data until we encounter FF D9.
If we keep in mind that each time a JPEG decoder sees FF nn it will assume it's a JPEG marker we can explain how we can sometimes repair half grey JPEG files: If we have FE 89 as some bytes in JPEG data and one single bit flips we could have FF 89 all of a sudden. Most JPEG decoders will choke as they can not determine what marker FF 89 is supposed to do. If we change the byte combo to anything NOT 'FF nn' decoder will happily continue although the image may look distorted.