diff --git a/Makefile b/Makefile index ceb6250..d0df480 100644 --- a/Makefile +++ b/Makefile @@ -3,19 +3,20 @@ CFLAGS_windows := -DFLIPS_WINDOWS -mwindows -lgdi32 -lcomdlg32 -lcomctl32 -luser CFLAGS_win := $(CFLAGS_windows) CFLAGS_cli := -DFLIPS_CLI -CFLAGS_G = -fno-rtti -fno-exceptions +CFLAGS_G = -fno-rtti -fno-exceptions -DNDEBUG FNAME_gtk := flips FNAME_windows := flips.exe -FNAME_win := $(FNAME_windows) FNAME_cli := flips CXX = g++ XFILES := +SOURCES := *.cpp + ifeq ($(TARGET),) - targetmachine := $(shell $(CC) -dumpmachine) + targetmachine := $(shell $(CXX) -dumpmachine) ifneq ($(findstring mingw,$(targetmachine)),) TARGET := windows else ifneq ($(findstring linux,$(targetmachine)),) @@ -48,7 +49,7 @@ ifeq ($(TARGET),gtk) endif ifeq ($(GTKFLAGS),) $(warning pkg-config can't find gtk+-3.0, or pkg-config itself can't be found) - $(warning if you have the needed files installed, specify their locations and names with `make 'GTKFLAGS=-I/usr/include' 'GTKLIBS=-L/usr/lib -lgtk'') + $(warning if you have the needed files installed, specify their locations and names with `make GTKFLAGS='-I/usr/include' GTKLIBS='-L/usr/lib -lgtk'') $(warning if not, the package name under Debian and derivates is `libgtk-3-dev'; on other distros, consult a search engine) $(warning switching to CLI build) TARGET := cli @@ -71,20 +72,21 @@ ifneq ($(DIVSUF),no) DIVSUF := $(subst /include/config.h,,$(DIVSUF_EXIST)) ifneq ($(DIVSUF),) DIVSUF_SOURCES := $(filter-out %/utils.c,$(wildcard $(DIVSUF)/lib/*.c)) - ifeq ($(DIVSUF_OPENMP),) - ifeq ($(TARGET),gtk) - DIVSUF_OPENMP := -fopenmp - endif - endif - DIVSUF_CFLAGS := -DUSE_DIVSUFSORT -I$(DIVSUF)/include -DHAVE_CONFIG_H -DNDEBUG -D__STDC_FORMAT_MACROS $(DIVSUF_OPENMP) + DIVSUF_CFLAGS := -I$(DIVSUF)/include -DHAVE_CONFIG_H -D__STDC_FORMAT_MACROS DIVSUF_LFLAGS := MOREFLAGS += $(DIVSUF_CFLAGS) $(DIVSUF_SOURCES) $(DIVSUF_LFLAGS) else - $(warning no libdivsufsort-2.x detected; switching to fallback SA-IS) - $(warning libdivsufsort is approximately twice as fast as SA-IS) + $(warning no libdivsufsort-2.x detected; switching to fallback libdivsufsort-lite) + $(warning libdivsufsort is approximately 3% faster than lite) + SOURCES += divsufsort.c endif +else + SOURCES += divsufsort.c endif +ifeq ($(TARGET),gtk) + CFLAGS_G += -fopenmp +endif -$(FNAME_$(TARGET)): *.cpp $(XFILES) +$(FNAME_$(TARGET)): $(SOURCES) $(XFILES) $(CXX) $^ -std=c++98 $(CFLAGS) $(LFLAGS) $(CFLAGS_G) $(MOREFLAGS) $(XFILES) -o$@ diff --git a/flips-gtk.cpp b/flips-gtk.cpp index d5505d3..c66d9ee 100644 --- a/flips-gtk.cpp +++ b/flips-gtk.cpp @@ -784,27 +784,19 @@ void a_SetEmulator(GtkButton* widget, gpointer user_data) gtk_widget_destroy(dialog); } -int ShowGUI(const char * filename) + +gchar * get_cfgpath() { - if (!canShowGUI) - { - g_warning("couldn't parse command line arguments, fix them or use command line"); - usage(); - } + static gchar * cfgpath=NULL; + if (!cfgpath) cfgpath=g_strconcat(g_get_user_config_dir(), "/flipscfg", NULL); + return cfgpath; +} + +void GUILoadConfig() +{ + if (!canShowGUI) return; - GdkDisplay* display=gdk_display_open(gdk_get_display_arg_name()); - if (!display) display=gdk_display_get_default(); - if (!display) - { - g_warning("couldn't connect to display, fix it or use command line"); - usage(); - } - gdk_display_manager_set_default_display(gdk_display_manager_get(), display); - - const gchar * cfgdir=g_get_user_config_dir(); - gchar * cfgpath=g_strndup(cfgdir, strlen(cfgdir)+strlen("/flipscfg")+1); - strcat(cfgpath, "/flipscfg"); - struct mem cfgin = file::read(cfgpath); + struct mem cfgin = file::read(get_cfgpath()); if (cfgin.len>=10+1+1+1+1+4+4 && !memcmp(cfgin.ptr, "FlipscfgG", 9) && cfgin.ptr[9]==cfgversion) { state.lastPatchType=cfgin.ptr[10]; @@ -836,6 +828,26 @@ int ShowGUI(const char * filename) state.lastPatchType=ty_bps; } free(cfgin.ptr); +} + +int GUIShow(const char * filename) +{ + if (!canShowGUI) + { + g_warning("couldn't parse command line arguments, fix them or use command line"); + usage(); + } + + GdkDisplay* display=gdk_display_open(gdk_get_display_arg_name()); + if (!display) display=gdk_display_get_default(); + if (!display) + { + g_warning("couldn't connect to display, fix it or use command line"); + usage(); + } + gdk_display_manager_set_default_display(gdk_display_manager_get(), display); + + GUILoadConfig(); if (filename) { @@ -891,7 +903,7 @@ int ShowGUI(const char * filename) cfgout.ptr[21]=romlist.len>>0; memcpy(cfgout.ptr+22, state.emulator, emulen); memcpy(cfgout.ptr+22+emulen, romlist.ptr, romlist.len); - filewrite::write(cfgpath, cfgout); + filewrite::write(get_cfgpath(), cfgout); return 0; } diff --git a/flips-w32.cpp b/flips-w32.cpp index 30a567e..cef6cac 100644 --- a/flips-w32.cpp +++ b/flips-w32.cpp @@ -863,7 +863,7 @@ int ShowMainWindow(HINSTANCE hInstance, int nCmdShow) return msg.wParam; } -void ClaimConsole() +void GUIClaimConsole() { //this one makes it act like a console app in all cases except it doesn't create a new console if // not launched from one (it'd swiftly go away on app exit anyways), and it doesn't like being @@ -884,16 +884,20 @@ void ClaimConsole() HINSTANCE hInstance_; int nCmdShow_; -int ShowGUI(LPCWSTR filename) +WCHAR * get_cfgpath() { - WCHAR cfgfname[MAX_PATH+8]; + static WCHAR cfgfname[MAX_PATH+8]; GetModuleFileNameW(NULL, cfgfname, MAX_PATH); WCHAR * ext=GetExtension(cfgfname); if (ext) *ext='\0'; wcscat(cfgfname, TEXT("cfg.bin")); - + return cfgfname; +} + +void GUILoadConfig() +{ memset(&state, 0, sizeof(state)); - struct mem configbin=ReadWholeFile(cfgfname); + struct mem configbin=ReadWholeFile(get_cfgpath()); void* configbin_org=configbin.ptr; if (configbin.len >= sizeof(state)) { @@ -925,6 +929,11 @@ int ShowGUI(LPCWSTR filename) set_st_emulator(TEXT("")); } free(configbin_org); +} + +int GUIShow(LPCWSTR filename) +{ + GUILoadConfig(); INITCOMMONCONTROLSEX initctrls; initctrls.dwSize=sizeof(initctrls); @@ -939,7 +948,7 @@ int ShowGUI(LPCWSTR filename) } else ret=ShowMainWindow(hInstance_, nCmdShow_); - HANDLE file=CreateFile(cfgfname, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL); + HANDLE file=CreateFile(get_cfgpath(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL); if (file!=INVALID_HANDLE_VALUE) { DWORD whocares; diff --git a/flips.cpp b/flips.cpp index 54839c3..59f2e7c 100644 --- a/flips.cpp +++ b/flips.cpp @@ -136,6 +136,7 @@ const struct errorinfo bpserrors[]={ { el_notthis, "That's the output file already." },//bps_to_output { el_notthis, "This patch is not intended for this ROM." },//bps_not_this { el_broken, "This patch is broken and can't be used." },//bps_broken + { el_broken, "Couldn't read input patch. What exactly are you doing?" },//bps_io { el_warning, "The files are identical! The patch will do nothing." },//bps_identical { el_broken, "These files are too big for this program to handle." },//bps_too_big @@ -311,10 +312,10 @@ LPCWSTR FindRomForPatch(file* patch, bool * possibleToFind) enum patchtype patchtype=IdentifyPatch(patch); if (patchtype==ty_bps) { - uint32_t crc; - if (bps_get_checksums(patch, &crc, NULL, NULL)!=bps_ok) return NULL; + struct bpsinfo info = bps_get_info(patch, false); + if (info.error) return NULL; if (possibleToFind) *possibleToFind=true; - return FindRomForSum(ch_crc32, &crc); + return FindRomForSum(ch_crc32, &info.crc_in); } //UPS has checksums too, but screw UPS. Nobody cares. return NULL; @@ -325,9 +326,9 @@ void AddToRomList(file* patch, LPCWSTR path) enum patchtype patchtype=IdentifyPatch(patch); if (patchtype==ty_bps) { - uint32_t crc; - if (bps_get_checksums(patch, &crc, NULL, NULL)!=bps_ok) return; - AddRomForSum(ch_crc32, &crc, path); + struct bpsinfo info = bps_get_info(patch, false); + if (info.error) return; + AddRomForSum(ch_crc32, &info.crc_in, path); } } @@ -586,11 +587,59 @@ struct errorinfo CreatePatch(LPCWSTR inromname, LPCWSTR outromname, enum patchty return errinf; } +int patchinfo(LPCWSTR patchname) +{ + GUIClaimConsole(); + + file* patch = file::create(patchname); + if (!patch) + { + puts("Couldn't read file"); + return el_broken; + } + + enum patchtype patchtype=IdentifyPatch(patch); + if (patchtype==ty_bps) + { + struct bpsinfo info = bps_get_info(patch, false); + if (info.error) + { + puts(bpserrors[info.error].description); + return bpserrors[info.error].level; + } + +#ifndef FLIPS_CLI + GUILoadConfig(); + LPCWSTR inromname = FindRomForPatch(patch, NULL); +#else + LPCWSTR inromname = NULL; +#endif +#ifdef FLIPS_WINDOWS +#define z "I" +#else +#define z "z" +#endif + printf("Input ROM: %" z "u bytes, CRC32 %.8X", info.size_in, info.crc_in); + if (inromname) wprintf(TEXT(", %s"), inromname); + puts(""); + + printf("Output ROM: %" z "u bytes, CRC32 %.8X\n", info.size_out, info.crc_out); + //floating point may lose a little precision, but it's easier than dodging overflows, and this + //is the output of inaccurate heuristics anyways, losing a little more makes no difference. + //Windows MulDiv could also work, but it's kinda nonportable. + //printf("Change index: %i / 1000\n", (int)(info.change_num / (float)info.change_denom * 1000)); + + return 0; + } + puts("No information available for this patch type"); + return el_broken; +} + void usage() { - ClaimConsole(); + GUIClaimConsole(); puts( // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 "usage:\n" @@ -601,7 +650,7 @@ void usage() "or " #endif "flips [--apply] [--exact] patch.bps rom.smc [outrom.smc]\n" - "or flips [--create] [--exact] [--ips | --bps | --bps-delta] clean.smc\n" + "or flips [--create] [--exact] [--bps | --bps-linear | --ips] clean.smc\n" " hack.smc [patch.bps]\n" #ifndef FLIPS_CLI "(for scripting, only the latter two are sensible)\n" @@ -612,6 +661,9 @@ void usage() "options:\n" "-a --apply: apply patch (default if given two arguments)\n" "-c --create: create patch (default if given three arguments)\n" + "-I --info: BPSes contain information about input and output roms, print it\n" + //" also estimates how much of the source file is retained\n" + //" anything under 400 is fine, anything over 600 should be treated with suspicion\n" "-i --ips, -b -B --bps --bps-delta, --bps-linear, --bps-delta-moremem:\n" " create this patch format instead of guessing based on file extension\n" " ignored when applying\n" @@ -639,7 +691,7 @@ void usage() int flipsmain(int argc, WCHAR * argv[]) { enum patchtype patchtype=ty_null; - enum { a_default, a_apply_filepicker, a_apply_given, a_create } action=a_default; + enum { a_default, a_apply_filepicker, a_apply_given, a_create, a_info } action=a_default; int numargs=0; LPCWSTR arg[3]={NULL,NULL,NULL}; bool hasFlags=false; @@ -669,6 +721,11 @@ int flipsmain(int argc, WCHAR * argv[]) if (action==a_default) action=a_create; else usage(); } + else if (!wcscmp(argv[i], TEXT("--info")) || !wcscmp(argv[i], TEXT("-I"))) + { + if (action==a_default) action=a_info; + else usage(); + } else if (!wcscmp(argv[i], TEXT("--ips")) || !wcscmp(argv[i], TEXT("-i"))) { if (patchtype==ty_null) patchtype=ty_ips; @@ -719,7 +776,7 @@ int flipsmain(int argc, WCHAR * argv[]) } else if (!wcscmp(argv[i], TEXT("--version")) || !wcscmp(argv[i], TEXT("-v"))) { - ClaimConsole(); + GUIClaimConsole(); puts(flipsversion); return 0; } @@ -746,7 +803,7 @@ int flipsmain(int argc, WCHAR * argv[]) if (numargs!=0 || hasFlags) usage(); #ifndef FLIPS_CLI guiActive=true; - return ShowGUI(NULL); + return GUIShow(NULL); #else usage(); #endif @@ -756,7 +813,7 @@ int flipsmain(int argc, WCHAR * argv[]) if (numargs!=1 || hasFlags) usage(); #ifndef FLIPS_CLI guiActive=true; - return ShowGUI(arg[0]); + return GUIShow(arg[0]); #else usage(); #endif @@ -764,7 +821,7 @@ int flipsmain(int argc, WCHAR * argv[]) case a_apply_given: { if (numargs!=2 && numargs!=3) usage(); - ClaimConsole(); + GUIClaimConsole(); struct errorinfo errinf=ApplyPatch(arg[0], arg[1], !ignoreChecksum, arg[2]?arg[2]:arg[1], &manifestinfo, false); puts(errinf.description); return errinf.level; @@ -772,7 +829,7 @@ int flipsmain(int argc, WCHAR * argv[]) case a_create: { if (numargs!=2 && numargs!=3) usage(); - ClaimConsole(); + GUIClaimConsole(); if (!arg[2]) { if (patchtype==ty_null) @@ -808,6 +865,20 @@ int flipsmain(int argc, WCHAR * argv[]) puts(errinf.description); return errinf.level; } + case a_info: + { + if (numargs!=1) usage(); + return patchinfo(arg[0]); + } } return 99;//doesn't happen } + + + + + + + + + diff --git a/flips.h b/flips.h index aa5e2d8..71f574d 100644 --- a/flips.h +++ b/flips.h @@ -34,7 +34,7 @@ //#define EXTERN_C //#endif -#define flipsversion "Flips v1.31" +#define flipsversion "Flips v1.32" #if defined(FLIPS_WINDOWS) @@ -87,7 +87,6 @@ #define wprintf printf #define TEXT(text) text //EXTERN_C int strcasecmp(const char *s1, const char *s2); -#define ClaimConsole() // all other platforms have consoles already #define strdup strdup_flips static inline char* strdup(const char * in) @@ -197,9 +196,13 @@ void bpsdeltaBegin(); bool bpsdeltaProgress(void* userdata, size_t done, size_t total); void bpsdeltaEnd(); -int ShowGUI(LPCWSTR filename); +int GUIShow(LPCWSTR filename); +void GUILoadConfig(); +//LPCWSTR GUIGetFileFor(uint32_t crc32); // use FindRomForPatch instead #ifdef FLIPS_WINDOWS -void ClaimConsole(); +void GUIClaimConsole(); +#else +#define GUIClaimConsole() // all other platforms have consoles already #endif //the OS port is responsible for main() diff --git a/flips.rc b/flips.rc index 4deead2..3012271 100644 --- a/flips.rc +++ b/flips.rc @@ -7,8 +7,8 @@ 1 24 "flips.Manifest" VS_VERSION_INFO VERSIONINFO -FILEVERSION 1,3,1,0 -PRODUCTVERSION 1,3,1,0 +FILEVERSION 1,3,2,0 +PRODUCTVERSION 1,3,2,0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS 0 //VS_FF_DEBUG VS_FF_PATCHED VS_FF_PRERELEASE VS_FF_PRIVATEBUILD VS_FF_SPECIALBUILD VS_FFI_FILEFLAGSMASK @@ -22,12 +22,12 @@ BEGIN BEGIN VALUE "CompanyName", "Alcaro" VALUE "FileDescription", "Flips Patch Utility" - VALUE "FileVersion", "1.3.1.0" + VALUE "FileVersion", "1.3.2.0" VALUE "InternalName", "Floating IPS" VALUE "LegalCopyright", "©2013-2015 Alcaro" VALUE "OriginalFilename", "flips.exe" VALUE "ProductName", "Floating IPS" - VALUE "ProductVersion", "1.3.1.0" + VALUE "ProductVersion", "1.3.2.0" END END END diff --git a/libbps-suf.cpp b/libbps-suf.cpp index 80a09f9..1b759b1 100644 --- a/libbps-suf.cpp +++ b/libbps-suf.cpp @@ -108,8 +108,7 @@ #include "sais.cpp" template -static void -sufsort(sais_index_type* SA, const uint8_t* T, sais_index_type n) { +static void sufsort(sais_index_type* SA, const uint8_t* T, sais_index_type n) { if(n <= 1) { if(n == 1) SA[0] = 0; return; } sais_main(T, SA, 0, n, 256); } @@ -122,14 +121,12 @@ sufsort(sais_index_type* SA, const uint8_t* T, sais_index_type n) { //I'd prefer to let them allocate from an array I give it, but divsuf doesn't allow that, and there // are only half a dozen allocations per call anyways. -#ifdef USE_DIVSUFSORT +//This ends up in libdivsufsort if available, otherwise lite. #include "divsufsort.h" - static void sufsort(int32_t* SA, uint8_t* T, int32_t n) { divsufsort(T, SA, n); } -#endif #ifdef USE_DIVSUFSORT64 #include "divsufsort64.h" @@ -196,6 +193,11 @@ struct bps_creator { out[outlen++] = num>>24; } + static size_t maxsize() + { + return SIZE_MAX>>2; // can be reduced to SIZE_MAX>>1 by amending append_cmd, but the mallocs overflow at that point anyways. + } + size_t sourcelen; size_t targetlen; const uint8_t* targetmem; @@ -660,6 +662,7 @@ off_t lerp(off_t x, off_t y, float frac) template static bpserror bps_create_suf_core(file* source, file* target, bool moremem, struct bps_creator * out) { +#define error(which) do { err = which; goto error; } while(0) bpserror err; size_t realsourcelen = source->len(); @@ -677,18 +680,20 @@ static bpserror bps_create_suf_core(file* source, file* target, bool moremem, st if ((off_t)overflowtest < 0) return bps_too_big; //the mallocs would overflow - if ((size_t)realsourcelen+realtargetlen >= SIZE_MAX/sizeof(off_t)) return bps_too_big; + if (realsourcelen+realtargetlen >= SIZE_MAX/sizeof(off_t)) return bps_too_big; + + if (realsourcelen+realtargetlen >= out->maxsize()) return bps_too_big; off_t sourcelen = realsourcelen; off_t targetlen = realtargetlen; - uint8_t* mem_joined = (uint8_t*)malloc(sizeof(uint8_t)*(sourcelen+targetlen)); + uint8_t* mem_joined = (uint8_t*)malloc(sizeof(uint8_t)*(realsourcelen+realtargetlen)); - off_t* sorted = (off_t*)malloc(sizeof(off_t)*(sourcelen+targetlen)); + off_t* sorted = (off_t*)malloc(sizeof(off_t)*(realsourcelen+realtargetlen)); off_t* sorted_inverse = NULL; - if (moremem) sorted_inverse = (off_t*)malloc(sizeof(off_t)*(sourcelen+targetlen)); + if (moremem) sorted_inverse = (off_t*)malloc(sizeof(off_t)*(realsourcelen+realtargetlen)); off_t* buckets = NULL; if (!sorted_inverse) buckets = (off_t*)malloc(sizeof(off_t)*65537); @@ -731,33 +736,21 @@ static bpserror bps_create_suf_core(file* source, file* target, bool moremem, st prevsortedsize = sortedsize; - if (!out->progress(progPreSort, targetlen)) - { - err = bps_canceled; - goto error; - } + if (!out->progress(progPreSort, targetlen)) error(bps_canceled); - target->read(mem_joined, 0, sortedsize); - source->read(mem_joined+sortedsize, 0, sourcelen); + if (!target->read(mem_joined, 0, sortedsize)) error(bps_io); + if (!source->read(mem_joined+sortedsize, 0, sourcelen)) error(bps_io); out->move_target(mem_joined); sufsort(sorted, mem_joined, sortedsize+sourcelen); - if (!out->progress(progPreInv, targetlen)) - { - err = bps_canceled; - goto error; - } + if (!out->progress(progPreInv, targetlen)) error(bps_canceled); if (sorted_inverse) create_reverse_index(sorted, sorted_inverse, sortedsize+sourcelen); else create_buckets(mem_joined, sorted, sortedsize+sourcelen, buckets); - if (!out->progress(progPreFind, targetlen)) - { - err = bps_canceled; - goto error; - } + if (!out->progress(progPreFind, targetlen)) error(bps_canceled); } off_t matchlen = 0; diff --git a/libbps.cpp b/libbps.cpp index e8c1cec..7250b0b 100644 --- a/libbps.cpp +++ b/libbps.cpp @@ -1,6 +1,6 @@ //Module name: libbps //Author: Alcaro -//Date: December 20, 2014 +//Date: November 7, 2015 //Licence: GPL v3.0 or higher #include "libbps.h" @@ -22,6 +22,37 @@ static uint32_t read32(uint8_t * ptr) enum { SourceRead, TargetRead, SourceCopy, TargetCopy }; +static bool try_add(size_t& a, size_t b) +{ + if (SIZE_MAX-a < b) return false; + a+=b; + return true; +} + +static bool try_shift(size_t& a, size_t b) +{ + if (SIZE_MAX>>b < a) return false; + a<<=b; + return true; +} + +static bool decodenum(const uint8_t*& ptr, size_t& out) +{ + out=0; + unsigned int shift=0; + while (true) + { + uint8_t next=*ptr++; + size_t addthis=(next&0x7F); + if (shift) addthis++; + if (!try_shift(addthis, shift)) return false; + // unchecked because if it was shifted, the lowest bit is zero, and if not, it's <=0x7F. + if (!try_add(out, addthis)) return false; + if (next&0x80) return true; + shift+=7; + } +} + #define error(which) do { error=which; goto exit; } while(0) #define assert_sum(a,b) do { if (SIZE_MAX-(a)<(b)) error(bps_too_big); } while(0) #define assert_shift(a,b) do { if (SIZE_MAX>>(b)<(a)) error(bps_too_big); } while(0) @@ -42,20 +73,7 @@ enum bpserror bps_apply(struct mem patch, struct mem in, struct mem * out, struc #define read8() (*(patchat++)) #define decodeto(var) \ do { \ - var=0; \ - unsigned int shift=0; \ - while (true) \ - { \ - uint8_t next=read8(); \ - assert_shift(next&0x7F, shift); \ - size_t addthis=(next&0x7F)<len(); - if (len<4+3+12) return bps_broken; - - uint8_t verify[4]; - if (!patch->read(verify, 0, 4) || memcmp(verify, "BPS1", 4)) return bps_broken; - - uint8_t checksums[12]; - if (!patch->read(checksums, len-12, 12)) return bps_broken; - if (inromsum) *inromsum =read32(checksums+0); - if (outromsum) *outromsum=read32(checksums+4); - if (patchsum) *patchsum =read32(checksums+8); - return bps_ok; -} - void bps_free(struct mem mem) { free(mem.ptr); } +#undef error + + + +struct bpsinfo bps_get_info(file* patch, bool changefrac) +{ +#define error(why) do { ret.error=why; return ret; } while(0) + struct bpsinfo ret; + size_t len = patch->len(); + if (len<4+3+12) error(bps_broken); + + uint8_t top[256]; + if (!patch->read(top, 0, len>256 ? 256 : len)) error(bps_io); + if (memcmp(top, "BPS1", 4)) error(bps_broken); + + const uint8_t* patchdat=top+4; + if (!decodenum(patchdat, ret.size_in)) error(bps_too_big); + if (!decodenum(patchdat, ret.size_out)) error(bps_too_big); + + uint8_t checksums[12]; + if (!patch->read(checksums, len-12, 12)) error(bps_io); + ret.crc_in = read32(checksums+0); + ret.crc_out = read32(checksums+4); + ret.crc_patch=read32(checksums+8); + + if (changefrac && ret.size_in>0) + { + //algorithm: each command adds its length to the numerator, unless it's above 32, in which case + // it adds 32; or if it's SourceRead, in which case it adds 0 + //denominator is just input length + uint8_t* patchbin=(uint8_t*)malloc(len); + patch->read(patchbin, 0, len); + size_t outpos=0; // position in the output file + size_t changeamt=0; // change score + const uint8_t* patchat=patchbin+(patchdat-top); + + size_t metasize; + if (!decodenum(patchat, metasize)) error(bps_too_big); + patchat+=metasize; + + const uint8_t* patchend=patchbin+len-12; + + while (patchat>2)+1; + int action=(thisinstr&3); + int min_len_32 = (length<32 ? length : 32); + + switch (action) + { + case SourceRead: + { + changeamt+=0; + } + break; + case TargetRead: + { + changeamt+=min_len_32; + patchat+=length; + } + break; + case SourceCopy: + case TargetCopy: + { + changeamt+=min_len_32; + size_t ignore; + decodenum(patchat, ignore); + } + break; + } + outpos+=length; + } + if (patchat>patchend || outpos>ret.size_out) error(bps_broken); + ret.change_num = (changeamtallocstackmax)allocstackmax=allocstacksum; -// printf("a %p(%i) (max %i)\n",ret,n,allocstackmax); -// allocstacklen[allocstackpos]=n; -// allocstack[allocstackpos]=ret; -// allocstackpos++; -// return ret; -//} -//static void myfree(void* p) -//{ -// printf("f %p\n",p); -// allocstackpos--; -// allocstacksum-=allocstacklen[allocstackpos]; -// if (p!=allocstack[allocstackpos]) printf("E: not stack\n"); -// if (allocstackpos==0) allocstackmax=0; -// free(p); -//} #define SAIS_MYMALLOC(_num, _type) ((_type *)malloc((_num) * sizeof(_type))) #define SAIS_MYFREE(_ptr, _num, _type) free((_ptr)) #define chr(_a) T[_a]