home *** CD-ROM | disk | FTP | other *** search
- const char *Version=
- "AFHack Version 1.1 - AF hacking utility for One Must Fall 2097 .AF files\n"
- "By Kenneth F. Henderson Jr. (71672,1777)";
- /*
- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
-
- >> BACK UP YOUR ORIGINAL .AF FILES BEFORE USING THIS PROGRAM! <<
-
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
- This code is, of course, provided AS IS, with no waranty or anything else.
- Copy it, rewrite it, use it, FUBAR it, whatever you like (I would prefer that
- you include the above credits if you redistribute it, BTW), however no matter
- what happens none of the aforementioned people can be held responsible, whether
- your hard disk crashes, your computer dies, OMF decides to cheat on you every
- time you play, or anything else. <grin>
-
- WARNING: The Borland C++ project file that accompanies this code turns
- 386 instructions ON, on the premise that since OMF requires a 386, all
- machines running this code will have at least a 386. The included .COM file
- was compiled using this project file, so it may very well hang (or worse)
- on a lesser machine unless recompiled with the option set down to whatever
- you're using.
-
- Before use, you *must* backup your .AF files. Personally, I recommend
- creating a subdirectory named "s" under your main OMF directory and copying
- *.AF into this new subdirectory. Then use the original .AFs in that
- subdirectory as the *SOURCE* .AF files with this utility, and use the ones in
- your main OMF directory as the *DESTINATION*. Make certain you don't ever
- overwrite the backups, or you'll have to reinstall OMF to restore the
- original operation of the 'bots. Running this program completely overwrites
- the destination AF file, but does not modify the hack file or the source AF.
-
- For basic usage instructions, just run the program with no parameters.
-
- If you just want to use this program to patch in moves created by others, you
- probably know all you need to know about this utility now. However, if you
- want to make your own moves using this utility, and you understand the .AF
- format well enough to write move strings, read on.
-
- This utility uses script files to describe moves for patching in. The format
- is very simple - any line which isn't part of the script is a comment, and
- *MUST* start with a semicolon (';'), unless you enjoy error messages. ;-)
- The first non-comment line in the file needs to be the number of hacks in the
- file. This is used as a checksum of sorts, and also makes sure the utility
- doesn't read any further than it needs to. (In fact, you could put comments
- without semicolons after the last hack, since, as long as the number of hacks
- is right, the utility will never get that far.)
- All non-comment lines after that (up to the number of hacks specified, as
- mentioned above) need to be in the following format, and in address order.
-
- AAAAAAAA:T:AF-String
-
- The 'A's should be the up-to-eight-digit *hexadecimal* file offset of the
- move you want to change. That is, the address of the first letter of the
- move. You can find this with the string finder included with this package,
- assuming you know what to look for (the keystroke sequences nearby are usually
- enough to tell you which string you're looking for.)
- The 'T' (type of patch) should be either an 'R' or an 'O'. 'R' is for
- automatic resizing - the space for the existing string will automatically be
- resized to fit the new string being patched in. This is intended only for use
- with the actual move strings, and may or may not work if you use it to modify
- other parts of the file. 'O' is used for directly overwriting the specified
- part of the file - good for key strings and the like.
- 'AF-String' is the whole string you wish to patch in. Don't use newlines in
- the string, or you'll confuse the utility. You may need a pretty good text
- editor to make long strings; most editors have some sort of limit on line
- length. If your editor can handle lines up to 1024 characters (which is about
- as much as OMF itself can take), you're all set. If not... get yourself a
- better editor! <g>
-
- Below is the actual C source code. Hackers only. B-)
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <string.h>
- #include <alloc.h>
-
- // Return codes (RET_)
- //These codes may change from version to version
- //If you use them, always check for changes when you get a new version
- #define RET_Normal 0
- #define RET_BadParms -1
- #define RET_BadFile -2
- #define RET_BadRead -3
- #define RET_BadClose -4
- #define RET_OutOfMemory -5
-
- int HackScanf(FILE *FileHack,const char *format, ...)
- {
- va_list args;
- int i;
-
- //Skip comments
- while( (i=getc(FileHack)) == ';' )
- while( getc(FileHack)!='\n' && !feof(FileHack) );
- ungetc(i,FileHack);
-
- va_start(args,format);
- i=vfscanf(FileHack,format,args);
- va_end(args);
- return i;
- }
-
- int ParseCString(char *String)
- {
- char *S,*D;
-
- //Check for first occurence; no occurence = nothing to do
- if( !(S=D=strchr(String,'\\')) )
- return strlen (String);
-
- //Loop until end of string, skip opening '\'. On last pass, S will still be
- //incremented again, but it doesn't matter since S isn't used after this
- while(*(S++))
- {
- switch(*S)
- {
- case 'a': *(D++)='\a';break;
- case 'b': *(D++)='\b';break;
- case 'f': *(D++)='\f';break;
- case 'n': *(D++)='\n';break;
- case 'r': *(D++)='\r';break;
- case 't': *(D++)='\t';break;
- case 'v': *(D++)='\v';break;
- case '\\': *(D++)='\\';break;
- case '\'': *(D++)='\'';break;
- case '\"': *(D++)='\"';break;
- case '\?': *(D++)='\?';break;
- //Numeric support hardcoded for 0 only; saves trouble. Maybe add later if requested.
- case '0': *(D++)='\0';break;
- case 'x':
- case 'X':
- printf("\n\\x escape sequence not supported yet");
- break;
- default:
- printf("\nBad \\ escape sequence: %c",*S);
- *(D++)='\\';
- S--; //Compensate for below
- break;
- }
-
- S++; //Skip at least one char from above; do here for compactness.
-
- //Copy up to next occurence or null, then loop again
- while( (*S!='\\') && (*S!='\0') )
- *(D++)=*(S++);
- }
-
- return D-String; //Return final parsed size of string
- }
-
- //Byte shoving functions in #defines for speed
-
- #define FRead(Buffer,BufferSize,File)\
- if( (BytesRead=fread(Buffer,1,BufferSize,File)) != BufferSize )\
- {\
- printf( "\nRead error. Unexpected end-of-file?"\
- "\nFRead reports %Xh of %Xh requested bytes read.",\
- BytesRead,BufferSize);\
- return RET_BadRead;\
- }
-
- #define FWrite(Buffer,BufferSize,File)\
- if( (BytesWritten=fwrite(Buffer,1,BufferSize,File)) != BufferSize)\
- printf( "\nWrite error. Out of disk space? Error ignored."\
- "\nFWrite reports %Xh of %Xh requested bytes written",\
- BytesWritten,BufferSize);
-
- #define TransferBlock(Size)\
- {\
- while(Size>=BufferSize)\
- {\
- FRead(Buffer,BufferSize,FileAFS)\
- FWrite(Buffer,BufferSize,FileAFD)\
- FilePos+=BufferSize;\
- Size-=BufferSize;\
- putchar('.');\
- }\
- if(Size)\
- {\
- FRead(Buffer,Size,FileAFS)\
- FWrite(Buffer,Size,FileAFD)\
- FilePos+=Size;\
- putchar('.');\
- }\
- }
-
- int ApplyHacks(FILE *FileHack,FILE *FileAFS,FILE *FileAFD,
- unsigned char *Buffer,size_t BufferSize)
- {
- size_t BytesRead,BytesWritten;
- unsigned char HackLocStr[9],HackType,*c;
- unsigned long HackLoc,FileSize,FilePos=0,l;
- unsigned int NumHacks,NumHacksDone=0,i,j;
-
- if(HackScanf(FileHack,"%d\n",&NumHacks)!=1)
- {
- printf("Invalid Hack file.\n");
- return RET_BadFile;
- }
- printf("Applying %u hack(s).",NumHacks);
-
- //Get length of file
- fseek(FileAFS,0,SEEK_END);
- FileSize=ftell(FileAFS);
- fseek(FileAFS,0,SEEK_SET);
-
- while(NumHacksDone<NumHacks)
- {
- //Read in Hack Location
- if(HackScanf(FileHack,"%8[0-9A-Fa-f]:%c:",&HackLocStr,&HackType)!=2)
- {
- printf(
- "\nUnexpected end-of-file or read error in Hack file");
- break;
- }
- //Actual point -2 if resizing, to access length field
- HackLoc=strtoul(HackLocStr,0,16) - (HackType=='R'?2:0);
-
- //Check for proper ordering
- if(HackLoc<FilePos)
- {
- printf(
- "\nOut-of-order hack at %lXh. Hacks must be in address order",
- HackLoc + (HackType=='R'?2:0));
- break;
- }
-
- //Copy up to the Hack point
- l=HackLoc-FilePos;
- TransferBlock(l);
-
- //Read in Hack String
- c=Buffer;
- while( ( *(c++) = getc(FileHack) ) != '\n' &&
- !feof(FileHack) );
-
- if(*(--c)!='\n') //Then the loop obviously terminated on EOF
- {
- printf(
- "\nUnexpected end-of-file or read error in Hack file");
- break;
- }
- *c='\0'; //Null terminate
- j=ParseCString(Buffer);
-
-
- if(HackType=='R') //If resizing...
- {
- //Skip existing string in original
- FRead(&i,sizeof(i),FileAFS)
- FilePos+=sizeof(i)+i;
- fseek(FileAFS,FilePos,SEEK_SET);
-
- //Warn if oversize
- if(j>1024)
- printf(
- "\nWarning: Hack string at %lXh exceeds 1KB (%i bytes)"
- "\nThis might cause problems with OMF",
- //Compensate for internal -2
- HackLoc+2,j);
-
- //Put new string in
- FWrite(&j,sizeof(j),FileAFD)
- FWrite(Buffer,j,FileAFD)
- }
- else if (HackType=='O') //If overwriting...
- {
- //Skip existing string in original
- FilePos+=j;
- fseek(FileAFS,FilePos,SEEK_SET);
-
- //Put new string in
- FWrite(Buffer,j,FileAFD)
- }
- else
- {
- printf("\nIgnoring bad hack type: %c",HackType);
- }
-
- NumHacksDone++;
- }
-
- //Copy rest of original file
- if( (l=FileSize-FilePos) != 0 )
- TransferBlock(l);
-
- printf("\n%u of %u hack(s) applied.\n",NumHacksDone,NumHacks);
- return RET_Normal;
- }
-
- int _cdecl main(int argc,unsigned char *argv[])
- {
- FILE *FileHack,*FileAFS,*FileAFD;
- unsigned char *Buffer;
- size_t BufferSize;
- int i;
- const char *FileOpenError="Couldn't open file: %s\n";
-
-
- puts(Version);
- if(argc<4)
- {
- printf(
- "Usage: AFHack <Hack file> <Source AF file> <Destination AF file>\n"
- );
- exit(RET_BadParms);
- }
-
- printf( "Hack : %s\n"
- "AF Src: %s\n"
- "AF Dst: %s\n",argv[1],argv[2],argv[3]);
-
- if(!(FileHack=fopen(argv[1],"rt")))
- {
- printf(FileOpenError,argv[1]);
- return RET_BadFile;
- }
- if(!(FileAFS=fopen(argv[2],"rb")) )
- {
- printf(FileOpenError,argv[2]);
- return RET_BadFile;
- }
- if(!(FileAFD=fopen(argv[3],"w+b")) )
- {
- printf(FileOpenError,argv[3]);
- return RET_BadFile;
- }
-
- //Allocate all available near memory as buffer
- if(!(Buffer=(unsigned char *)malloc(BufferSize=coreleft())))
- {
- puts("Could not allocate available memory as buffer!\n");
- return RET_OutOfMemory;
- }
- printf("Buffer: %u bytes.\n",BufferSize);
-
- i=ApplyHacks(FileHack,FileAFS,FileAFD,Buffer,BufferSize);
-
- free(Buffer);
-
- if(fclose(FileHack)|fclose(FileAFS)|fclose(FileAFD))
- {
- puts("Error closing file(s)!\n");
- return RET_BadClose;
- }
-
- return i;
- }
-