/* * patch_xc.c - patches extended control panel stuff to work with CT60: * XCONTROL.ACC v1.31 (in one of 3 languages) * GENERAL.CPX v1.30 (French) & v1.40 (English) * * * Version: 1.0 * Date: 4 August 2003 * Author: Roger Burrows, Ottawa, Ontario, Canada * Compiler: Lattice C v5.60 * Changes: Original version * * * Syntax: * patch_xc * */ #include #include #include #include #include #include #include #define VERSION "Version 1.0" #define COPYRIGHT "Copyright ½ Roger Burrows, 2003" /* standard GEM defines */ #define ERROR -1 #define OK 0 /* program-specific defines */ #define INTRO_MSG "[2][CT60 updater for: |XCONTROL v1.31 (FRA/UK/USA) |GENERAL.CPX v1.30 (French) |GENERAL.CPX v1.40 (English) ][Continue|Cancel]" #define CONTINUE_MSG "[2][Process more files? ][ Yes | No ]" #define PRG_NAME "" #define PRG_MASK "*.*" #define PATCH_OK 1 /* patch status ... */ #define ALREADY_PATCHED 2 #define VERIFY_FAILED 3 #define PATCH_SKIPPED 4 #define UPDATE_FILE 1 /* options for patch_file() */ #define RESTORE_FILE 2 #define UPDATE_DONE 1 /* return codes from patch_file() */ #define RESTORE_DONE 2 #define PATHLEN 128 typedef struct { /* program file data, one per patchable version, terminated by 0 */ long program_size; ULONG *patch_data; char *desc; } PATCHINFO; /* * global variables - general */ WORD gl_apid; /* referenced in system library */ char version[] = VERSION; char copyright[] = COPYRIGHT; char name[FMSIZE] = ""; char init_path[PATHLEN] = ""; char path[PATHLEN] = ""; /* the following is patch data. each set of 3 longs is * offset, old data, new data * an offset of 1 means the data immediately follows the * previous data; an offset of 0 terminates the patch. */ ULONG xc131_fra[] = { 26190, 0x2f0a2019, 0x2f0a2019, /* relocation routine */ 1, 0x4a806722, 0x67242450, 1, 0x2450d5c0, 0x600cb03c, 1, 0x6012b03c, 0x00016606, 1, 0x00016606, 0x45ea00fe, 1, 0x45ea00fe, 0x6006d5c0, 1, 0x600a4241, 0x2210d392, 1, 0x1200d4c1, 0x70001019, 1, 0x2210d392, 0x66e83f3c, 1, 0x10194a00, 0x0c6d4e4e, 1, 0x66e4245f, 0x544f245f, 40604, 0x312e3331, 0x312e3336, /* version 1.36 */ 0 }; ULONG xc131_uk[] = { 26218, 0x2f0a2019, 0x2f0a2019, /* relocation routine */ 1, 0x4a806722, 0x67242450, 1, 0x2450d5c0, 0x600cb03c, 1, 0x6012b03c, 0x00016606, 1, 0x00016606, 0x45ea00fe, 1, 0x45ea00fe, 0x6006d5c0, 1, 0x600a4241, 0x2210d392, 1, 0x1200d4c1, 0x70001019, 1, 0x2210d392, 0x66e83f3c, 1, 0x10194a00, 0x0c6d4e4e, 1, 0x66e4245f, 0x544f245f, 40608, 0x312e3331, 0x312e3336, /* version 1.36 */ 0 }; ULONG xc131_usa[] = { 26214, 0x2f0a2019, 0x2f0a2019, /* relocation routine */ 1, 0x4a806722, 0x67242450, 1, 0x2450d5c0, 0x600cb03c, 1, 0x6012b03c, 0x00016606, 1, 0x00016606, 0x45ea00fe, 1, 0x45ea00fe, 0x6006d5c0, 1, 0x600a4241, 0x2210d392, 1, 0x1200d4c1, 0x70001019, 1, 0x2210d392, 0x66e83f3c, 1, 0x10194a00, 0x0c6d4e4e, 1, 0x66e4245f, 0x544f245f, 40604, 0x312e3331, 0x312e3336, /* version 1.36 */ 0 }; ULONG general_fra[] = { 4, 0x4647454e, 0x46474536, /* cpx id 'FGE6' */ 134, 0x6c000000, 0x6c203630, /* add '60' to title */ 7336, 0x701eb097, 0x703cb097, /* check for 060 cpu */ 7408, 0x00003111, 0xa0808000, /* CACR value to check */ 8532, 0x203c0000, 0x2f3cc60c, /* cache enable */ 1, 0x39194e7b, 0x00014e4e, 1, 0x00024e75, 0x584f4e75, 8544, 0x203c0000, 0x2f3cc60c, /* cache disable */ 1, 0x08084e7b, 0x00004e4e, 1, 0x00024e75, 0x584f4e75, 0 }; ULONG general_eng[] = { 4, 0x47454e00, 0x47454e36, /* cpx id 'GEN6' */ 134, 0x70000000, 0x70203630, /* add '60' to title */ 7338, 0x701eb097, 0x703cb097, /* check for 060 cpu */ 7410, 0x00003111, 0xa0808000, /* CACR value to check */ 8532, 0x203c0000, 0x2f3cc60c, /* cache enable */ 1, 0x39194e7b, 0x00014e4e, 1, 0x00024e75, 0x584f4e75, 8544, 0x203c0000, 0x2f3cc60c, /* cache disable */ 1, 0x08084e7b, 0x00004e4e, 1, 0x00024e75, 0x584f4e75, 0 }; PATCHINFO patchinfo[] = { { 44990, xc131_fra, "French" }, /* France */ { 44685, xc131_uk, "UK" }, /* UK */ { 44681, xc131_usa, "USA" }, /* USA */ { 14133, general_fra, "French" }, /* French-language */ { 14111, general_eng, "English" },/* English-language */ { 0, NULL } }; /* * private function prototypes */ int init_gem(void); int term_gem(void); WORD get_file(char *prompt,char *buffer,char *wild,char *initial); void get_cwd(char *buffer); void get_path_name(char *dname); void main(int argc,char **argv); void terminate(char *msg); void status(char *msg); int err(char *msg,char *filename,int changes); int check_file(FILE *fp,PATCHINFO *pi); int patch_file(char *filename,FILE *fp,PATCHINFO *pi,int option); /* * main program */ void main(int argc,char **argv) { PATCHINFO *pi; FILE *fp; char msg[200], *p; int rc; /* * tell GEM hello. * we're glad to see him too. */ if (init_gem() == ERROR) exit(1); graf_mouse(ARROW,0); if (form_alert(1,INTRO_MSG) != 1) terminate("Program cancelled"); do { strcpy(path,init_path); strcpy(name,PRG_NAME); if (get_file("Select program file",path,PRG_MASK,name) <= 0) continue; strcpy(init_path,path); /* re-initialise */ p = strrchr(init_path,'\\'); if (p) *p = '\0'; if ((fp=fopen(path,"rb+")) == NULL) { err("can't open file",path,0); continue; } for (pi = patchinfo; pi->program_size; pi++) if (filelength(fileno(fp)) == pi->program_size) break; rc = PATCH_SKIPPED; switch(check_file(fp,pi)) { case PATCH_OK: sprintf(msg,"[2][You have selected the |original %s version. ][Update|Skip]",pi->desc); if (form_alert(1,msg) == 1) rc = patch_file(path,fp,pi,UPDATE_FILE); break; case ALREADY_PATCHED: sprintf(msg,"[2][You have selected the |updated %s version. ][Restore|Skip]",pi->desc); if (form_alert(2,msg) == 1) rc = patch_file(path,fp,pi,RESTORE_FILE); break; case VERIFY_FAILED: form_alert(1,"[1][Selected file does not |match update data. ][Skip]"); break; default: form_alert(1,"[1][Error reading program file. ][Skip]"); break; } if (rc == PATCH_SKIPPED) continue; switch(rc) { case UPDATE_DONE: sprintf(msg,"[1][Program has been updated |for CT60. ][Continue]"); break; case RESTORE_DONE: sprintf(msg,"[1][Original version has been |restored. ][Continue]"); break; case 0: sprintf(msg,"[1][Program modification failed. |No changes have been made. ][Continue]"); break; default: sprintf(msg,"[1][Program changes incomplete. |Restore from a backup! ][Continue]"); break; } form_alert(1,msg); } while (form_alert(2,CONTINUE_MSG) == 1); terminate(""); } /* ********** ye olde GEM interface routines */ int init_gem() { appl_init(); /* register application */ if (gl_apid < 0) { form_alert(1,"[3][Fatal error !|Cannot initialise application][ Quit ]"); return ERROR; } return OK; } int term_gem() { if (appl_exit() == 0) { form_alert(1,"[3][Fatal error !|Cannot exit properly][ Quit ]"); return ERROR; } return OK; } /* ********** file-handling routines */ /* * get the current working directory into buffer * * Input: * buffer - large enough to hold a valid path name * Output: * (void) buffer contains the cwd * */ void get_cwd(char *buffer) { register int drive; drive = Dgetdrv(); /* get default drive */ buffer[0] = drive + 'A'; /* & move to buffer */ buffer[1] = ':'; buffer[2] = '\0'; Dgetpath(&buffer[2],0); /* get current directory */ strcat(buffer,"\\"); /* & terminate it with \. */ } /* * get a file name using fsel_exinput() dialog * * Input: * buffer - buffer large enough to hold the longest valid pathname. * If buffer[0] == '\0' on entry, the current working * directory is used. If buffer[0] != '\0' it is assumed * to contain the directory path to be initially shown * in the dialog. * wild - the wild card spec to use to match file names. * initial- the initial file selection if any. It must be * large enough to hold a valid file name (13 characters). * Output: * -1 - if an error occurred in the dialog * 0 - if the user hit CANCEL * >0 - if no error and user hit OK. Buffer will contain the * complete path name of the file selected. Initial will * contain only the file name selected. */ WORD get_file(char *prompt,char *buffer,char *wild,char *initial) { WORD button; int l; if (*buffer == '\0') /* get current pathname into buffer */ get_cwd(buffer); else { /* path specified - check it ends in '\' */ l = strlen(buffer); if (buffer[l-1] != '\\') { /* doesn't, so put it in */ buffer[l] = '\\'; buffer[l+1] = '\0'; } } strcat(buffer,wild); /* add on wildcard spec */ if (fsel_exinput(buffer,initial,&button,prompt) == FALSE) return ERROR; /* error occurred */ if (button) { /* user clicked on OK */ get_path_name(buffer); strcat(buffer,initial); } return button; } /* * get pointer to the name at the end of a file spec * * Input: * name - the full path name * * Output: * pointer to the last component of the full path name */ char *get_name(char *name) { register char c; register int i; i = strlen(name); while ((i > 0) && (((c = name[i-1]) != '\\') && (c != ':'))) i--; return name+i; } /* * remove the name at the end of a file spec * * Input: * dname - the directory name, with possibly wild card characters at * the end * Output: * (void) dname contains the full pathname, excluding the filename */ void get_path_name(char *dname) { register char *p; p = get_name(dname); /* get ptr to last component of name */ if (*(p-1) == ':') /* if back at drive spec, */ *p++ = '\\'; /* insert the \. */ *p = '\0'; /* terminate dir name */ } /* * replace the extension at the end of a file spec * * Input: * path - the full pathname, including filename and extension * ext - the new extension * Output: * (void) path contains the full pathname, with new extension */ void make_path_name(char *path,char *ext) { register char *p; p = get_name(path); /* get ptr to last component of name */ while (*p && (*p != '.')) /* look for start of extension */ p++; strcpy(p,"."); /* tag on the new extension */ strcat(p,ext); } /* ********** other routines */ /* * check patch file * returns: PATCH_OK patch verifies olddata OK * ALREADY_PATCHED file is already patched * VERIFY_FAILED patch doesn't fit * ERROR error accessing file */ int check_file(FILE *fp,PATCHINFO *pi) { ULONG *pd, olddata; int old = 0, new = 0, tot = 0; int match; if (!pi->patch_data) return VERIFY_FAILED; for (pd = pi->patch_data; *pd; tot++, pd += 3) { /* terminate on word of zeros */ if (*pd != 1) if (fseek(fp,*pd,SEEK_SET) < 0) return ERROR; olddata = fgetl(fp); if (feof(fp)) return ERROR; match = 0; if (olddata == *(pd+1)) old++, match++; if (olddata == *(pd+2)) new++, match++; if (!match) //debug only olddata = 999; //debug only } if (old == tot) return PATCH_OK; if (new == tot) return ALREADY_PATCHED; return VERIFY_FAILED; } /* * do patch * returns: PATCH_DONE patch completed OK * RESTORE_DONE restore completed OK * 0 failed, but no changes made * -N failed, N changes made */ int patch_file(char *filename,FILE *fp,PATCHINFO *pi,int option) { int changes = 0; ULONG *pd; for (pd = pi->patch_data; *pd; changes++, pd += 3) { /* terminate on word of zeros */ if (*pd != 1) if (fseek(fp,*pd,SEEK_SET) < 0) return err("can't seek in file",filename,changes); if (option == RESTORE_FILE) /* if restoring */ fputl(*(pd+1),fp); /* use col 2 */ else fputl(*(pd+2),fp); /* else col 3 */ if (feof(fp)) return err("can't write file",filename,changes); } if (fclose(fp)) return err("can't close file",filename,changes); if (option == RESTORE_FILE) return RESTORE_DONE; return UPDATE_DONE; } /* * terminate with an optional message */ void terminate(char *msg) { char s[200]; if (*msg) { sprintf(s,"[1][|%s][ Done ]",msg); form_alert(1,s); } /* tell GEM bye, bye */ if (term_gem() == ERROR) exit(1); exit(0); } void status(char *msg) { char s[200]; sprintf(s,"[1][%s ][Continue]",msg); form_alert(1,s); } int err(char *msg,char *filename,int changes) { char s[200]; sprintf(s,"[1][Error: | %s | %s ][Continue]",msg,filename); form_alert(1,s); return -changes; }