home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Elysian Archive
/
AmigaElysianArchive.iso
/
compress
/
ppackerp.lzh
/
PP
/
PP.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-07-31
|
18KB
|
696 lines
/* 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);
}