home *** CD-ROM | disk | FTP | other *** search
/ World of A1200 / World_Of_A1200.iso / programs / compress / misc / pp / pp.c < prev    next >
C/C++ Source or Header  |  1995-02-27  |  18KB  |  696 lines

  1. /* pp_patcher v1.0
  2. **
  3. ** pp_patcher is a small utility which enables programs to get at
  4. ** PowerPacked files, as if these were normal files. This is acomplished
  5. ** by patching some vital DOS functions, thus redirecting calls to these to
  6. ** my own internal routines. The overall effect is that PowerPacked data-
  7. ** files seem to be normal files. You can TYPE them in a CLI window, or
  8. ** bring them directly into your favourite editor. Another good way to
  9. ** use this proggy is if you crunch your workbench icons. Workbench will
  10. ** never know the difference, but it reduces the diskspace occupied by
  11. ** icons by some 65-70% (usually). There is a nominal performance reduction
  12. ** due to the fact that we have to decrunch the icons before passing them
  13. ** on to Workbench, but this doesn't seem too annoying. Especially not if
  14. ** you use optimized (B.A.D.) disks, or the CLI command Addbuffers or
  15. ** equivalent.
  16. **
  17. ** For further info, read the DOC file.
  18. **
  19. ** Compiles under Aztec V5.0 using large code/large data/32 bit integers
  20. ** Should do fine under Lattice, too, if you fix the #pragma's.
  21. **
  22. ** Shareware 1991, copyright (C) 1991 by Michael Berg
  23. **                     Sct. Peders Gade 24A, 2th
  24. **                     8900  Randers
  25. **                     DENMARK
  26. */
  27.  
  28. /* That's right -- ALL of these are necessary! */
  29. #include <functions.h>
  30. #include <exec/nodes.h>
  31. #include <exec/lists.h>
  32. #include <exec/memory.h>
  33. #include <libraries/dos.h>
  34. #include <libraries/dosextens.h>
  35. #include <intuition/intuition.h>
  36. #include "ppbase.h"            /* enclosed in this directory */
  37.  
  38. #asm
  39. DOSLibPatch MACRO
  40.     public    _Real\1
  41.     public    _New\1
  42.     public    _Make\1
  43.     public    _Rest\1
  44.     public    _LVO\1
  45.     public    \2
  46.     cseg
  47.  
  48. _Make\1                ;make a doslib patch
  49.     move.l    a6,-(sp)    ;save regs
  50.     move.l    \2,a6        ;get at the dosbase
  51.     lea    _LVO\1(a6),a6    ;jump vector address
  52.     moveq    #0,d0
  53.     move.w    (a6),Orig\1    ;fetch the 'moveq #?,d0' opcode
  54.     move.w    4(a6),d0    ;fetch the 'bra' offset
  55.     add.l    a6,d0        ;add to find branch target
  56.     addq.l    #4,d0        ;pc relative offset compensation
  57.     move.l    d0,Orig\1+4    ;we need this so we can jump directly
  58.     move.w    #$4ef9,(a6)    ;initiate new sequence: jmp $abs_addrs
  59.     move.l    #_New\1,2(a6)    ;jump to our new function, of corz!
  60.     move.l    (sp)+,a6    ;restore regs
  61.     rts
  62.  
  63.     dseg
  64.     ds    0
  65. _Real\1
  66. Orig\1
  67.     dc.w    0        ;this will be replaced by a moveq
  68.     dc.w    $4ef9,0,0    ;the JMP address will be fixed
  69.  
  70.     cseg
  71. _Rest\1                ;restore a doslib patch
  72.     tst.l    Orig\1+4    ;did we make it at all?
  73.     beq.s    1$        ;nope, don't do anything
  74.     move.l    a6,-(sp)    ;save regs
  75.     move.l    \2,a6        ;get at the dosbase
  76.     lea    _LVO\1(a6),a6    ;find jump vector address
  77.     move.l    Orig\1+4,d0    ;calculate a bra offset to the orig. addrs
  78.     subq.l    #4,d0        ;pc relative offset compensation
  79.     sub.l    a6,d0        ;subtract base address
  80.     move.w    Orig\1,(a6)    ;restore the moveq opcode
  81.     move.w    #$6000,2(a6)    ;opcode for 'bra.l'
  82.     move.w    d0,4(a6)    ;save the branch offset
  83.     move.l    (sp)+,a6    ;restore registers
  84. 1$    rts
  85.     ENDM
  86.  
  87. ;DOSLibPatch is a macro which defines three functions. For Open(), they are:
  88. ;
  89. ;MakeOpen    - Patch the DOS library Open() vector
  90. ;RestOpen    - Restore the DOS library to it's original state
  91. ;RealOpen    - This calls the original DOS function
  92. ;
  93. ;Furthermore, you yourself have to create a function called (in this case)
  94. ;NewOpen, with a parameter specification like Open(). Future calls to Open
  95. ;will be redirected to NewOpen. NewOpen has access to the original DOS code
  96. ;through the function RealOpen. RealOpen should also be defined like Open.
  97. ;More details follow. For now, you need to know that DOSLibPatch works only
  98. ;with the DOS library, which differes in format from other libraries.
  99.  
  100.     DOSLibPatch Open,_DOSBase
  101.     DOSLibPatch Close,_DOSBase
  102.     DOSLibPatch Examine,_DOSBase
  103.     DOSLibPatch Write,_DOSBase
  104. #endasm
  105.  
  106. /* Well, the compiler has the inline-code ability -- why not use them? */
  107. #define strlen    _BUILTIN_strlen
  108. #define strcpy    _BUILTIN_strcpy
  109.  
  110. /* Some defines which make the source code look pretty */
  111. typedef struct IntuitionBase INTUIBASE;
  112. typedef struct GfxBase GFXBASE;
  113. typedef struct PPBase PPBASE;
  114.  
  115. /* A tiny window enables the user to control the program */
  116. struct NewWindow nw =
  117. {
  118.     0,0,320,10,
  119.     -1,-1,
  120.     CLOSEWINDOW,
  121.     WINDOWDRAG | WINDOWCLOSE | WINDOWDEPTH | ACTIVATE,
  122.     NULL,NULL,
  123.     (UBYTE *)"Powerpacker Patcher V1.0",
  124.     NULL,NULL,
  125.     0,0,0,0,
  126.     WBENCHSCREEN
  127. };
  128.  
  129. /* powerpacker.library related functions */
  130. extern int ppLoadData(char *, int, int, UBYTE **, int *, int (*)());
  131. #pragma amicall(PPBase, 0x1e, ppLoadData(a0,d0,d1,a1,a2,a3))
  132.  
  133. /* Open()-patch related functions */
  134. extern void MakeOpen(void), RestOpen(void);
  135. extern BPTR RealOpen(char *, int);
  136. extern BPTR NewOpen(char *, int);
  137. #pragma regcall(RealOpen(d1,d2))    /* All of these PRAGMAs are EXTREMELY */
  138. #pragma regcall(NewOpen(d1,d2))        /* important !!!!!!!!!!              */
  139.  
  140. /* Close()-patch related functions */
  141. extern void MakeClose(void), RestClose(void);
  142. extern void RealClose(BPTR);
  143. extern void NewClose(BPTR);
  144. #pragma regcall(RealClose(d1))
  145. #pragma regcall(NewClose(d1))
  146.  
  147. /* Examine()-patch related functions */
  148. extern void MakeExamine(void), RestExamine(void);
  149. extern int RealExamine(BPTR, struct FileInfoBlock *);
  150. extern int NewExamine(BPTR, struct FileInfoBlock *);
  151. #pragma regcall(RealExamine(d1,d2))
  152. #pragma regcall(NewExamine(d1,d2))
  153.  
  154. /* Write()-patch related functions */
  155. extern void MakeWrite(void), RestWrite(void);
  156. extern int RealWrite(BPTR, char *, int);
  157. extern int NewWrite(BPTR, char *, int);
  158. #pragma regcall(RealWrite(d1,d2,d3))
  159. #pragma regcall(NewWrite(d1,d2,d3))
  160.  
  161. /* Define the maximum length of a filename and its path
  162. ** 256 is, as far as I can tell, the AmigaDOS limit (due to BSTR's).
  163. */
  164. #define MAXPATHLEN    256
  165.  
  166. /* Match tags for PowerPacked files */
  167. #define PP20        (('P' << 24) + ('P' << 16) + ('2' << 8) + '0')
  168. #define PX20        (('P' << 24) + ('X' << 16) + ('2' << 8) + '0')
  169.  
  170. /* One of these for each opened, powerpacked file, which has not yet been
  171. ** closed
  172. */
  173. struct filenode
  174. {
  175.     struct MinNode mn;
  176.  
  177.     BPTR filehandle;
  178.     char *ram_filename, *orig_filename;
  179.     short dirty;
  180. };
  181.  
  182. /* One of these for each caller to NewOpen() */
  183. struct caller
  184. {
  185.     struct MinNode mn;
  186.  
  187.     struct Task *tc;
  188. };
  189.  
  190. /* Global data */
  191. struct MinList templist, callers;
  192. struct Window *win;
  193. struct IntuitionBase *IntuitionBase;
  194. struct GfxBase *GfxBase;
  195. struct PPBase *PPBase;
  196. int patched;
  197.  
  198. /* Exported to detach module */
  199. int _stack = 4000;
  200. int _priority = 5;
  201. char *_procname = "Powerpacker Patcher";
  202. BPTR _BackGroundIO;
  203.  
  204. /* Universal termination code */
  205. void die(int errcode)
  206. {
  207.     /* Restore the original DOS functions. You will not find these
  208.     ** functions in the sourcecode. They are generated by the assembler
  209.     ** macro DOSLibPatch -- see above
  210.     */
  211.     if (patched)
  212.     {
  213.         Forbid();
  214.             RestOpen();
  215.             RestClose();
  216.             RestExamine();
  217.             RestWrite();
  218.         Permit();
  219.     }
  220.  
  221.     /* Close the window */
  222.     if (win) CloseWindow(win);
  223.  
  224.     /* Close libraries */
  225.     if (IntuitionBase)    CloseLibrary(IntuitionBase);
  226.     if (GfxBase)        CloseLibrary(GfxBase);
  227.     if (PPBase)        CloseLibrary(PPBase);
  228.  
  229.     /* Finally! */
  230.     exit(errcode);
  231. }
  232.  
  233. /* Open up all required libraries */
  234. void openlibs()
  235. {
  236.   if
  237.   (
  238.     !(GfxBase        =   (GFXBASE *)OpenLibrary("graphics.library",   0)) ||
  239.     !(IntuitionBase = (INTUIBASE *)OpenLibrary("intuition.library",  0)) ||
  240.     !(PPBase        =    (PPBASE *)OpenLibrary("powerpacker.library",0))
  241.   )
  242.     /* Graphics & Intuition should NEVER fail to open */
  243.     die(10);
  244. }
  245.  
  246. /* Attempt to open the window */
  247. void openwindow()
  248. {
  249.     if (!(win = OpenWindow(&nw)))
  250.         die(20);
  251. }
  252.  
  253. /* Install the DOS patches */
  254. void installpatch()
  255. {
  256.     Forbid();
  257.         MakeOpen();
  258.         MakeClose();
  259.         MakeExamine();
  260.         MakeWrite();
  261.     Permit();
  262.  
  263.     patched = 1;
  264. }
  265.  
  266. /* Some Lists need to be initialized */
  267. void initlist()
  268. {
  269.     NewList((struct List *)&templist);
  270.     NewList((struct List *)&callers);
  271. }
  272.  
  273. /* Open up everything */
  274. void openstuff()
  275. {
  276.     openlibs();
  277.     openwindow();
  278.     initlist();
  279.     installpatch();
  280. }
  281.  
  282. /* Add a new caller to NewOpen() to the list of callers */
  283. addcaller(register struct Task *tc)
  284. {
  285.     register struct caller *memgot;
  286.  
  287.     if (memgot = AllocMem(sizeof(*memgot),0))
  288.     {
  289.         memgot->tc = tc;
  290.         AddTail((struct List *)&callers, (struct Node *)memgot);
  291.         return(1);
  292.     }
  293.     else
  294.         return(0);
  295. }
  296.  
  297. /* Add a file node to the list of files which we have created. These
  298. ** exist temporarily in RAM: and we need to get rid of these along the way,
  299. ** as they are Close()'d.
  300. */
  301. addfilenode(register BPTR fn, register char *filename, register char *orig)
  302. {
  303.     register struct filenode *memgot;
  304.  
  305.     if
  306.     (
  307.         (memgot = AllocMem(sizeof(*memgot),MEMF_CLEAR))         &&
  308.         (memgot->ram_filename  = AllocMem(strlen(filename)+1,0)) &&
  309.         (memgot->orig_filename = AllocMem(strlen(orig)+1,0))
  310.     )
  311.     {
  312.         memgot->filehandle = fn;
  313.         strcpy(memgot->ram_filename,filename);
  314.         strcpy(memgot->orig_filename,orig);
  315.         AddTail((struct List *)&templist, (struct Node *)memgot);
  316.         return(1);
  317.     }
  318.     else
  319.         return(0);
  320. }
  321.  
  322. /* Find a filenode (keyed by its filehandle) */
  323. struct filenode *findfilenode(register BPTR fn)
  324. {
  325.     register struct filenode *search;
  326.  
  327.     /* Linear search is employed */
  328.     for
  329.     (
  330.         search = (struct filenode *)(templist.mlh_Head);
  331.         search->mn.mln_Succ;
  332.         search = (struct filenode *)(search->mn.mln_Succ)
  333.     )
  334.         if (search->filehandle == fn)
  335.             return(search);
  336.  
  337.     return(NULL);
  338. }
  339.  
  340. /* Find a caller on the callers list */
  341. struct caller *findcaller(register struct Task *tc)
  342. {
  343.     register struct caller *search;
  344.  
  345.     for
  346.     (
  347.         search = (struct caller *)(callers.mlh_Head);
  348.         search->mn.mln_Succ;
  349.         search = (struct caller *)(search->mn.mln_Succ)
  350.     )
  351.         if (search->tc == tc)
  352.             return(search);
  353.  
  354.     return(NULL);
  355. }
  356.  
  357. /* This baby builds a complete filename (including a path) from a BCPL
  358. ** pointer to a filehandle. Optimizations are most welcome. All those
  359. ** ParentDir() calls take a LONG time.
  360. */
  361. char *fullname(register BPTR lock, register struct FileInfoBlock *fib)
  362. {
  363.     static char pathandfile[MAXPATHLEN];
  364.     char tmp[MAXPATHLEN];
  365.     register BPTR parentlock, unlocklock;
  366.     register char *co;
  367.  
  368.     strcpy(pathandfile,fib->fib_FileName);
  369.  
  370.     parentlock = lock;
  371.     unlocklock = (BPTR)0;
  372.     while (parentlock=ParentDir(parentlock))
  373.     {
  374.         if (unlocklock)
  375.             UnLock(unlocklock);
  376.  
  377.         if (RealExamine(parentlock,fib))
  378.         {
  379.             strcpy(tmp,fib->fib_FileName);
  380.             strcat(tmp,"/");
  381.             strcat(tmp,pathandfile);
  382.             strcpy(pathandfile,tmp);
  383.         }
  384.         else
  385.         {
  386.             UnLock(parentlock);
  387.             return(NULL);
  388.         }
  389.  
  390.         unlocklock = parentlock;
  391.     }
  392.  
  393.     if (unlocklock)
  394.         UnLock(unlocklock);
  395.  
  396.     if (co = (char *)index(pathandfile,'/'))
  397.         *co = ':';
  398.  
  399.     /* This fixes a bug in the old RAM disk */
  400.     if (!strcmp(pathandfile,":"))
  401.         strcpy(pathandfile,"RAM:");
  402.  
  403.     return(pathandfile);
  404. }
  405.  
  406. /* Is a filename really a file? We need to know this, because it is
  407. ** rediculous to try to read in the PowerPacker matchtag from something
  408. ** like CON:0/0/544/23/ConWindow.
  409. */
  410. reallyfile(register char *filename)
  411. {
  412.     /* Don't like to hard-wire it like this, but it seems to
  413.     ** be the only realistic approach. Don't worry, you can't
  414.     ** do an ASSIGN to any of these. If you could, we would have
  415.     ** to check all volumenodes on the DeviceList stored in
  416.     ** the RootNode of the DosLibrary. (Got that?!)
  417.     */
  418.     static char *duds[] =
  419.     {
  420.         "NIL:",    "CON:",
  421.         "RAW:",    "PRT:",
  422.         "PAR:",    "SER:"
  423.     };
  424.  
  425.     register short i;
  426.  
  427.     /* A simple, linear search is employed */
  428.     for (i = 0; i < sizeof(duds)/sizeof(char *); i++)
  429.     {
  430.         register short foundit;
  431.         register char *cmp, *cmp2;
  432.         register short j;
  433.  
  434.         cmp  = duds[i];
  435.         cmp2 = filename;
  436.  
  437.         for (foundit = 1, j = 0; j < 4; j++)
  438.         {
  439.             if (toupper(*cmp++) != toupper(*cmp2++))
  440.             {
  441.                 foundit = 0;
  442.                 break;
  443.             }
  444.         }
  445.  
  446.         if (foundit)
  447.             return(0);
  448.     }
  449.  
  450.     /* One last check, just to be sure */
  451.     if (!strcmp(filename,"*"))
  452.         return(0);
  453.  
  454.     return(1);
  455. }
  456.  
  457. /* When somebody opens a PP file, we decrunch it into a temporary RAM file
  458. ** and return a filehandle to that file. When the caller closes the file
  459. ** (which it thinks is the original disk file), it will really be closing
  460. ** the temporary RAM file. This is a good chance for us to get rid of it,
  461. ** so that RAM: won't get crowded in time. HOWEVER! If the caller has
  462. ** written new data into the file, we have to rewrite the temporary file
  463. ** over the original (disk) file. flushout() does exactly that.
  464. */
  465. flushout(register struct filenode *fn)
  466. {
  467.     register BPTR orighandle;
  468.  
  469.     /* First of all, we have to open the original file. We're in
  470.     ** trouble if this is not possible...
  471.     */
  472.     if (orighandle = RealOpen(fn->orig_filename, MODE_NEWFILE))
  473.     {
  474.         char buffer[4096];    /* should suffice when copying  */
  475.         register short readlen;    /* from a fast device like RAM: */
  476.  
  477.         Seek(fn->filehandle, 0, -1);
  478.  
  479.         do
  480.         {
  481.             readlen = Read(fn->filehandle, buffer, 4096);
  482.             RealWrite(orighandle, buffer, readlen);
  483.         }
  484.             while (readlen == 4096);
  485.  
  486.         RealClose(orighandle);
  487.     }
  488. }
  489.  
  490. /* This is the new Open() functions. All future calls to the DOS Open()
  491. ** function will be rerouted through here.
  492. */
  493. BPTR NewOpen(register char *filename, register mode)
  494. {
  495.     UBYTE *memgot;
  496.     int filelen;
  497.     register BPTR tempfh = (BPTR)0;
  498.     register struct Task *thistask;
  499.     register struct caller *thiscaller;
  500.  
  501.     /* We only deal with a few of the incoming calls:
  502.     **
  503.     ** 1) We can't do anything about new files
  504.     ** 2) Equally, we don't care about CON: or NIL: file open requests
  505.     ** 3) If we have seen the calling task before, the one who is making
  506.     **    the request must be ppLoadData. It is absolutely vital that
  507.     **    we forward this request to the original DOS code. Otherwise
  508.     **    we would end up in an infinite (recursive) loop, with ppLoadData
  509.     **    calling NewOpen calling ppLoadData ...
  510.     ** 4) If we cannot add a caller to the list of callers (see 3), we
  511.     **    ignore the call. If we miss a few in a low memory situation,
  512.     **    so be it.
  513.     */
  514.  
  515.     if
  516.     (
  517.         mode == MODE_NEWFILE           ||
  518.         !reallyfile(filename)           ||
  519.         findcaller(thistask = FindTask(0)) ||
  520.         !addcaller(thistask)
  521.     )
  522.         return(RealOpen(filename,mode));
  523.  
  524.     /* Now, ask ppLoadData to bring in the file */
  525.     if (!ppLoadData(filename,DECR_NONE,0,&memgot,&filelen,(int (*)())0))
  526.     {
  527.         char filnambuf[40];
  528.         register char *t, *m;
  529.  
  530.         /* Generate a name for the temporary file in RAM */
  531.         t = filename;
  532.         if (m = (char *)index(t,':')) t = m+1;
  533.         while (m = (char *)index(t,'/')) t = m+1;
  534.  
  535.         strcpy(filnambuf,"RAM:");
  536.         strcat(filnambuf,t);
  537.         strcat(filnambuf,".tmp");
  538.  
  539.         /* We have to ensure that the name is unique on RAM: */
  540.         while (tempfh = RealOpen(filnambuf,MODE_OLDFILE))
  541.         {
  542.             char *xtra = "?";
  543.  
  544.             /* Pad the name with random characters. This
  545.             ** should do the trick
  546.             */
  547.             RealClose(tempfh);
  548.             *xtra = 'A' + (rand() % 26);
  549.             strcat(filnambuf,xtra);
  550.         }
  551.  
  552.         /* Now, open the RAM file and flush data we loaded into
  553.         ** this file.
  554.         */
  555.         if (tempfh = RealOpen(filnambuf,MODE_NEWFILE))
  556.         {
  557.             /* Remember that WE created that file */
  558.             if (!addfilenode(tempfh,filnambuf,filename))
  559.             {
  560.                 /* Couln't do it. Simulate a "Can't open
  561.                 ** file" from the real Open().
  562.                 */
  563.                 RealClose(tempfh);
  564.                 DeleteFile(filnambuf);
  565.                 tempfh = (BPTR)0;
  566.             }
  567.             else
  568.             {
  569.                 /* Flush out the file. Probably should do
  570.                 ** a check on RealWrite... Oh well.
  571.                 */
  572.                 RealWrite(tempfh,(char *)memgot,filelen);
  573.                 Seek(tempfh,0,-1);
  574.             }
  575.         }
  576.         /* Housekeeping */
  577.         FreeMem(memgot,filelen);
  578.     }
  579.  
  580.     /* We no longer have to worry about this caller */
  581.     thiscaller = findcaller(thistask);
  582.     Remove((struct Node *)thiscaller);
  583.     FreeMem(thiscaller, sizeof(*thiscaller));
  584.  
  585.     /* Return a filehandle to the file */
  586.     return(tempfh);
  587. }
  588.  
  589. /* Yep! A new Write() function. We have to know if a process has updated
  590. ** the (substitute) file we created for it. If true, mark the file as
  591. ** being "dirty", so that we can later save it over the original PP file.
  592. */
  593. int NewWrite(register BPTR filehandle, register char *buffer, int length)
  594. {
  595.     register struct filenode *fn;
  596.     register wrtret;
  597.  
  598.     wrtret = RealWrite(filehandle, buffer, length);
  599.  
  600.     if (fn = findfilenode(filehandle))
  601.         fn->dirty = 1;
  602.  
  603.     return(wrtret);
  604. }
  605.  
  606. /* A new Close() function. It removes non-dirty, temporary files from
  607. ** RAM, and it keeps track of which files have to be updated back onto
  608. ** disk.
  609. */
  610. void NewClose(register BPTR filehandle)
  611. {
  612.     register struct filenode *fn;
  613.  
  614.     if (fn = findfilenode(filehandle))
  615.     {
  616.         if (fn->dirty)
  617.             flushout(fn);
  618.  
  619.         RealClose(filehandle);
  620.         DeleteFile(fn->ram_filename);
  621.         Remove((struct Node *)fn);
  622.         FreeMem(fn,sizeof(*fn));
  623.     }
  624.     else
  625.         RealClose(filehandle);
  626. }
  627.  
  628. /* A new Examine() function. Often, programs examine a file before opening
  629. ** it. This way, they can allocate just enough memory to hold the entire
  630. ** file. However, we have to correct Examine() calls to PowerPacked files,
  631. ** so that the correct amount of memory will be allocated by the caller.
  632. */
  633. int NewExamine(BPTR lock, struct FileInfoBlock *fib)
  634. {
  635.     int ppmatchtag;
  636.     int decrunchinfo;
  637.     register examinereturn;
  638.     register BPTR tmpfh;
  639.     struct FileInfoBlock fib_backup;
  640.  
  641.     /* Start off by examining the lock */
  642.     if (!(examinereturn = RealExamine(lock,fib)))
  643.         return(0);
  644.  
  645.     /* If it's a directory, or if it's pp_LoadData, never mind */
  646.     if (fib->fib_DirEntryType >= 0 || findcaller(FindTask(0)))
  647.         return(examinereturn);
  648.  
  649.     /* The lock target was a simple file. Check to see if it's a
  650.     ** PP file
  651.     */
  652.     fib_backup = *fib;
  653.     tmpfh       = RealOpen(fullname(lock,fib),MODE_OLDFILE);
  654.     *fib       = fib_backup;
  655.  
  656.     if (!tmpfh)
  657.         return(examinereturn);
  658.  
  659.     Seek(tmpfh,0,-1);
  660.     Read(tmpfh,(char *)&ppmatchtag,sizeof(int));
  661.  
  662.     if (ppmatchtag == PP20 || ppmatchtag == PX20)
  663.     {
  664.         /* It was. Examine decrunchinfo to get at the original
  665.         ** filesize, so that programs trying to allocate enough
  666.         ** memory to hold a certain file will get the correct
  667.         ** filesize (which is, of corz, size of the decrunched
  668.         ** file!)
  669.         */
  670.         Seek(tmpfh,-4,1);
  671.         Read(tmpfh,(char *)&decrunchinfo,sizeof(int));
  672.         fib->fib_Size = decrunchinfo >> 8;
  673.     }
  674.  
  675.     RealClose(tmpfh);
  676.  
  677.     return(examinereturn);
  678. }
  679.  
  680. /* Hangaround() simply waits forever for a message from Intuition. When we
  681. ** get one, it can only be a CLOSEWINDOW request.
  682. */
  683. hangaround()
  684. {
  685.     WaitPort(win->UserPort);
  686.     GetMsg(win->UserPort);
  687. }
  688.  
  689. /* Entry point */
  690. void main()
  691. {
  692.     openstuff();
  693.     hangaround();
  694.     die(0);
  695. }
  696.