// This tool will be implemented using only standard C, the better #include #include #include #include #include "MPWObjFuncs.c" #ifndef __GNUC__ #include #include #endif #define ERRSTR "# Vectorize Error: " #define WARNSTR "# Vectorize Warning: " // Default options const char PASSIVE_OFFSETS[11][3] = { /* 0*/ {0}, /* 1*/ {0}, /* 2*/ {0}, /* 3*/ {0}, /* 4*/ {0}, /* 5*/ {2,0}, /* 6*/ {2,0}, /* 7*/ {0}, /* 8*/ {0}, /* 9*/ {4,0}, /*10*/ {4,6,0}, }; const char OFFSETS[11][3] = { /* 0*/ {0}, /* 1*/ {0}, /* 2*/ {0}, /* 3*/ {0}, /* 4*/ {4,0}, /* 5*/ {2,4,0}, /* 6*/ {2,0}, /* 7*/ {0}, /* 8*/ {0}, /* 9*/ {4,0}, /*10*/ {4,6,0}, }; int warnings = 1; char *patchObjPath = NULL; char *symFilePath = NULL; int do_patch = 1; char *outputObjPath = NULL; char **inputPaths = NULL; int inputCount = 0; void to_pas(uchar *p, char *c) { int sl = strlen(c); p[0] = sl; memcpy(p+1, c, sl); } char *fname(char *x) { char *y = x; while(*y) y++; while(y != x && y[-1] != ':') y--; return y; } char *slurp(char *path) { FILE *f; long pos; char *bytes; f = fopen(path, "rb"); if(f == NULL) return NULL; fseek(f, 0, SEEK_END); pos = ftell(f); fseek(f, 0, SEEK_SET); bytes = (char *)malloc(pos); if(bytes == NULL) return NULL; fread(bytes, pos, 1, f); fclose(f); return bytes; } int blat(char *path, char *buf, ulong len) { FILE *f; f = fopen(path, "wb"); if(f == NULL) return 1; fwrite(buf, len, 1, f); fclose(f); return 0; } void printUsage(char *cmdName) { printf("USAGE: %s [-w] [-v PATCHES.o | -nopatch] [-log ROM.sym] -o ROM.lib INPUT1.o ...\n", cmdName); } int parseOpts(int argc, char **argv) { int i; int err = 0; for(i=1; i *a) return -1; if(*b < *a) return 1; return 0; } int tblcmp(const void *a, const void *b) { return pstrcmp(((struct tblent *)a)->name, ((struct tblent *)b)->name); } int ptch_order_cmp(const void *aa, const void *bb) { long a = (**(struct tblent **)aa).offset_of_parent_in_module; long b = (**(struct tblent **)bb).offset_of_parent_in_module; if(a > b) return -1; if(a < b) return 1; return 0; } // int tblcmp2(const void *a, const void *b) // { // int res; // printf("Comparing %.*s to %.*s", *((struct tblent *)a)->name, ((struct tblent *)a)->name+1, *((struct tblent *)b)->name, ((struct tblent *)b)->name+1); // res = pstrcmp(((struct tblent *)a)->name, ((struct tblent *)b)->name); // if(!res) printf(" ...match!"); // printf("\n"); // return res; // } // void print_tbl(struct tblent *tbl, long cnt) // { // long i; // for(i=0; i \"{PatchDump}\"\n\n", patchObjPath); fprintf(l, " # and navigate its contents:\n"); fprintf(l, " Find /NAVIGATE PATCHES/ \"{Active}\"\n"); fprintf(l, "\n"); } fprintf(l, "# Dump the \"vectorized\" output file: (takes a while)\n"); fprintf(l, " Set RomDump \"{TempFolder}VectorRomDump\"; DumpObj \"%s\" > \"{RomDump}\"\n", outputObjPath); fprintf(l, "\n"); fprintf(l, " # and look at it:\n"); fprintf(l, " File \"{RomDump}\"\n"); fprintf(l, "\n"); fprintf(l, " # or navigate it input-file-wise:\n"); fprintf(l, " Find /NAVIGATE OUTPUT/ \"{Active}\"\n"); for(file_i=0; file_i nextdelta) nextdelta = hid; } if(t == C_SIZE) { mod_sizeobj = dest.at; } if(t == C_MODULE || t == C_ENTRYPT) { struct tblent fodder_for_bsearch, *ent; uchar *my_name; char *patch_mod = NULL; int is_vectorized = 0; my_name = dict_str_from_id(&cur_dict, obj_id(&cur_obj)); if(!my_name) { fprintf(stderr, ERRSTR "Module with unlisted ID\n"); return 1; } fodder_for_bsearch.name = my_name; ent = (struct tblent *)bsearch(&fodder_for_bsearch, tbl, tblcnt, sizeof (struct tblent), tblcmp); if(ent) /* there is a patch module, but it may be zero-size! */ { if(ent->data && ent->len) { ent->clean_id = obj_id(&dest); patchstack[patchcnt++] = ent; is_vectorized = 1; if(t == C_MODULE) { ent->offset_of_parent_in_module = 0; } else { ent->offset_of_parent_in_module = longfrom(dest.at+4); } } } else { if(warnings) { fprintf(stderr, WARNSTR "Missing vector patch: %.*s\n", *my_name, my_name+1); } } if(l) { if(t == C_MODULE) { fprintf(l, "File \"{RomDump}\"; Line 0; File \"{RomDump}\"; Find /Module=¶\""); } else { fprintf(l, " File \"{RomDump}\"; Line 0; File \"{RomDump}\"; Find /Entry=¶\""); } if(is_vectorized) fprintf(l, "__v__"); fprintf(l, "%.*s¶\"/ # ID %d", *my_name, my_name+1, is_vectorized ? ent->__v__id : obj_id(&dest)); if(t == C_ENTRYPT) { fprintf(l, ", offset $%x", longfrom(dest.at+4)); } fprintf(l, "\n"); } } if(mod_copy_start && obj_islastinmod(&cur_obj)) /* empty the patchstack */ { int pi; // qsort((void *)patchstack, patchcnt, sizeof (struct tblent *), ptch_order_cmp); for(pi=0; piname; original_size = longfrom(mod_sizeobj + 2); if(original_size & 1) original_size ++; /* even-align the patches -- found this bug while reversing QuickDraw */ if(l) fprintf(l, " File \"{RomDump}\"; Line 0; File \"{RomDump}\"; Find /Entry=¶\"%.*s¶\"/ # patch ID %d, ", *my_name, my_name+1, patchstack[pi]->clean_id); if(l) debug_print_about_patch(l, patchstack[pi]->data, patchstack[pi]->len); if(l) fprintf(l, ", offset $%x\n", original_size); obj_next(&dest); /* EXTRA RECORD: CONTENTS */ dest.at[0] = C_CONTENTS; /* type */ dest.at[1] = 8; /* offset field present */ setshort(dest.at + 2, 8 + patchstack[pi]->len); /* size = 8b header, plus data */ setlong(dest.at + 4, original_size); /* offset = end of module data so far */ memcpy(dest.at + 8, patchstack[pi]->data, patchstack[pi]->len); /* actual, you know, *contents* */ obj_next(&dest); /* EXTRA RECORD: ENTRY POINT (of patch) */ dest.at[0] = C_ENTRYPT; /* type */ dest.at[1] = 8; /* external entry point */ setshort(dest.at + 2, patchstack[pi]->clean_id); /* ID */ setlong(dest.at + 4, original_size); /* offset = end of module data so far */ /* Increment the size of this module */ setlong(mod_sizeobj + 2, original_size + patchstack[pi]->len); /* Now do the dirty work of changing self-references to use __v__... */ for(scratch.at=mod_copy_start; scratch.at<=mod_copy_last; obj_next(&scratch)) { char tt = obj_type(&scratch); for(i=0; PASSIVE_OFFSETS[tt][i]; i++) { id is = shortfrom(scratch.at + PASSIVE_OFFSETS[tt][i]); if(is == patchstack[pi]->clean_id) { setshort(scratch.at + PASSIVE_OFFSETS[tt][i], patchstack[pi]->__v__id); } } } } /* patchstack loop */ if(l) fprintf(l, "\n"); /* newline before each module... nice touch */ patchcnt = 0; } /* do patchstack or not? */ } /* for each record */ free(cur_bin); //fprintf(stderr, "Culminating in %d\n", nextdelta); delta = nextdelta + 1; // delta += 5000; // delta -= (delta % 5000); } obj_next(&dest); dest.at[0] = C_LAST; dest.at[1] = 0; if(blat(outputObjPath, dest.buf, (long)(dest.at) - (long)(dest.buf) + 2)) { fprintf(stderr, ERRSTR "Failed to write output file\n"); return 5; } if(l) fclose(l); #ifndef __GNUC__ { FInfo fi; Str255 pas; int e; to_pas(pas, outputObjPath); e = GetFInfo(pas, 0, &fi); if (e == 0) { fi.fdType = 0x4F424A20; /* TEXT */ fi.fdCreator = 0x4D505320; /* MPS */ e = SetFInfo(pas, 0, &fi); } if(symFilePath) { to_pas(pas, symFilePath); e = GetFInfo(pas, 0, &fi); if (e == 0) { fi.fdType = 0x54455854; /* TEXT */ fi.fdCreator = 0x4D505320; /* MPS */ e = SetFInfo(pas, 0, &fi); } } } #endif return 0; }