#include #include #include #include #include // for ftruncate and fileno using namespace std; #define IPS_ADDRESS_EXTERN 0x01 #define IPS_ADDRESS_GLOBAL 0x02 static bool optimize = false; static size_t Improvize(char* buf, size_t n, size_t at) { static bool warned = false; if(!warned) { fprintf(stderr, "Warning: Output file will be larger than original.\n"); warned = true; } // Instead of filling the content with zero, fill with a 01234..pattern. // This helps make BPS patch creation a lot faster than without. if(optimize) for(size_t a=0; a 1 && argv[1][0]=='-' && argv[1][1] == 'O') { optimize = true; for(int p=1; p 0) ++patchpos; size_t plainlen = patchpos-oldpos; /*fprintf(stderr, "patchpos: %lX @ %lX,%lX (plain %lX); old(%lX)new(%lX)\n", patchpos, inpos,oldpos, plainlen, oldsize, newsize);*/ while(plainlen > 0) { char tmp[4096]; size_t bytes = plainlen; if(bytes > sizeof(tmp)) bytes = sizeof(tmp); size_t c = fread(tmp, 1, bytes, original); if(c <= 0 && ferror(original)) goto inferr; if(!c) c = Improvize(tmp, bytes, ftell(resultfile)); size_t c2 = fwrite(tmp, 1, c, resultfile); if(c2 <= 0 && ferror(resultfile)) goto outferr; if(c2 != c) goto outfeof; plainlen -= c; } oldpos = ftell(resultfile); while(oldpos < newsize) { int c = fgetc(fp); if(c == 0) break; { int e = fgetc(original); if(e != EOF) c ^= e; } fputc(c, resultfile); ++oldpos; } if(oldpos == newsize) { fseek(fp, -12, SEEK_END); break; } } fflush(resultfile); ftruncate(fileno(resultfile), newsize); goto Created; } setbuf(fp, NULL); setbuf(original, NULL); setbuf(resultfile, NULL); fseek(fp,5,SEEK_SET); rewind(original); rewind(resultfile); if(strncmp((const char *)Buf, "PATCH", 5)) { fprintf(stderr, "This isn't a patch!\n"); arf:fclose(fp); goto arf2; inferr: perror("fread"); goto arf2; outferr: perror("fwrite"); goto arf2; outfeof: fprintf(stderr, "Error writing to resultfile\n"); goto arf2; arf2: fclose(original); fclose(resultfile); return -1; } {multimap > lumps; {size_t col=0; for(;;) { bool rle=false; size_t wanted, c = fread(Buf, 1, 3, fp); if(c <= 0 && ferror(fp)) { ipserr: perror("fread"); goto arf; } if(c < (wanted=3)) { ipseof: fprintf(stderr, "Unexpected end of file (%s) - wanted %d, got %d\n", patchfn, (int)wanted, (int)c); goto arf; } if(!strncmp((const char *)Buf, "EOF", 3))break; //fprintf(stderr, "%ld %02X %02X %02X %ld\n", (long)c, Buf[0],Buf[1],Buf[2], (long)ftell(fp)); size_t pos = (((size_t)Buf[0]) << 16) |(((size_t)Buf[1]) << 8) | ((size_t)Buf[2]); c = fread(Buf, 1, 2, fp); if(c <= 0 && ferror(fp)) { fprintf(stderr, "Got pos %lX\n", (unsigned long)pos); goto ipserr; } if(c < (wanted=2)) { goto ipseof; } size_t len = (((size_t)Buf[0]) << 8) | ((size_t)Buf[1]); if(len==0) { rle=true; c = fread(Buf, 1, 2, fp); if(c <= 0 && ferror(fp)) { fprintf(stderr, "Got pos %lX\n", (unsigned long)pos); goto ipserr; } if(c < (wanted=2)) { goto ipseof; } len = (((size_t)Buf[0]) << 8) | ((size_t)Buf[1]); fprintf(stderr, "%06lX <= %-5lu ", (unsigned long)pos, (unsigned long)len); } else fprintf(stderr, "%06lX <- %-5lu ", (unsigned long)pos, (unsigned long)len); if(++col == 5) { fprintf(stderr, "\n"); col=0; } vector Buf2(len); if(rle) { c = fread(&Buf2[0], 1, 1, fp); if(c <= 0 && ferror(fp)) { goto ipserr; } if(c != (wanted=1)) { goto ipseof; } for(size_t c=1; c > (pos, Buf2)); } } if(col) fprintf(stderr, "\n"); }fclose(fp); size_t curpos=0; char tmp[4096]; multimap >::const_iterator i; for(i=lumps.begin(); i!=lumps.end(); ++i) { size_t newpos = i->first; if(newpos < curpos) { fprintf(stderr, "Malformed patch (overlapping chunks at %lX)!\n", (unsigned long)newpos); goto arf2; } size_t plainlen = newpos-curpos; while(plainlen > 0) { size_t bytes = plainlen; if(bytes > sizeof(tmp)) bytes = sizeof(tmp); size_t c = fread(tmp, 1, bytes, original); if(c <= 0 && ferror(original)) goto inferr; if(!c) c = Improvize(tmp, bytes, ftell(resultfile)); size_t c2 = fwrite(tmp, 1, c, resultfile); if(c2 <= 0 && ferror(resultfile)) goto outferr; if(c2 != c) goto outfeof; plainlen -= c; } size_t patchlen = i->second.size(); size_t skiplen = patchlen; while(skiplen > 0) // original is not necessarily seekable, thus just read it { size_t bytes = skiplen; if(bytes > sizeof(tmp)) bytes = sizeof(tmp); size_t c = fread(tmp, 1, bytes, original); if(c <= 0 && ferror(original)) goto inferr; if(!c) c = Improvize(tmp, bytes, ftell(resultfile)); skiplen -= c; } size_t c = fwrite(&i->second[0], 1, patchlen, resultfile); if(c <= 0 && ferror(resultfile)) goto outferr; if(c != patchlen)goto outfeof; curpos = newpos + patchlen; } while(!feof(original)) { size_t c = fread(tmp, 1, sizeof tmp, original); if(c <= 0 && ferror(original)) goto inferr; if(!c) break; size_t c2 = fwrite(tmp, 1, c, resultfile); if(c2 <= 0 && ferror(resultfile)) goto outferr; if(c2 != c) goto outfeof; }} Created: fprintf(stderr, "%s created successfully. %s not modified.\n", resfn, origfn); fclose(original); fclose(resultfile); return 0; }