home *** CD-ROM | disk | FTP | other *** search
- /* pp_patcher v1.0
- **
- ** pp_patcher is a small utility which enables programs to get at
- ** PowerPacked files, as if these were normal files. This is acomplished
- ** by patching some vital DOS functions, thus redirecting calls to these to
- ** my own internal routines. The overall effect is that PowerPacked data-
- ** files seem to be normal files. You can TYPE them in a CLI window, or
- ** bring them directly into your favourite editor. Another good way to
- ** use this proggy is if you crunch your workbench icons. Workbench will
- ** never know the difference, but it reduces the diskspace occupied by
- ** icons by some 65-70% (usually). There is a nominal performance reduction
- ** due to the fact that we have to decrunch the icons before passing them
- ** on to Workbench, but this doesn't seem too annoying. Especially not if
- ** you use optimized (B.A.D.) disks, or the CLI command Addbuffers or
- ** equivalent.
- **
- ** For further info, read the DOC file.
- **
- ** Compiles under Aztec V5.0 using large code/large data/32 bit integers
- ** Should do fine under Lattice, too, if you fix the #pragma's.
- **
- ** Shareware 1991, copyright (C) 1991 by Michael Berg
- ** Sct. Peders Gade 24A, 2th
- ** 8900 Randers
- ** DENMARK
- */
-
- /* That's right -- ALL of these are necessary! */
- #include <functions.h>
- #include <exec/nodes.h>
- #include <exec/lists.h>
- #include <exec/memory.h>
- #include <libraries/dos.h>
- #include <libraries/dosextens.h>
- #include <intuition/intuition.h>
- #include "ppbase.h" /* enclosed in this directory */
-
- #asm
- DOSLibPatch MACRO
- public _Real\1
- public _New\1
- public _Make\1
- public _Rest\1
- public _LVO\1
- public \2
- cseg
-
- _Make\1 ;make a doslib patch
- move.l a6,-(sp) ;save regs
- move.l \2,a6 ;get at the dosbase
- lea _LVO\1(a6),a6 ;jump vector address
- moveq #0,d0
- move.w (a6),Orig\1 ;fetch the 'moveq #?,d0' opcode
- move.w 4(a6),d0 ;fetch the 'bra' offset
- add.l a6,d0 ;add to find branch target
- addq.l #4,d0 ;pc relative offset compensation
- move.l d0,Orig\1+4 ;we need this so we can jump directly
- move.w #$4ef9,(a6) ;initiate new sequence: jmp $abs_addrs
- move.l #_New\1,2(a6) ;jump to our new function, of corz!
- move.l (sp)+,a6 ;restore regs
- rts
-
- dseg
- ds 0
- _Real\1
- Orig\1
- dc.w 0 ;this will be replaced by a moveq
- dc.w $4ef9,0,0 ;the JMP address will be fixed
-
- cseg
- _Rest\1 ;restore a doslib patch
- tst.l Orig\1+4 ;did we make it at all?
- beq.s 1$ ;nope, don't do anything
- move.l a6,-(sp) ;save regs
- move.l \2,a6 ;get at the dosbase
- lea _LVO\1(a6),a6 ;find jump vector address
- move.l Orig\1+4,d0 ;calculate a bra offset to the orig. addrs
- subq.l #4,d0 ;pc relative offset compensation
- sub.l a6,d0 ;subtract base address
- move.w Orig\1,(a6) ;restore the moveq opcode
- move.w #$6000,2(a6) ;opcode for 'bra.l'
- move.w d0,4(a6) ;save the branch offset
- move.l (sp)+,a6 ;restore registers
- 1$ rts
- ENDM
-
- ;DOSLibPatch is a macro which defines three functions. For Open(), they are:
- ;
- ;MakeOpen - Patch the DOS library Open() vector
- ;RestOpen - Restore the DOS library to it's original state
- ;RealOpen - This calls the original DOS function
- ;
- ;Furthermore, you yourself have to create a function called (in this case)
- ;NewOpen, with a parameter specification like Open(). Future calls to Open
- ;will be redirected to NewOpen. NewOpen has access to the original DOS code
- ;through the function RealOpen. RealOpen should also be defined like Open.
- ;More details follow. For now, you need to know that DOSLibPatch works only
- ;with the DOS library, which differes in format from other libraries.
-
- DOSLibPatch Open,_DOSBase
- DOSLibPatch Close,_DOSBase
- DOSLibPatch Examine,_DOSBase
- DOSLibPatch Write,_DOSBase
- #endasm
-
- /* Well, the compiler has the inline-code ability -- why not use them? */
- #define strlen _BUILTIN_strlen
- #define strcpy _BUILTIN_strcpy
-
- /* Some defines which make the source code look pretty */
- typedef struct IntuitionBase INTUIBASE;
- typedef struct GfxBase GFXBASE;
- typedef struct PPBase PPBASE;
-
- /* A tiny window enables the user to control the program */
- struct NewWindow nw =
- {
- 0,0,320,10,
- -1,-1,
- CLOSEWINDOW,
- WINDOWDRAG | WINDOWCLOSE | WINDOWDEPTH | ACTIVATE,
- NULL,NULL,
- (UBYTE *)"Powerpacker Patcher V1.0",
- NULL,NULL,
- 0,0,0,0,
- WBENCHSCREEN
- };
-
- /* powerpacker.library related functions */
- extern int ppLoadData(char *, int, int, UBYTE **, int *, int (*)());
- #pragma amicall(PPBase, 0x1e, ppLoadData(a0,d0,d1,a1,a2,a3))
-
- /* Open()-patch related functions */
- extern void MakeOpen(void), RestOpen(void);
- extern BPTR RealOpen(char *, int);
- extern BPTR NewOpen(char *, int);
- #pragma regcall(RealOpen(d1,d2)) /* All of these PRAGMAs are EXTREMELY */
- #pragma regcall(NewOpen(d1,d2)) /* important !!!!!!!!!! */
-
- /* Close()-patch related functions */
- extern void MakeClose(void), RestClose(void);
- extern void RealClose(BPTR);
- extern void NewClose(BPTR);
- #pragma regcall(RealClose(d1))
- #pragma regcall(NewClose(d1))
-
- /* Examine()-patch related functions */
- extern void MakeExamine(void), RestExamine(void);
- extern int RealExamine(BPTR, struct FileInfoBlock *);
- extern int NewExamine(BPTR, struct FileInfoBlock *);
- #pragma regcall(RealExamine(d1,d2))
- #pragma regcall(NewExamine(d1,d2))
-
- /* Write()-patch related functions */
- extern void MakeWrite(void), RestWrite(void);
- extern int RealWrite(BPTR, char *, int);
- extern int NewWrite(BPTR, char *, int);
- #pragma regcall(RealWrite(d1,d2,d3))
- #pragma regcall(NewWrite(d1,d2,d3))
-
- /* Define the maximum length of a filename and its path
- ** 256 is, as far as I can tell, the AmigaDOS limit (due to BSTR's).
- */
- #define MAXPATHLEN 256
-
- /* Match tags for PowerPacked files */
- #define PP20 (('P' << 24) + ('P' << 16) + ('2' << 8) + '0')
- #define PX20 (('P' << 24) + ('X' << 16) + ('2' << 8) + '0')
-
- /* One of these for each opened, powerpacked file, which has not yet been
- ** closed
- */
- struct filenode
- {
- struct MinNode mn;
-
- BPTR filehandle;
- char *ram_filename, *orig_filename;
- short dirty;
- };
-
- /* One of these for each caller to NewOpen() */
- struct caller
- {
- struct MinNode mn;
-
- struct Task *tc;
- };
-
- /* Global data */
- struct MinList templist, callers;
- struct Window *win;
- struct IntuitionBase *IntuitionBase;
- struct GfxBase *GfxBase;
- struct PPBase *PPBase;
- int patched;
-
- /* Exported to detach module */
- int _stack = 4000;
- int _priority = 5;
- char *_procname = "Powerpacker Patcher";
- BPTR _BackGroundIO;
-
- /* Universal termination code */
- void die(int errcode)
- {
- /* Restore the original DOS functions. You will not find these
- ** functions in the sourcecode. They are generated by the assembler
- ** macro DOSLibPatch -- see above
- */
- if (patched)
- {
- Forbid();
- RestOpen();
- RestClose();
- RestExamine();
- RestWrite();
- Permit();
- }
-
- /* Close the window */
- if (win) CloseWindow(win);
-
- /* Close libraries */
- if (IntuitionBase) CloseLibrary(IntuitionBase);
- if (GfxBase) CloseLibrary(GfxBase);
- if (PPBase) CloseLibrary(PPBase);
-
- /* Finally! */
- exit(errcode);
- }
-
- /* Open up all required libraries */
- void openlibs()
- {
- if
- (
- !(GfxBase = (GFXBASE *)OpenLibrary("graphics.library", 0)) ||
- !(IntuitionBase = (INTUIBASE *)OpenLibrary("intuition.library", 0)) ||
- !(PPBase = (PPBASE *)OpenLibrary("powerpacker.library",0))
- )
- /* Graphics & Intuition should NEVER fail to open */
- die(10);
- }
-
- /* Attempt to open the window */
- void openwindow()
- {
- if (!(win = OpenWindow(&nw)))
- die(20);
- }
-
- /* Install the DOS patches */
- void installpatch()
- {
- Forbid();
- MakeOpen();
- MakeClose();
- MakeExamine();
- MakeWrite();
- Permit();
-
- patched = 1;
- }
-
- /* Some Lists need to be initialized */
- void initlist()
- {
- NewList((struct List *)&templist);
- NewList((struct List *)&callers);
- }
-
- /* Open up everything */
- void openstuff()
- {
- openlibs();
- openwindow();
- initlist();
- installpatch();
- }
-
- /* Add a new caller to NewOpen() to the list of callers */
- addcaller(register struct Task *tc)
- {
- register struct caller *memgot;
-
- if (memgot = AllocMem(sizeof(*memgot),0))
- {
- memgot->tc = tc;
- AddTail((struct List *)&callers, (struct Node *)memgot);
- return(1);
- }
- else
- return(0);
- }
-
- /* Add a file node to the list of files which we have created. These
- ** exist temporarily in RAM: and we need to get rid of these along the way,
- ** as they are Close()'d.
- */
- addfilenode(register BPTR fn, register char *filename, register char *orig)
- {
- register struct filenode *memgot;
-
- if
- (
- (memgot = AllocMem(sizeof(*memgot),MEMF_CLEAR)) &&
- (memgot->ram_filename = AllocMem(strlen(filename)+1,0)) &&
- (memgot->orig_filename = AllocMem(strlen(orig)+1,0))
- )
- {
- memgot->filehandle = fn;
- strcpy(memgot->ram_filename,filename);
- strcpy(memgot->orig_filename,orig);
- AddTail((struct List *)&templist, (struct Node *)memgot);
- return(1);
- }
- else
- return(0);
- }
-
- /* Find a filenode (keyed by its filehandle) */
- struct filenode *findfilenode(register BPTR fn)
- {
- register struct filenode *search;
-
- /* Linear search is employed */
- for
- (
- search = (struct filenode *)(templist.mlh_Head);
- search->mn.mln_Succ;
- search = (struct filenode *)(search->mn.mln_Succ)
- )
- if (search->filehandle == fn)
- return(search);
-
- return(NULL);
- }
-
- /* Find a caller on the callers list */
- struct caller *findcaller(register struct Task *tc)
- {
- register struct caller *search;
-
- for
- (
- search = (struct caller *)(callers.mlh_Head);
- search->mn.mln_Succ;
- search = (struct caller *)(search->mn.mln_Succ)
- )
- if (search->tc == tc)
- return(search);
-
- return(NULL);
- }
-
- /* This baby builds a complete filename (including a path) from a BCPL
- ** pointer to a filehandle. Optimizations are most welcome. All those
- ** ParentDir() calls take a LONG time.
- */
- char *fullname(register BPTR lock, register struct FileInfoBlock *fib)
- {
- static char pathandfile[MAXPATHLEN];
- char tmp[MAXPATHLEN];
- register BPTR parentlock, unlocklock;
- register char *co;
-
- strcpy(pathandfile,fib->fib_FileName);
-
- parentlock = lock;
- unlocklock = (BPTR)0;
- while (parentlock=ParentDir(parentlock))
- {
- if (unlocklock)
- UnLock(unlocklock);
-
- if (RealExamine(parentlock,fib))
- {
- strcpy(tmp,fib->fib_FileName);
- strcat(tmp,"/");
- strcat(tmp,pathandfile);
- strcpy(pathandfile,tmp);
- }
- else
- {
- UnLock(parentlock);
- return(NULL);
- }
-
- unlocklock = parentlock;
- }
-
- if (unlocklock)
- UnLock(unlocklock);
-
- if (co = (char *)index(pathandfile,'/'))
- *co = ':';
-
- /* This fixes a bug in the old RAM disk */
- if (!strcmp(pathandfile,":"))
- strcpy(pathandfile,"RAM:");
-
- return(pathandfile);
- }
-
- /* Is a filename really a file? We need to know this, because it is
- ** rediculous to try to read in the PowerPacker matchtag from something
- ** like CON:0/0/544/23/ConWindow.
- */
- reallyfile(register char *filename)
- {
- /* Don't like to hard-wire it like this, but it seems to
- ** be the only realistic approach. Don't worry, you can't
- ** do an ASSIGN to any of these. If you could, we would have
- ** to check all volumenodes on the DeviceList stored in
- ** the RootNode of the DosLibrary. (Got that?!)
- */
- static char *duds[] =
- {
- "NIL:", "CON:",
- "RAW:", "PRT:",
- "PAR:", "SER:"
- };
-
- register short i;
-
- /* A simple, linear search is employed */
- for (i = 0; i < sizeof(duds)/sizeof(char *); i++)
- {
- register short foundit;
- register char *cmp, *cmp2;
- register short j;
-
- cmp = duds[i];
- cmp2 = filename;
-
- for (foundit = 1, j = 0; j < 4; j++)
- {
- if (toupper(*cmp++) != toupper(*cmp2++))
- {
- foundit = 0;
- break;
- }
- }
-
- if (foundit)
- return(0);
- }
-
- /* One last check, just to be sure */
- if (!strcmp(filename,"*"))
- return(0);
-
- return(1);
- }
-
- /* When somebody opens a PP file, we decrunch it into a temporary RAM file
- ** and return a filehandle to that file. When the caller closes the file
- ** (which it thinks is the original disk file), it will really be closing
- ** the temporary RAM file. This is a good chance for us to get rid of it,
- ** so that RAM: won't get crowded in time. HOWEVER! If the caller has
- ** written new data into the file, we have to rewrite the temporary file
- ** over the original (disk) file. flushout() does exactly that.
- */
- flushout(register struct filenode *fn)
- {
- register BPTR orighandle;
-
- /* First of all, we have to open the original file. We're in
- ** trouble if this is not possible...
- */
- if (orighandle = RealOpen(fn->orig_filename, MODE_NEWFILE))
- {
- char buffer[4096]; /* should suffice when copying */
- register short readlen; /* from a fast device like RAM: */
-
- Seek(fn->filehandle, 0, -1);
-
- do
- {
- readlen = Read(fn->filehandle, buffer, 4096);
- RealWrite(orighandle, buffer, readlen);
- }
- while (readlen == 4096);
-
- RealClose(orighandle);
- }
- }
-
- /* This is the new Open() functions. All future calls to the DOS Open()
- ** function will be rerouted through here.
- */
- BPTR NewOpen(register char *filename, register mode)
- {
- UBYTE *memgot;
- int filelen;
- register BPTR tempfh = (BPTR)0;
- register struct Task *thistask;
- register struct caller *thiscaller;
-
- /* We only deal with a few of the incoming calls:
- **
- ** 1) We can't do anything about new files
- ** 2) Equally, we don't care about CON: or NIL: file open requests
- ** 3) If we have seen the calling task before, the one who is making
- ** the request must be ppLoadData. It is absolutely vital that
- ** we forward this request to the original DOS code. Otherwise
- ** we would end up in an infinite (recursive) loop, with ppLoadData
- ** calling NewOpen calling ppLoadData ...
- ** 4) If we cannot add a caller to the list of callers (see 3), we
- ** ignore the call. If we miss a few in a low memory situation,
- ** so be it.
- */
-
- if
- (
- mode == MODE_NEWFILE ||
- !reallyfile(filename) ||
- findcaller(thistask = FindTask(0)) ||
- !addcaller(thistask)
- )
- return(RealOpen(filename,mode));
-
- /* Now, ask ppLoadData to bring in the file */
- if (!ppLoadData(filename,DECR_NONE,0,&memgot,&filelen,(int (*)())0))
- {
- char filnambuf[40];
- register char *t, *m;
-
- /* Generate a name for the temporary file in RAM */
- t = filename;
- if (m = (char *)index(t,':')) t = m+1;
- while (m = (char *)index(t,'/')) t = m+1;
-
- strcpy(filnambuf,"RAM:");
- strcat(filnambuf,t);
- strcat(filnambuf,".tmp");
-
- /* We have to ensure that the name is unique on RAM: */
- while (tempfh = RealOpen(filnambuf,MODE_OLDFILE))
- {
- char *xtra = "?";
-
- /* Pad the name with random characters. This
- ** should do the trick
- */
- RealClose(tempfh);
- *xtra = 'A' + (rand() % 26);
- strcat(filnambuf,xtra);
- }
-
- /* Now, open the RAM file and flush data we loaded into
- ** this file.
- */
- if (tempfh = RealOpen(filnambuf,MODE_NEWFILE))
- {
- /* Remember that WE created that file */
- if (!addfilenode(tempfh,filnambuf,filename))
- {
- /* Couln't do it. Simulate a "Can't open
- ** file" from the real Open().
- */
- RealClose(tempfh);
- DeleteFile(filnambuf);
- tempfh = (BPTR)0;
- }
- else
- {
- /* Flush out the file. Probably should do
- ** a check on RealWrite... Oh well.
- */
- RealWrite(tempfh,(char *)memgot,filelen);
- Seek(tempfh,0,-1);
- }
- }
- /* Housekeeping */
- FreeMem(memgot,filelen);
- }
-
- /* We no longer have to worry about this caller */
- thiscaller = findcaller(thistask);
- Remove((struct Node *)thiscaller);
- FreeMem(thiscaller, sizeof(*thiscaller));
-
- /* Return a filehandle to the file */
- return(tempfh);
- }
-
- /* Yep! A new Write() function. We have to know if a process has updated
- ** the (substitute) file we created for it. If true, mark the file as
- ** being "dirty", so that we can later save it over the original PP file.
- */
- int NewWrite(register BPTR filehandle, register char *buffer, int length)
- {
- register struct filenode *fn;
- register wrtret;
-
- wrtret = RealWrite(filehandle, buffer, length);
-
- if (fn = findfilenode(filehandle))
- fn->dirty = 1;
-
- return(wrtret);
- }
-
- /* A new Close() function. It removes non-dirty, temporary files from
- ** RAM, and it keeps track of which files have to be updated back onto
- ** disk.
- */
- void NewClose(register BPTR filehandle)
- {
- register struct filenode *fn;
-
- if (fn = findfilenode(filehandle))
- {
- if (fn->dirty)
- flushout(fn);
-
- RealClose(filehandle);
- DeleteFile(fn->ram_filename);
- Remove((struct Node *)fn);
- FreeMem(fn,sizeof(*fn));
- }
- else
- RealClose(filehandle);
- }
-
- /* A new Examine() function. Often, programs examine a file before opening
- ** it. This way, they can allocate just enough memory to hold the entire
- ** file. However, we have to correct Examine() calls to PowerPacked files,
- ** so that the correct amount of memory will be allocated by the caller.
- */
- int NewExamine(BPTR lock, struct FileInfoBlock *fib)
- {
- int ppmatchtag;
- int decrunchinfo;
- register examinereturn;
- register BPTR tmpfh;
- struct FileInfoBlock fib_backup;
-
- /* Start off by examining the lock */
- if (!(examinereturn = RealExamine(lock,fib)))
- return(0);
-
- /* If it's a directory, or if it's pp_LoadData, never mind */
- if (fib->fib_DirEntryType >= 0 || findcaller(FindTask(0)))
- return(examinereturn);
-
- /* The lock target was a simple file. Check to see if it's a
- ** PP file
- */
- fib_backup = *fib;
- tmpfh = RealOpen(fullname(lock,fib),MODE_OLDFILE);
- *fib = fib_backup;
-
- if (!tmpfh)
- return(examinereturn);
-
- Seek(tmpfh,0,-1);
- Read(tmpfh,(char *)&ppmatchtag,sizeof(int));
-
- if (ppmatchtag == PP20 || ppmatchtag == PX20)
- {
- /* It was. Examine decrunchinfo to get at the original
- ** filesize, so that programs trying to allocate enough
- ** memory to hold a certain file will get the correct
- ** filesize (which is, of corz, size of the decrunched
- ** file!)
- */
- Seek(tmpfh,-4,1);
- Read(tmpfh,(char *)&decrunchinfo,sizeof(int));
- fib->fib_Size = decrunchinfo >> 8;
- }
-
- RealClose(tmpfh);
-
- return(examinereturn);
- }
-
- /* Hangaround() simply waits forever for a message from Intuition. When we
- ** get one, it can only be a CLOSEWINDOW request.
- */
- hangaround()
- {
- WaitPort(win->UserPort);
- GetMsg(win->UserPort);
- }
-
- /* Entry point */
- void main()
- {
- openstuff();
- hangaround();
- die(0);
- }
-