home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Utilities / MungeImage 1.2.0 / MungeImage.p < prev    next >
Encoding:
Text File  |  1994-08-15  |  22.5 KB  |  813 lines  |  [TEXT/PJMM]

  1. unit MungeImage;
  2.  
  3. interface
  4.  
  5.     uses
  6.         MungeCommon;
  7.  
  8.     function Main (dctl: DCtlPtr; pb: mungeParamBlockPtr; sel: integer): OSErr;
  9.  
  10. implementation
  11.  
  12.     uses
  13.         Processes, Aliases, FixMath, DartIntf, LZRW1KH, MungeLibs;
  14.  
  15.     const
  16.         chunk_bytes = 20480;                    (* the chunk size which we load (and compress) images *)
  17.         chunk_k = 20;
  18.  
  19.     const
  20.         kLZRW1KHCompress = $42;            (* 'our' compression algorithm *)
  21.  
  22.         max_drive_count = 15;                    (* hardwired limit to the number of floppies you can mount *)
  23.         kOptionKey = 58;
  24.         disk_just_inserted = 1;                    (* some random constants used by the floppy driver *)
  25.         disk_read = 2;                                (* we make some attempt to emulate this behaviour *)
  26.  
  27.     type
  28.         diskCopyHeader = record                (* this record maps on to the first N bytes of a DiskCopy image file *)
  29.                 name: Str63;
  30.                 data_size: longint;
  31.                 tag_size: longint;
  32.                 data_checksum: longint;
  33.                 tag_checksum: longint;
  34.                 unknown: longint;
  35.             end;
  36.  
  37.     const
  38.         dataCRCheaderOffset = 72;            (* offset in the header at which to patch in data CRC when writing DiskCopy images *)
  39.  
  40.     type
  41.         driveRecord = record                    (* a record that holds all the important information about a specific 'drive' *)
  42.                 flags: signedByte;                    (* the following 4 bytes must be in order and immediately in front of dqel *)
  43.                 disk_in_place: signedByte;
  44.                 drive_installed: signedByte;
  45.                 number_of_sides: signedByte;
  46.                 dqel: DrvQEl;
  47.                 image: Ptr;
  48.                 disk_size: longint;
  49.                 readonly: boolean;                    (* disk is mounted read-only, if false then the image must be an uncompressed DiskCopy image *)
  50.                 alias: AliasHandle;                (* an alias to the image file (where a read-write image is written back) *)
  51.                 writeout: boolean;
  52.                 dart_header: HDSrcInfoRec;    (* contains information about the image,  most importantly the compression used *)
  53.             end;
  54.  
  55.     (* disk_in_place is 0 iff (image is nil) *)
  56.  
  57.     type
  58.         iconType = array[0..255] of signedByte;
  59.         physIcon = record
  60.                 phys_icon: iconType;
  61.                 location_str: Str63;
  62.             end;
  63.  
  64.     var
  65.         AlreadyOpen: boolean;                    (* the driver is open *)
  66.         phys_icon: physIcon;
  67.         media_icon: iconType;
  68.         drives: array[1..max_drive_count] of driveRecord;
  69.  
  70. (* The chunk cache is used to reduce the access time by holding one chunk of one disk uncompressed. *)
  71. (* This means that we don't need to uncompress a chunk for each read. If the read is to the same chunk *)
  72. (* as the previous read then the data is already in the chunk_cache *)
  73.  
  74. (* Read-Write disks can't be compressed and hence the chunk_cache is ignored for them. Thus there is *)
  75. (* no cache dirty bit. *)
  76.  
  77. (* The chunk cache is actually DDBLOCKSIZE bytes big because we're not entirely sure how the DART image *)
  78. (* decompression code works. The sample we got allocates a DDBLOCKSIZE buffer and we do the same *)
  79. (* for obvious safety reasons. *)
  80.  
  81.         chunk_cache: Ptr;                            (* pointer to a buffer that holds the cached chunk *)
  82.         cached_drive_ndx: integer;            (* the drive that's being cached *)
  83.         cached_chunk: integer;                    (* the chunk that's being cached *)
  84.  
  85.     const
  86.         Size_Of_Globals = sizeof(AlreadyOpen) + sizeof(phys_icon) * 2 + sizeof(drives) + sizeof(chunk_cache) + sizeof(cached_drive_ndx) + sizeof(cached_chunk) + $100;
  87.  
  88.     procedure SetRegA4 (n: univ Ptr);
  89.     inline
  90.         $285F;
  91.  
  92.     function CreateTrackCache: OSErr;
  93.         var
  94.             err: OSErr;
  95.     begin
  96.         err := noErr;
  97.         if chunk_cache = nil then begin
  98.             chunk_cache := NewPtrSys(DDBLOCKSIZE);
  99.             err := MemError;
  100.             cached_drive_ndx := -1;
  101.         end; (* if *)
  102.         CreateTrackCache := err;
  103.     end; (* CreateTrackCache *)
  104.  
  105.     procedure DestroyTrackCache;
  106.     begin
  107.         DisposePtr(chunk_cache);
  108.         chunk_cache := nil;
  109.     end; (* DestroyTrackCache *)
  110.  
  111.     function CalcChecksum (data: Ptr; datasize: longint): longint;
  112.         type
  113.             bigArray = array[0..123456] of integer;
  114.             bigArrayPtr = ^bigArray;
  115.         var
  116.             i: longint;
  117.             word: integer;
  118.             checksum: longint;
  119.     begin
  120.         if odd(datasize) then begin
  121.             DebugStr('datasize shouldnt be odd!');
  122.         end; (* if *)
  123.         checksum := 0;
  124.         for i := 0 to datasize div 2 - 1 do begin
  125.             word := bigArrayPtr(data)^[i];
  126.             checksum := checksum + band(word, $0000FFFF);
  127.             checksum := brotl(checksum, 31);
  128.         end; (* for *)
  129.         CalcChecksum := checksum;
  130.     end; (* CalcChecksum *)
  131.  
  132. (* ***** Operations on the drive queue and our drives globals ***** *)
  133.  
  134.     function DriveExists (drive_num: integer): boolean;
  135.         var
  136.             cur_el: DrvQElPtr;
  137.     begin
  138.         DriveExists := false;
  139.         cur_el := DrvQElPtr(GetDrvQHdr^.qHead);
  140.         while cur_el <> nil do begin
  141.             if cur_el^.dQDrive = drive_num then begin
  142.                 DriveExists := true;
  143.                 leave;
  144.             end; (* if *)
  145.             cur_el := DrvQElPtr(cur_el^.qLink);
  146.         end; (* while *)
  147.     end; (* DriveExists *)
  148.  
  149.     function FindFreeDriveRecord (var ndx: integer): boolean;
  150.         var
  151.             i: integer;
  152.     begin
  153.         ndx := 0;
  154.         for i := 1 to max_drive_count do begin
  155.             if drives[i].disk_in_place = 0 then begin
  156.                 ndx := i;
  157.                 leave;
  158.             end; (* if *)
  159.         end; (* for *)
  160.         FindFreeDriveRecord := (ndx <> 0);
  161.     end; (* FindFreeDriveRecord *)
  162.  
  163.     function DriveToDriveRecord (drive_num: integer; var ndx: integer): OSErr;
  164.         var
  165.             i: integer;
  166.     begin
  167.         ndx := 0;
  168.         for i := 1 to max_drive_count do begin
  169.             if (drives[i].disk_in_place <> 0) and (drives[i].dqel.dQDrive = drive_num) then begin
  170.                 ndx := i;
  171.                 leave;
  172.             end; (* if *)
  173.         end; (* for *)
  174.         if ndx = 0 then begin
  175.             DriveToDriveRecord := nsDrvErr;
  176.         end
  177.         else begin
  178.             DriveToDriveRecord := noErr;
  179.         end; (* if *)
  180.     end; (* DriveToDriveRecord *)
  181.  
  182.     function AnyDriveRecordInUse: boolean;
  183.         var
  184.             i: integer;
  185.     begin
  186.         AnyDriveRecordInUse := false;
  187.         for i := 1 to max_drive_count do begin
  188.             if drives[i].disk_in_place <> 0 then begin
  189.                 AnyDriveRecordInUse := true;
  190.                 leave;
  191.             end; (* if *)
  192.         end; (* for *)
  193.     end; (* AnyDriveRecordInUse *)
  194.  
  195.     procedure CreateDriveRecord (ndx: integer);
  196.     begin
  197.         drives[ndx].alias := nil;
  198.         drives[ndx].image := nil;
  199.         drives[ndx].disk_in_place := disk_just_inserted;
  200.         drives[ndx].writeout := false;
  201.     end; (* CreateDriveRecord *)
  202.  
  203.     procedure DestroyDriveRecord (ndx: integer);
  204.     begin
  205.         if drives[ndx].alias <> nil then begin
  206.             DisposeHandle(handle(drives[ndx].alias));
  207.         end;
  208.         if drives[ndx].image <> nil then begin
  209.             DisposePtr(drives[ndx].image);
  210.         end;
  211.         drives[ndx].image := nil;
  212.         drives[ndx].disk_in_place := 0;
  213.         drives[ndx].writeout := false;
  214.         if not AnyDriveRecordInUse then begin
  215.             DestroyTrackCache;
  216.         end; (* if *)
  217.     end; (* DestroyDriveRecord *)
  218.  
  219.     function ChunkIndexToOffset (var dart_header: HDSrcInfoRec; blockIdx: integer; var offset: longint): OSErr;
  220.         var
  221.             err: OSErr;
  222.             block: integer;
  223.             blockLen: integer;
  224.     begin
  225.         err := noErr;
  226.         offset := 0;
  227.         for block := 1 to blockIdx - 1 do begin
  228.             blockLen := dart_header.bLength[block];
  229.             if (blockLen = -1) then begin
  230.                 blockLen := DDBLOCKSIZE;
  231.             end
  232.             else begin
  233.                 if (dart_header.srcCmp = kRLECompress) then begin
  234.                     blockLen := blockLen * 2;
  235.                 end; (* if *)
  236.             end; (* if *)
  237.             if not ((blockLen > 0) and (blockLen <= DDBLOCKSIZE)) then begin
  238.                 err := paramErr; (* oops! bogus block length encountered *)
  239.             end; (* if *)
  240.             offset := offset + blockLen;
  241.         end; (* for *)
  242.         ChunkIndexToOffset := err;
  243.     end; (* ChunkIndexToOffset *)
  244.  
  245.     function DecompressChunk (var dart_header: HDSrcInfoRec; image, outBlock: Ptr; blockIdx: integer): OSErr;
  246.         var
  247.             err: OSErr;
  248.             offset: longint;
  249.             junk: BufferSize;
  250.     begin
  251.         err := ChunkIndexToOffset(dart_header, blockIdx, offset);
  252.         if err = noErr then begin
  253.             image := Ptr(longint(image) + offset);
  254.             if (dart_header.bLength[blockIdx] <> -1) then begin
  255.                 case dart_header.srcCmp of
  256.                     kRLECompress: 
  257.                         err := RLEExpandBlock(image, DDPtr(outBlock), dart_header.bLength[blockIdx]);
  258.                     kLZHCompress: 
  259.                         err := LZHExpandBlock(image, DDPtr(outBlock), dart_header.bLength[blockIdx]);
  260.                     kLZRW1KHCompress: 
  261.                         junk := LZRW1KHDecompress(BufferPtr(image), BufferPtr(outBlock), dart_header.bLength[blockIdx]);
  262.                     kNoCompress: 
  263.                         BlockMove(image, outBlock, dart_header.bLength[blockIdx]);
  264.                     otherwise
  265.                         err := paramErr;
  266.                 end; (* case *)
  267.             end
  268.             else begin
  269.                 BlockMove(image, outBlock, chunk_bytes);
  270.             end; (* if *)
  271.         end; (* if *)
  272.         DecompressChunk := err;
  273.     end; (* DecompressChunk *)
  274.  
  275.     function Main (dctl: DCtlPtr; pb: mungeParamBlockPtr; sel: integer): OSErr;
  276.  
  277.         function DoOpen: OSErr;
  278.             var
  279.                 err: OSErr;
  280.                 i: integer;
  281.                 junk: OSErr;
  282.         begin
  283.             err := noErr;
  284.             if dctl^.dCtlStorage = nil then begin
  285.                 dctl^.dCtlStorage := NewHandleSysClear(Size_Of_Globals);
  286.                 err := MemError;
  287.                 if err = noErr then begin
  288.                     HLock(dctl^.dCtlStorage);
  289.                     SetRegA4(dctl^.dCtlStorage^);
  290.                 end;
  291.             end;
  292.             if (err = noErr) & not AlreadyOpen then begin
  293.                 AlreadyOpen := true;
  294.             end;
  295.             if (err = noErr) then begin
  296.                 phys_icon.location_str := GetString(128)^^;
  297.                 BlockMove(GetResource('ICN#', 200)^, @phys_icon.phys_icon, sizeof(iconType));
  298.                 BlockMove(GetResource('ICN#', 201)^, @media_icon, sizeof(media_icon));
  299.                 for i := 1 to max_drive_count do begin
  300.                     drives[i].disk_in_place := 0;
  301.                     drives[i].image := nil;
  302.                 end; (* for *)
  303.             end; (* if *)
  304.             chunk_cache := nil;
  305.             DoOpen := err;
  306.         end; (* DoOpen *)
  307.  
  308.         function CallProgressProc (pb: mungeParamBlockPtr; proc: ProcPtr): OSErr;
  309.         inline
  310.             $205F, (* move.l    (a7)+,a0        ; pop proc address        *)
  311.             $4E90; (* jsr            (a0)            ; call proc                    *)
  312.  
  313.         function CallProgress (progress_done, progress_total: longint): OSErr;
  314.             var
  315.                 err: OSErr;
  316.         begin
  317.             err := noErr;
  318.             if pb^.progress <> nil then begin
  319.                 pb^.progress_done := FracDiv(progress_done, progress_total);
  320.                 err := CallProgressProc(pb, pb^.progress);
  321.             end; (* if *)
  322.             CallProgress := err;
  323.         end; (* CallProgress *)
  324.  
  325.         function ReadDARTFile (ndx: integer; refnum: integer): OSErr;
  326.             var
  327.                 err: OSErr;
  328.                 i: integer;
  329.                 total_chunks: integer;
  330.                 file_size: longint;
  331.                 count: longint;
  332.                 offset: longint;
  333.                 bytes: longint;
  334.         begin
  335.             err := FSReadQ(refnum, sizeof(drives[ndx].dart_header), @drives[ndx].dart_header);
  336.  
  337.             if err = noErr then begin
  338.                 if not (drives[ndx].dart_header.srcType in [kMacHiDDisk, kMSDOSHiDDisk]) then begin
  339.                     err := SetFPos(refnum, fsFromStart, sizeof(SrcInfoRec));
  340.                 end; (* if *)
  341.             end; (* if *)
  342.  
  343.             if err = noErr then begin
  344.                 total_chunks := drives[ndx].dart_header.srcSize div chunk_k;
  345.                 if total_chunks > 72 then begin
  346.                     err := paramErr;
  347.                 end; (* if *)
  348.             end; (* if *)
  349.             if err = noErr then begin
  350.                 err := ChunkIndexToOffset(drives[ndx].dart_header, total_chunks + 1, file_size);
  351.             end; (* if *)
  352.             if err = noErr then begin
  353.                 drives[ndx].disk_size := longint(drives[ndx].dart_header.srcSize) * 1024;
  354.                 drives[ndx].image := NewPtrSys(file_size);
  355.                 err := MemError;
  356.             end; (* if *)
  357.             if err = noErr then begin
  358.                 offset := 0;
  359.                 while (offset < file_size) and (err = noErr) do begin
  360.                     bytes := file_size - offset;
  361.                     if bytes > chunk_bytes then begin
  362.                         bytes := chunk_bytes;
  363.                     end; (* if *)
  364.                     err := FSReadQ(refnum, bytes, Ptr(longint(drives[ndx].image) + offset));
  365.                     offset := offset + bytes;
  366.                     if err = noErr then begin
  367.                         err := CallProgress(offset, file_size);
  368.                     end; (* if *)
  369.                 end; (* while *)
  370.             end; (* if *)
  371.             ReadDARTFile := err;
  372.         end; (* ReadDARTFile *)
  373.  
  374.         function ReadDiskCopyFile (ndx: integer; refnum: integer; readonly: boolean): OSErr;
  375.             var
  376.                 err: OSErr;
  377.                 header: diskCopyHeader;
  378.                 i: integer;
  379.                 offset: longint;
  380.                 compressed_size: integer;
  381.         begin
  382.             err := FSReadQ(refnum, sizeof(header), @header);
  383.             if err = noErr then begin
  384.                 if header.data_size div chunk_bytes > 72 then begin
  385.                     err := paramErr;
  386.                 end; (* if *)
  387.             end; (* if *)
  388.             if err = noErr then begin
  389.                 drives[ndx].disk_size := header.data_size;
  390.                 drives[ndx].image := NewPtrSys(drives[ndx].disk_size + 72);
  391.                 err := MemError;
  392.             end; (* if *)
  393.             if err = noErr then begin
  394.                 cached_drive_ndx := -1;
  395.                 offset := 0;
  396.                 for i := 1 to header.data_size div chunk_bytes do begin
  397.                     err := FSReadQ(refnum, chunk_bytes, chunk_cache);
  398.                     if err = noErr then begin
  399.                         if readonly then begin
  400.                             compressed_size := LZRW1KHCompress(BufferPtr(chunk_cache), BufferPtr(longint(drives[ndx].image) + offset), chunk_bytes);
  401.                         end
  402.                         else begin
  403.                             BlockMove(chunk_cache, Ptr(longint(drives[ndx].image) + offset), chunk_bytes);
  404.                             compressed_size := chunk_bytes;
  405.                         end; (* if *)
  406.                         drives[ndx].dart_header.bLength[i] := compressed_size;
  407.                         offset := offset + compressed_size;
  408.                     end; (* if *)
  409.                     if err = noErr then begin
  410.                         err := CallProgress(i, header.data_size div chunk_bytes);
  411.                     end; (* if *)
  412.                     if err <> noErr then begin
  413.                         leave;
  414.                     end; (* if *)
  415.                 end; (* for *)
  416.                 if err = noErr then begin
  417.                     SetPtrSize(drives[ndx].image, offset);
  418.                     err := MemError;
  419.                     if err <> noErr then begin
  420.                         DebugStr('Pete is extremely skeptical!');
  421.                     end; (* if *)
  422.                 end; (* if *)
  423.             end; (* if *)
  424.             if err = noErr then begin
  425.                 with drives[ndx].dart_header do begin (* safe *)
  426.                     if readonly then begin
  427.                         srcCmp := kLZRW1KHCompress;
  428.                     end
  429.                     else begin
  430.                         srcCmp := kNoCompress;
  431.                     end; (* if *)
  432.                     srcType := 0;
  433.                     srcSize := header.data_size div 1024;
  434.                 end; (* with *)
  435.             end; (* if *)
  436.             ReadDiskCopyFile := err;
  437.         end; (* ReadDiskCopyFile *)
  438.  
  439.         function MountImage: OSErr;
  440.             var
  441.                 err: OSErr;
  442.                 junk: OSErr;
  443.                 ndx: integer;
  444.                 refnum: integer;
  445.                 drive_num: integer;
  446.                 readonly: boolean;
  447.                 oldzone: THz;
  448.                 file_info: FInfo;
  449.         begin
  450.             readonly := not btst(pb^.mount_flags, mf_read_write);
  451.             err := CreateTrackCache;
  452.             ndx := 0;        (* important safety tip! *)
  453.             if not FindFreeDriveRecord(ndx) then begin
  454.                 err := -666;
  455.             end
  456.             else begin
  457.                 CreateDriveRecord(ndx);
  458.                 if err = noErr then begin
  459.                     err := FSpOpenDF(pb^.file_to_mount^, fsRdPerm, refnum);
  460.                     if err = noErr then begin
  461.                         err := FSpGetFInfo(pb^.file_to_mount^, file_info);
  462.  
  463.                         if err = noErr then begin
  464.                             if file_info.fdType = kDiskCopyType then begin
  465.                                 err := ReadDiskCopyFile(ndx, refnum, readonly);
  466.                             end
  467.                             else begin
  468.                                 err := ReadDARTFile(ndx, refnum);
  469.                                 readonly := true;
  470.                             end;
  471.                         end; (* if *)
  472.  
  473.                         junk := FSClose(refnum);
  474.                     end;
  475.                 end; (* if *)
  476.  
  477.                 if not readonly then begin
  478.                     oldzone := GetZone;
  479.                     SetZone(SystemZone);
  480.                     if NewAlias(nil, pb^.file_to_mount^, drives[ndx].alias) <> noErr then begin
  481.                         drives[ndx].alias := nil;
  482.                     end; (* if *)
  483.                     SetZone(oldzone);
  484.                 end;
  485.  
  486.                 if err = noErr then begin (* mount the image *)
  487.                     drive_num := 4;
  488.                     while DriveExists(drive_num) do begin
  489.                         drive_num := drive_num + 1;
  490.                     end; (* while *)
  491.                     drives[ndx].readonly := readonly;
  492.                     drives[ndx].flags := signedByte($80 * ord(readonly));
  493.                     drives[ndx].drive_installed := 0;
  494.                     drives[ndx].number_of_sides := 0;            (* ? should set to 0 for 400K disk images*)
  495.                     drives[ndx].dqel.qType := 1;
  496.                     drives[ndx].dqel.dQDrive := drive_num;
  497.                     drives[ndx].dqel.dQRefNum := pb^.ioCRefNum;
  498.                     drives[ndx].dqel.dQFSID := 0;
  499.                     drives[ndx].dqel.dQDrvSz := drives[ndx].disk_size div 512;
  500.                     drives[ndx].dqel.dQDrvSz2 := 0;
  501.                     AddDrive(dctl^.dCtlRefNum, drive_num, @drives[ndx].dqel);
  502.                     junk := PostEvent(diskEvt, drive_num);
  503.                 end
  504.                 else begin
  505.                     DestroyDriveRecord(ndx);
  506.                 end; (* if *)
  507.                 InitCursor;
  508.             end; (* if *)
  509.             MountImage := err;
  510.         end; (* MountImage *)
  511.  
  512.         procedure WriteBack (ndx: integer);
  513.             var
  514.                 err: OSErr;
  515.                 aliascount: integer;
  516.                 fss: array[1..2] of FSSPec;
  517.                 needsUpdate: boolean;
  518.                 rn: integer;
  519.                 crc: longInt;
  520.         begin
  521.             aliascount := 2;
  522.             err := MatchAlias(nil, kARMNoUI + kARMSearch, drives[ndx].alias, aliascount, @fss, needsUpdate, nil, nil);
  523.             if (err = noErr) & (aliascount <> 1) then begin
  524.                 err := -1;
  525.             end; (* if *)
  526.             if err = noErr then begin
  527.                 err := FSpOpenDF(fss[1], fsRdWrPerm, rn);
  528.                 if err = noErr then begin
  529.                     err := MyFSWriteAt(rn, fsFromStart, SizeOf(diskCopyHeader), drives[ndx].disk_size, drives[ndx].image);
  530.                     if err = noErr then begin
  531.                         crc := CalcChecksum(drives[ndx].image, drives[ndx].disk_size);
  532.                         err := MyFSWriteAt(rn, fsFromStart, dataCRCheaderOffset, SizeOf(crc), @crc);
  533.                     end; (* if *)
  534.                     err := FSClose(rn);
  535.                 end; (* if *)
  536.             end; (* if *)
  537.         end; (* WriteBack *)
  538.  
  539.         function UnMountImage: OSErr;
  540.             var
  541.                 err: OSErr;
  542.                 junk: OSErr;
  543.                 ndx: integer;
  544.         begin
  545.             err := DriveToDriveRecord(pb^.ioVRefNum, ndx);
  546.             if err = noErr then begin
  547.                 if ndx = cached_drive_ndx then begin
  548.                     cached_drive_ndx := -1;
  549.                 end; (* if *)
  550.                 if DeQueue(@drives[ndx].dqel, GetDrvQHdr) <> noErr then begin
  551.                     DebugStr('Hmm, removing a non-existant drive');
  552.                 end; (* if *)
  553.                 if not drives[ndx].readonly then begin
  554.                     drives[ndx].writeout := true;
  555.                 end
  556.                 else begin
  557.                     DestroyDriveRecord(ndx);
  558.                 end;
  559.             end; (* if *)
  560.             UnMountImage := err;
  561.         end; (* UnMountImage *)
  562.  
  563.         function DoClose: OSErr;
  564.             var
  565.                 err: OSErr;
  566.         begin
  567.             if AnyDriveRecordInUse then begin
  568.                 err := closErr;
  569.             end
  570.             else begin
  571.                 if chunk_cache <> nil then begin
  572.                     DebugStr('Pete lied!');
  573.                 end; (* if *)
  574.                 err := noErr;
  575.             end; (* if *)
  576.             DoClose := err;
  577.         end; (* DoClose *)
  578.  
  579.         function ReadCachedChunk (ndx: integer; chunk: integer): OSErr;
  580.             var
  581.                 err: OSErr;
  582.         begin
  583.             err := noErr;
  584.             if (ndx <> cached_drive_ndx) or (chunk <> cached_chunk) then begin
  585.                 err := DecompressChunk(drives[ndx].dart_header, drives[ndx].image, chunk_cache, chunk);
  586.                 if err = noErr then begin
  587.                     cached_chunk := chunk;
  588.                     cached_drive_ndx := ndx;
  589.                 end
  590.                 else begin
  591.                     cached_drive_ndx := -1;
  592.                 end; (* if *)
  593.             end; (* if *)
  594.             ReadCachedChunk := err;
  595.         end; (* ReadCachedChunk *)
  596.  
  597.         function ReadCached (ndx: integer; offset: longint; buffer: Ptr; count: longint): OSErr;
  598.             var
  599.                 err: OSErr;
  600.                 cur_chunk: integer;
  601.                 bytes_in_this_chunk: longint;
  602.                 bytes_to_do: longint;
  603.         begin
  604.             err := noErr;
  605.             while (count > 0) and (err = noErr) do begin
  606.                 cur_chunk := offset div chunk_bytes + 1;
  607.                 err := ReadCachedChunk(ndx, cur_chunk);
  608.                 if err = noErr then begin
  609.                     bytes_in_this_chunk := longint(cur_chunk) * chunk_bytes - offset;
  610.                     if bytes_in_this_chunk > count then begin
  611.                         bytes_to_do := count;
  612.                     end
  613.                     else begin
  614.                         bytes_to_do := bytes_in_this_chunk;
  615.                     end; (* if *)
  616.                     BlockMove(Ptr(longint(chunk_cache) + chunk_bytes - bytes_in_this_chunk), buffer, bytes_to_do);
  617.                     offset := offset + bytes_to_do;
  618.                     buffer := Ptr(longint(buffer) + bytes_to_do);
  619.                     count := count - bytes_to_do;
  620.                 end; (* if *)
  621.             end; (* while *)
  622.             ReadCached := err;
  623.         end; (* ReadCached *)
  624.  
  625.         function DoPrime: OSErr;
  626.             var
  627.                 err: OSErr;
  628.                 offset: longint;
  629.                 ndx: integer;
  630.         begin
  631.             err := DriveToDriveRecord(pb^.ioVRefNum, ndx);
  632.             if err = noErr then begin
  633.                 offset := dctl^.dCtlPosition;
  634.                 if (offset < 0) or (pb^.ioReqCount < 0) or (offset + pb^.ioReqCount > drives[ndx].disk_size) then begin
  635.                     pb^.ioActCount := 0;
  636.                     err := paramErr;
  637.                 end
  638.                 else begin
  639.                     err := noErr;
  640.                     pb^.ioActCount := 0;
  641.                     if odd(pb^.ioTrap) then begin
  642.                         (* write *)
  643.                         if drives[ndx].readonly then begin
  644.                             err := wPrErr;
  645.                         end
  646.                         else begin
  647.                             BlockMove(pb^.ioBuffer, Ptr(ord(drives[ndx].image) + offset), pb^.ioReqCount);
  648.                         end;
  649.                     end
  650.                     else begin
  651.                         (* read *)
  652.                         drives[ndx].disk_in_place := disk_read;
  653.                         if drives[ndx].readonly then begin
  654.                             err := ReadCached(ndx, offset, pb^.ioBuffer, pb^.ioReqCount);
  655.                         end
  656.                         else begin
  657.                             (* read-write disks are always uncompressed (and hence uncached) *)
  658.                             BlockMove(Ptr(ord(drives[ndx].image) + offset), pb^.ioBuffer, pb^.ioReqCount);
  659.                         end; (* if *)
  660.                     end; (* if *)
  661.                     if err = noErr then begin
  662.                         pb^.ioActCount := pb^.ioReqCount;
  663.                         dctl^.dCtlPosition := dctl^.dCtlPosition + pb^.ioActCount;
  664.                     end;
  665.                 end; (* if *)
  666.             end; (* if *)
  667.             DoPrime := err;
  668.         end; (* DoPrime *)
  669.  
  670.         procedure DoAccRun;
  671.             var
  672.                 i: integer;
  673.         begin
  674.             for i := 1 to max_drive_count do begin
  675.                 if drives[i].disk_in_place <> 0 then begin
  676.                     if drives[i].writeout then begin
  677.                         WriteBack(i);
  678.                         DestroyDriveRecord(i);
  679.                     end;
  680.                 end;
  681.             end;
  682.         end;
  683.  
  684.         function DoControl: OSErr;
  685.             const
  686.                 super_drive_info = $00000404;
  687.             var
  688.                 err: OSErr;
  689.                 keys: KeyMap;
  690.         begin
  691.             case pb^.csCode of
  692.                 1: 
  693.                     err := -1; (* KillIO *)
  694.                 5: 
  695.                     err := noErr; (* Verify Disk *)
  696.                 6: 
  697.                     err := noErr; (* Format Disk *)
  698.                 7: 
  699.                     err := UnMountImage; (* Eject Disk *)
  700.                 8: 
  701.                     if pb^.ioMisc = nil then begin    (* Set Tag Buffer *)
  702.                         err := noErr;
  703.                     end
  704.                     else begin
  705.                         err := -1;
  706.                     end; (* if *)
  707.                 9: 
  708.                     err := -1; (* Track Cache Control *)
  709.                 21:  begin    (* Return Physical Icon *)
  710.                     pb^.ioMisc := @phys_icon;
  711.                     err := noErr;
  712.                 end;
  713.                 22:  begin    (* Return Media Icon *)
  714.                     pb^.ioMisc := @media_icon;
  715.                     err := noErr;
  716.                 end;
  717.                 23:  begin (* Return Drive Info *)
  718.                     pb^.ioMisc := Ptr(super_drive_info);
  719.                     err := noErr;
  720.                 end;
  721.                 accRun: 
  722.                     DoAccRun;
  723.                 csMountImage:  begin
  724.                     pb^.progress := nil;
  725.                     pb^.mount_flags := 0;
  726.                     GetKeys(keys);
  727.                     if keys[kOptionKey] then begin
  728.                         bset(pb^.mount_flags, mf_read_write);
  729.                     end; (* if *)
  730.                     err := MountImage;
  731.                 end;
  732.                 csMountImageWithProgress:  begin
  733.                     err := MountImage;
  734.                 end;
  735.                 667: 
  736.                     err := noErr;
  737.                 18244:  begin
  738.                     err := -1;
  739.                 end;
  740.                 otherwise
  741.                     err := controlErr;
  742.             end; (* case *)
  743.             DoControl := err;
  744.         end; (* DoControl *)
  745.  
  746.         function DoStatus: OSErr;
  747.             const
  748.                 mfm_1440_capacity = 1440 * 2;
  749.                 mfm_1440_stuff = $D2120050;
  750.             var
  751.                 err: OSErr;
  752.                 ndx: integer;
  753.         begin
  754.             case pb^.csCode of
  755.                 6:  begin (* Return Format List *)
  756.                     err := DriveToDriveRecord(pb^.ioVRefNum, ndx);
  757.                     if err = noErr then begin
  758.                         with pb^ do begin
  759.                             if format_count > 0 then begin
  760.                                 format_count := 1;
  761.                                 format_point^.capacity := drives[ndx].disk_size div 512;
  762.                                 format_point^.stuff := 0;
  763.                                 err := noErr;
  764.                             end
  765.                             else begin
  766.                                 err := paramErr;
  767.                             end; (* if *)
  768.                         end; (* with *)
  769.                     end; (* if *)
  770.                 end;
  771.                 8:  begin
  772.                     err := DriveToDriveRecord(pb^.ioVRefNum, ndx);
  773.                     if err = noErr then begin
  774.                         with pb^ do begin
  775.                             status_current_track := 0;
  776.                             status_flags := drives[ndx].flags;
  777.                             status_disk_in_place := drives[ndx].disk_in_place;
  778.                             status_drive_installed := drives[ndx].drive_installed;
  779.                             status_number_of_sides := drives[ndx].number_of_sides;
  780.                             status_dqel := drives[ndx].dqel;
  781.                             status_dqel.dQDrvSz := -1;
  782.                             status_dqel.dQDrvSz2 := 0;
  783.                         end; (* with *)
  784.                         err := noErr;
  785.                     end; (* if *)
  786.                 end;
  787.                 otherwise
  788.                     err := controlErr;
  789.             end; (* case *)
  790.             DoStatus := err;
  791.         end; (* DoStatus *)
  792.  
  793.         var
  794.             err: OSErr;
  795.     begin
  796.         case sel of
  797.             0: 
  798.                 err := DoOpen;
  799.             1: 
  800.                 err := DoPrime;
  801.             2: 
  802.                 err := DoControl;
  803.             3: 
  804.                 err := DoStatus;
  805.             4: 
  806.                 err := DoClose;
  807.             otherwise
  808.                 err := noErr;
  809.         end; (* case *)
  810.         Main := err;
  811.     end; (* Main *)
  812.  
  813. end. (* MungeImage *)