// chewfat - a FAT12 filesystem reader // // This program reads an .ISO file containing a FAT12 filesystem dumped from // the Dakota Digital Camera's flash memory, and outputs all available picture // files. Raw memory dumps are in an interleaved/scrambled format due to the // flash memory wear-leveling algorithm in use - use John Maushammer's flashdump2iso // to unscramble to .ISO. // // If the filesystem does not begin at the beginning of the .ISO, chewfat will scan for it. // // Usage: chewfat // // Output: Picture files (DSC_????.JPG) to the default directory. On most systems // this should be the directory you are in when the program is run. // // WARNING: Output will overwrite any existing picture file with the same name! // Move them out of the default directory if you want to keep them! // // Additional warnings: This program has not been idiot-proofed against bad filenames, full disks, // corrupted/short input files, and the like; results in these cases will be unpredictable // and probably not good. This program makes assumptions about the directory structure // and cluster lengths that could be subject to change in future camera revisions. // This is a crudely hacked-together program that has not been extensively tested // and is not guaranteed to work properly, if at all. No lifeguard on duty. // // ------------ // Copyright 2003 T. R. Gipson // sysadmin at http://cexx.org/ // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #pragma hdrstop #include //#include #include #include #include //--------------------------------------------------------------------------- #pragma argsused FILE *in; FILE *out; int clustlen; int FATIsAt; int RootIsAt; int DataIsAt; unsigned char FATBuf[1536]; int readword(char* buf, long int pos) { unsigned short word=*((WORD*) &buf[pos]); return word; } int nextcluster (int StartingCluster) { unsigned int nextcluster; unsigned int index=3*StartingCluster/2; unsigned short shorty=*((WORD*) &FATBuf[index]); if (StartingCluster%2 == 0) /* Even */ { nextcluster=shorty & 0x0fff; } else /* Odd */ { nextcluster=(shorty >> 4); } return nextcluster; } void getfile(long int CurrDirPos) { string temp=""; fseek(in, CurrDirPos, SEEK_SET); char buf[32]; fread(buf, 1, 32, in); /* Get filename */ for(int k=0; k<11; k++) { if (buf[k] != 0x20) /* Stop at spaces */ temp+=buf[k]; if (k==7) temp+="."; } /* Get file length */ int filesize= *(unsigned int*)(&buf[28]); cout << "Grabbing file " << temp << "\n"; cout << "File size: " << filesize << "\n"; out=fopen(temp.c_str(),"wb"); int StartingCluster=readword(buf,26); //cout << "Starting cluster: " << StartingCluster << "\n"; int k=0; int next_cluster=0; char filebuf[16384]; /* clustlen=16384; damn constant expression thingy */ while (StartingCluster<0xff0) /* Break it off if bad/reserved cluster or end of chain */ { next_cluster=nextcluster(StartingCluster); int readfrom=DataIsAt+((StartingCluster-2)*clustlen); //cout << "Reading cluster starting at " << readfrom << "\n"; fseek(in, readfrom, SEEK_SET); fread(filebuf, 1, clustlen, in); if (next_cluster<0xff0) { fwrite(filebuf, 1, clustlen, out); } else { fwrite(filebuf, 1, filesize%clustlen, out); } StartingCluster=next_cluster; k++; } fclose(out); } string GetCWD(long int CurrDirPos) { string temp=""; fseek(in, CurrDirPos, SEEK_SET); char buf[32]; fread(buf, 1, 32, in); for(int k=0; k<11; k++) { if (buf[k] != 0x20) /* Stop at spaces */ temp+=buf[k]; } return temp; } long int FollowDir(long int CurrDirPos) { fseek(in, CurrDirPos, SEEK_SET); char buf[32]; fread(buf, 1, 32, in); int StartingCluster=readword(buf,26); //cout << "Starting cluster: " << StartingCluster << "\n"; /* Laziness: Assuming directories don't exceed 1 cluster */ CurrDirPos=DataIsAt+((StartingCluster-2)*clustlen); return CurrDirPos; } int main(int argc, char* argv[]) { //string isoname="c:\\MY2.ISO"; const int sectsize=512; /* Pick a small safe value, 2^something */ char sectbuf[sectsize]; int fsfound=0; cout << "chewfat (c) 2003 T. R. Gipson. http://cexx.org/dakota\n\n"; if ((in=fopen(argv[1], "rb")) == NULL) //isoname.c_str() { cout << "Could not open input file." << "\n\n"; cout << "Usage: chewfat \nExample: chewfat C:\\camera\\flash-image.iso\n"; exit(1); } rewind(in); /* Find start of FAT12 filesystem */ cout << "Searching for start of FAT12 filesystem..."; while(!(fsfound)) { fread(sectbuf, 1, sectsize, in); string temp=""; for (int k=54; k<59;k++) { temp+=sectbuf[k]; /* Crude kludge #973192 */ } if (temp == "FAT12") { fsfound=TRUE; cout << "found it.\n\n"; } } cout << "Found what appears to be a valid FAT12 FS at file position " << (ftell(in)-sectsize) << "\n"; /* FS found, now read BPB info */ /* Is it legal to define vars any old place? Can't remember... maybe only in CPP? */ int BytesPerSector=readword(sectbuf,11); int SectorsPerCluster=sectbuf[13]; int ReservedSectors=readword(sectbuf,14); int TotalFATs=sectbuf[16]; int MaxRootEntries=readword(sectbuf,17); int SectorsPerFAT=readword(sectbuf,22); cout << "Bytes per sector: " << BytesPerSector << "\nSectors per cluster: " << SectorsPerCluster << "\nReserved Sectors: " << ReservedSectors << "\nTotal FATs: " << TotalFATs << "\nMax Root Entries: " << MaxRootEntries << "\nSectors Per FAT: " << SectorsPerFAT << "\n\n"; clustlen=BytesPerSector*SectorsPerCluster; FATIsAt=(ftell(in)-sectsize) + (ReservedSectors*BytesPerSector); RootIsAt=FATIsAt+(TotalFATs*SectorsPerFAT*BytesPerSector); DataIsAt=RootIsAt+(MaxRootEntries*32); cout << "FAT is at " << FATIsAt << "\nRoot is at " << RootIsAt << "\nData is at " << DataIsAt << "\n\n"; /* Got BPB info, now read FAT to a buffer */ fseek(in, FATIsAt+1536, SEEK_SET); fread(FATBuf, 1, BytesPerSector*SectorsPerFAT, in); /* We know where the image files are in the directory structure, so just drill down to them now. */ /* A proper app would let you browse thru the directories yourself, but nobody said this was a proper app :) */ long int CurrDirPos=RootIsAt; /* Drill until we get to a dir. with picture files */ string temp=""; while (1) { temp=GetCWD(CurrDirPos); if (temp=="DCIM" || temp=="100MEDIA") { CurrDirPos=FollowDir(CurrDirPos); } else if (temp.substr(0,3)=="DSC") { break; } //cout << "Directory: " << temp << "\n"; CurrDirPos+=32; } while (GetCWD(CurrDirPos).substr(8,3) == "JPG") { if (GetCWD(CurrDirPos).substr(0,1) != "å") /* Skip deleted files */ { //cout << GetCWD(CurrDirPos) << "\n"; getfile(CurrDirPos); } CurrDirPos+=32; } //getch(); fclose(in); return 0; }