home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 18 / amigaformatcd18.iso / -in_the_mag- / emulation / emus / handlers / amicdfilesystem / src / hfs.c < prev    next >
C/C++ Source or Header  |  1997-07-31  |  20KB  |  849 lines

  1. /* hfs.c:
  2.  *
  3.  * Support for the Macintosh HFS filing system.
  4.  *
  5.  * Modified by Nicola Salmoria.
  6.  *
  7.  * ----------------------------------------------------------------------
  8.  * This code is (C) Copyright 1993,1994 by Frank Munkert.
  9.  * All rights reserved.
  10.  * This software may be freely distributed and redistributed for
  11.  * non-commercial purposes, provided this notice is included.
  12.  * ----------------------------------------------------------------------
  13.  * [History removed]
  14.  */
  15.  
  16. #include <stdlib.h>
  17. #include <string.h>
  18.  
  19. #include <dos/dos.h>
  20. #include <proto/exec.h>
  21. #include <proto/dos.h>
  22. #include <proto/utility.h>
  23.  
  24. #include "cdrom.h"
  25. #include "generic.h"
  26. #include "hfs.h"
  27.  
  28.  
  29. typedef struct hfs_vol_info {
  30.   t_mdb            mdb;
  31.   int            start_block;
  32.   t_ulong        root_node;
  33. } t_hfs_vol_info;
  34.  
  35. typedef struct hfs_obj_info {
  36.   t_bool        data_fork;
  37.   t_ulong        parent_id;
  38.   char            name[50];
  39.   t_catalog_record    cat_rec;
  40. } t_hfs_obj_info;
  41.  
  42. typedef struct leaf_record_pos {
  43.   t_ulong        node;
  44.   t_ushort        record;
  45.   t_node_descr        node_descr;
  46.   t_catalog_record    cat_rec;
  47.   t_leaf_record        leaf_rec;
  48.   char            pad[32]; /* space for name from t_leaf_record */
  49. } t_leaf_record_pos;
  50.  
  51. #define VOL(vol,tag) (((t_hfs_vol_info *)(vol->vol_info))->tag)
  52. #define OBJ(obj,tag) (((t_hfs_obj_info *)(obj->obj_info))->tag)
  53.  
  54. /* Number of seconds betweem 01-Jan-1904 and 01-Jan-1978: */
  55.  
  56. #define TIME_DIFF ((74 * 365 + 19) * 24 * 60 * 60)
  57.  
  58. static char g_conv_table[128] = {
  59.   'Ä', 'Å', 'Ç', 'É', 'Ñ', 'Ö', 'Ü', 'á',
  60.   'à', 'â', 'ä', 'ã', 'å', 'ç', 'é', 'è',
  61.   'ê', 'ë', 'í', 'ì', 'î', 'ï', 'ñ', 'ó',
  62.   'ò', 'ô', 'ö', 'õ', 'ú', 'ù', 'û', 'ü',
  63.   '_', '°', '¢', '£', '§', '·', '¶', 'ß',
  64.   '®', '©', '_', '´', '¨', '_', 'Æ', 'Ø',
  65.   '_', '±', '_', '_', '¥', 'µ', 'ð', '_',
  66.   '_', '_', '_', 'ª', 'º', '_', 'æ', 'ø',
  67.   '¿', '¡', '¬', '_', '_', '_', '_', '«',
  68.   '»', '_', ' ', 'À', 'Ã', 'Õ', '_', '_',
  69.   '­', '-', '\"', '\"', '`', '´', '÷', '_',
  70.   'ÿ', '_', '/', '¤', '_', '_', '_', '_',
  71.   '_', '.', '¸', '_', '_', 'Â', 'Ê', 'Á',
  72.   'Ë', 'È', 'Í', 'Î', 'Ï', 'Ì', 'Ó', 'Ô',
  73.   '_', 'Ò', 'Ú', 'Û', 'Ù', '_', '^', '~',
  74.   '­', '_', '·', '°', '_', '\"', '.', '_'
  75. };
  76.  
  77. void Convert_Mac_Characters (char *p_buf, int p_buf_len)
  78. {
  79.   unsigned char *cp = (unsigned char *) p_buf;
  80.   int i;
  81.  
  82.  
  83.   for (i=0; i<p_buf_len; i++, cp++)
  84.     if (*cp >= 128)
  85.       *cp = g_conv_table[*cp-128];
  86.     else if (*cp == ':')
  87.       *cp = '.';
  88.     else if (*cp == '/')
  89.       *cp = '-';
  90.     else if (*cp < ' ')
  91.       *cp = '_';
  92. }
  93.  
  94.  
  95. void Cat_Extension(UBYTE *name,UBYTE *ext)
  96. {
  97. /* make sure that the name doesn't exceed AmigaDOS limit (30 characters) */
  98. name[30 - strlen(ext)] = 0;
  99. strcat(name,ext);
  100. }
  101.  
  102.  
  103. t_uchar *Read_Block (CDROM *p_cdrom, t_ulong p_block)
  104. {
  105.   if (!Read_Sector (p_cdrom, p_block >> 2))
  106.     return NULL;
  107.  
  108.   return p_cdrom->buffer + ((p_block & 3) << 9);
  109. }
  110.  
  111. t_uchar *Read_Contiguous_Blocks (CDROM *p_cdrom, t_ulong p_block,
  112.                  t_ulong p_last_block)
  113. {
  114.   if (!Read_Contiguous_Sectors (p_cdrom, p_block >> 2, p_last_block >> 2))
  115.     return NULL;
  116.  
  117.   return p_cdrom->buffer + ((p_block & 3) << 9);
  118. }
  119.  
  120. /* Convert allocation block number into 512 byte block number.
  121.  */
  122.  
  123. t_ulong AB_to_Block (VOLUME *p_volume, t_ulong p_alloc_block)
  124. {
  125.   return p_alloc_block * (VOL(p_volume,mdb).AlBlkSiz >> 9) +
  126.        VOL(p_volume,mdb).AlBlSt + VOL(p_volume,start_block);
  127. }
  128.  
  129. int HFS_Find_Master_Directory_Block (CDROM *p_cd, t_mdb *p_mdb)
  130. {
  131. t_uchar *block;
  132. typedef struct partition_map {
  133.     t_ushort    pmSig;
  134.     t_ushort    reSigPad;
  135.     t_ulong    pmMapBlkCnt;
  136.     t_ulong    pmPyPartStart;
  137.     t_ulong    pmPartBlkCnt;
  138.     char    pmPartName[32];
  139.     char    pmPartType[32];
  140. } t_partition_map;
  141. int i, entries;
  142. int result = 2;
  143.  
  144.  
  145. block = Read_Block (p_cd, 1);
  146. if (block && block[0] == 'P' && block[1] == 'M')
  147. {
  148.     entries = ((t_partition_map *) block)->pmMapBlkCnt;
  149.     for (i=0; i<entries; i++)
  150.     {
  151.         block = Read_Block (p_cd, i+1);
  152.         if (block && block[0] == 'P' || block[1] == 'M')
  153.         {
  154.             if (memcmp (((t_partition_map *) block)->pmPartType,
  155.                 "Apple_HFS", 9) == 0)
  156.             {
  157.                 result = ((t_partition_map *) block)->pmPyPartStart + 2;
  158.                 break;
  159.             }
  160.         }
  161.     }
  162. }
  163.  
  164. /* don't try to read the other block if the previous read failed */
  165. if (block && (block = Read_Block(p_cd, result)) &&
  166.         block[0] == 'B' && block[1] == 'D')
  167. {
  168.     memcpy (p_mdb, block, sizeof (*p_mdb));
  169.     return result;
  170. }
  171. else return -1;
  172. }
  173.  
  174.  
  175.  
  176. t_bool Uses_HFS_Protocol (CDROM *p_cd, int *p_skip)
  177. {
  178.   t_mdb mdb;
  179.   int blk;
  180.  
  181.  
  182.   SetIoErr(ERROR_NOT_A_DOS_DISK);    /* initialize error code (Read_Sector() */
  183.                                     /* will change it if needed) */
  184.  
  185.   blk = HFS_Find_Master_Directory_Block (p_cd, &mdb);
  186.   if (blk == -1)
  187.     return FALSE;
  188.  
  189.   *p_skip = blk - 2; /* *p_skip holds the start block of the volume */
  190.   return TRUE;
  191. }
  192.  
  193. t_bool HFS_Get_Header_Node (CDROM *p_cd, t_ulong p_mdb_pos,
  194.                 t_mdb *p_mdb, t_hdr_node *p_hdr_node)
  195. {
  196.   t_ulong pos = (p_mdb_pos - 2 + p_mdb->AlBlSt +
  197.            p_mdb->CTExtRec[0].StABN * (p_mdb->AlBlkSiz >> 9));
  198.   t_hdr_node *hdr;
  199.  
  200.   hdr = (t_hdr_node *) Read_Block (p_cd, pos);
  201.   if (!hdr)
  202.     return FALSE;
  203.  
  204.   memcpy (p_hdr_node, hdr, sizeof (t_hdr_node));
  205.   return TRUE;
  206. }
  207.  
  208. t_node_descr *HFS_Get_Node (CDROM *p_cd, t_ulong p_mdb_pos, t_mdb *p_mdb,
  209.                 t_ulong p_node)
  210. {
  211.   t_ulong first_node;
  212.   t_ulong pos;
  213.   t_ulong max = 0;
  214.   t_ulong sub = 0;
  215.   int i;
  216.  
  217.  
  218.   for (i=0; i<3; i++) {
  219.     max += p_mdb->CTExtRec[i].NumABlks * (p_mdb->AlBlkSiz >> 9);
  220.     if (p_node < max)
  221.       break;
  222.     sub = max;
  223.   }
  224.  
  225.   if (i==3)
  226.     return NULL;
  227.  
  228.   first_node = (p_mdb_pos - 2 + p_mdb->AlBlSt +
  229.           p_mdb->CTExtRec[i].StABN * (p_mdb->AlBlkSiz >> 9));
  230.  
  231.   pos = first_node + (p_node - sub);
  232.  
  233.   return (t_node_descr *)Read_Block(p_cd, pos);
  234. }
  235.  
  236. t_node_descr *HFS_Node (VOLUME *p_volume, t_ulong p_node)
  237. {
  238.   return HFS_Get_Node (p_volume->cd, VOL(p_volume,start_block) + 2,
  239.                  &VOL(p_volume,mdb), p_node);
  240. }
  241.  
  242. void HFS_Load_Catalog_Record (VOLUME *p_volume,t_catalog_record *p_cat_rec,
  243.                   char *p_node, int p_record)
  244. {
  245. short *sp = (short *) p_node;
  246. int len;
  247. int start;
  248.  
  249.  
  250. start = sp[255-p_record];
  251. start += p_node[start] + 1;
  252. if (start & 1) start++;
  253.  
  254. len = sp[254-p_record] - start;
  255. memcpy (p_cat_rec, p_node + start, len);
  256.  
  257. if (p_cat_rec->d.type == 3)
  258. {
  259. /* /// there are other fields containing names, but they don't seem to be used??? */
  260.     /* make sure that the name doesn't exceed AmigaDOS limit (30 characters) */
  261.     if (p_cat_rec->dt.CNameLen > 30) p_cat_rec->dt.CNameLen = 30;
  262.  
  263.     /* convert mac characters into Amiga characters */
  264.     Convert_Mac_Characters(p_cat_rec->dt.CName,p_cat_rec->dt.CNameLen);
  265. }
  266. }
  267.  
  268.  
  269. void HFS_Load_Leaf_Record (VOLUME *p_volume,t_leaf_record *p_leaf_rec,
  270.                   char *p_node, int p_pos)
  271. {
  272. memcpy(p_leaf_rec,p_node + p_pos,p_node[p_pos] + 1);
  273.  
  274. /* make sure that the name doesn't exceed AmigaDOS limit (30 characters) */
  275. if (p_leaf_rec->name_length > 30) p_leaf_rec->name_length = 30;
  276.  
  277. /* convert mac characters into Amiga characters */
  278. Convert_Mac_Characters(p_leaf_rec->name,p_leaf_rec->name_length);
  279. }
  280.  
  281.  
  282. t_bool HFS_Find_Next_Leaf (VOLUME *p_volume, t_leaf_record_pos *p_leaf)
  283. {
  284.   t_node_descr *node;
  285.   short *sp;
  286.   int pos;
  287.  
  288.   node = HFS_Node (p_volume, p_leaf->node);
  289.   if (!node)
  290.     return FALSE;
  291.  
  292.   p_leaf->record++;
  293.   if (p_leaf->record == node->NRecs) {
  294.     if (node->FLink) {
  295.       node = HFS_Node (p_volume, p_leaf->node = node->FLink);
  296.       if (!node)
  297.         return FALSE;
  298.       p_leaf->record = 0;
  299.     } else
  300.     {
  301.     SetIoErr(ERROR_NO_MORE_ENTRIES);
  302.       return FALSE;
  303.     }
  304.   }
  305.  
  306.   sp = (short *) node;
  307.   pos = sp[255-p_leaf->record];
  308.   memcpy (&p_leaf->node_descr, node, sizeof (t_node_descr));
  309.   HFS_Load_Leaf_Record(p_volume,&p_leaf->leaf_rec,(char *)node,pos);
  310.   HFS_Load_Catalog_Record(p_volume,&p_leaf->cat_rec, (char *) node, p_leaf->record);
  311.  
  312.   return TRUE;
  313. }
  314.  
  315. /* Find leaf record p_leaf->record in node p_leaf->node.
  316.  * Store the result in *p_leaf.
  317.  */
  318.  
  319. t_bool HFS_Find_This_Leaf (VOLUME *p_volume, t_leaf_record_pos *p_leaf)
  320. {
  321.   t_node_descr *node;
  322.   short *sp;
  323.   int pos;
  324.  
  325.   node = HFS_Node (p_volume, p_leaf->node);
  326.   if (!node)
  327.     return FALSE;
  328.  
  329.   sp = (short *) node;
  330.   pos = sp[255-p_leaf->record];
  331.   memcpy (&p_leaf->node_descr, node, sizeof (t_node_descr));
  332.   HFS_Load_Leaf_Record(p_volume,&p_leaf->leaf_rec,(char *)node,pos);
  333.   HFS_Load_Catalog_Record (p_volume,&p_leaf->cat_rec, (char *) node, p_leaf->record);
  334.  
  335.   return TRUE;
  336. }
  337.  
  338. /* Find the first leaf record with parent ID p_parent_id.
  339.  * If the leaf node is found, TRUE is returned and *p_cat_rec will be
  340.  * loaded with the catalog record. Otherwise, FALSE is returned.
  341.  */
  342.  
  343. t_bool HFS_Find_Leaf_Record (VOLUME *p_volume, t_leaf_record_pos *p_leaf,
  344.                  t_ulong p_parent_id)
  345. {
  346.   t_node_descr *node;
  347.   short *sp;
  348.   int i;
  349.   t_ulong this_node = VOL(p_volume,root_node);
  350.   t_ulong next_node;
  351.  
  352.   node = HFS_Node (p_volume, VOL(p_volume,root_node));
  353.   if (!node) {
  354.     return FALSE;
  355.   }
  356.  
  357.   for (;;) {
  358.     if (node->Type == 0) { /* index node */
  359.       sp = (short *) node;
  360.       next_node = (t_ulong)-1;
  361.       for (i=0; i<node->NRecs; i++) {
  362.         t_idx_record *idx = (t_idx_record *) ((char *) node + sp[255-i]);
  363.     if (idx->length != 0) {
  364.           if (idx->parent_id >= p_parent_id)
  365.         break;
  366.       next_node = idx->pointer;
  367.     }
  368.       }
  369.     } else if (node->Type == 0xff) { /* leaf node */
  370.       break;
  371.     } else {
  372.       SetIoErr(ISOERR_INTERNAL);
  373.       return FALSE;
  374.     }
  375.  
  376.     if (next_node == -1) {
  377.       SetIoErr(ISOERR_INTERNAL);
  378.       return FALSE;
  379.     }
  380.  
  381.     node = HFS_Node (p_volume, next_node);
  382.     if (!node) {
  383.       return FALSE;
  384.     }
  385.     this_node = next_node;
  386.   }
  387.  
  388.   p_leaf->node = this_node;
  389.   p_leaf->record = 0;
  390.   memcpy (&p_leaf->node_descr, node, sizeof (t_node_descr));
  391.   HFS_Load_Leaf_Record(p_volume,&p_leaf->leaf_rec,(char *)node,0xe);
  392.   HFS_Load_Catalog_Record (p_volume,&p_leaf->cat_rec, (char *) node, 0);
  393.  
  394.   /* walk forwards until the same key is found: */
  395.   for (;;) {
  396.     if (p_leaf->leaf_rec.parent_id == p_parent_id)
  397.       return TRUE;
  398.  
  399.     if (p_leaf->leaf_rec.parent_id > p_parent_id) {
  400.       SetIoErr(ISOERR_INTERNAL);
  401.       return FALSE;
  402.     }
  403.  
  404.     if (!HFS_Find_Next_Leaf (p_volume, p_leaf)) {
  405.       SetIoErr(ISOERR_INTERNAL);
  406.       return FALSE;
  407.     }
  408.  
  409.   }
  410. }
  411.  
  412. t_bool HFS_Init_Vol_Info (VOLUME *p_volume, int p_start_block)
  413. {
  414.   extern t_handler g_hfs_handler;
  415.   t_uchar *block;
  416.   t_hdr_node hdr;
  417.  
  418.   /*
  419.    * Volume info initialization:
  420.    */
  421.  
  422.   p_volume->handler = &g_hfs_handler;
  423.  
  424.   p_volume->vol_info = AllocMem (sizeof (t_hfs_vol_info), MEMF_PUBLIC);
  425.   if (!p_volume->vol_info) {
  426.     SetIoErr(ERROR_NO_FREE_STORE);
  427.     return FALSE;
  428.   }
  429.  
  430.   VOL(p_volume,start_block) = p_start_block;
  431.  
  432.   if (!(block = Read_Block (p_volume->cd, p_start_block + 2))) {
  433.     FreeMem (p_volume->vol_info, sizeof (t_hfs_vol_info));
  434.     return FALSE;
  435.   }
  436.  
  437.   memcpy (&VOL(p_volume,mdb), block, sizeof (t_mdb));
  438.  
  439.   if (!HFS_Get_Header_Node (p_volume->cd, p_start_block + 2,
  440.                   &VOL(p_volume,mdb), &hdr)) {
  441.     FreeMem (p_volume->vol_info, sizeof (t_hfs_vol_info));
  442.     return FALSE;
  443.   }
  444.  
  445.   VOL(p_volume,root_node) = hdr.Root;
  446.  
  447.   return TRUE;
  448. }
  449.  
  450. void HFS_Close_Vol_Info (VOLUME *p_volume)
  451. {
  452.   FreeMem (p_volume->vol_info, sizeof (t_hfs_vol_info));
  453. }
  454.  
  455. CDROM_OBJ *HFS_Alloc_Obj (void)
  456. {
  457. CDROM_OBJ *obj;
  458.  
  459.  
  460. if (obj = AllocMem(sizeof(CDROM_OBJ),MEMF_PUBLIC | MEMF_CLEAR))
  461. {
  462.     if (obj->obj_info = AllocMem(sizeof(t_hfs_obj_info),MEMF_PUBLIC | MEMF_CLEAR))
  463.         return(obj);
  464.  
  465.     FreeMem(obj,sizeof(CDROM_OBJ));
  466. }
  467.  
  468. SetIoErr(ERROR_NO_FREE_STORE);
  469. return(NULL);
  470. }
  471.  
  472. CDROM_OBJ *HFS_Open_Top_Level_Directory (VOLUME *p_volume)
  473. {
  474.   CDROM_OBJ *obj = HFS_Alloc_Obj ();
  475.  
  476.   if (!obj)
  477.     return NULL;
  478.  
  479.   obj->directory_f = TRUE;
  480.   obj->handler = p_volume->handler;
  481.  
  482.   OBJ(obj,parent_id) = 1;
  483.   strcpy (OBJ(obj,name), ":");
  484.   OBJ(obj,cat_rec).d.DirID = 2;
  485.  
  486.   return obj;
  487. }
  488.  
  489. CDROM_OBJ *HFS_Open_Object (VOLUME *p_volume, char *p_name, t_ulong p_parent)
  490. {
  491.   t_leaf_record_pos leaf;
  492.   CDROM_OBJ *obj;
  493.   char name[50];
  494.   char type;
  495.   t_bool data_fork;
  496.  
  497.   if (!HFS_Find_Leaf_Record (p_volume, &leaf, p_parent))
  498.     return NULL;
  499.  
  500.   for (;;) {
  501.     type = leaf.cat_rec.d.type;
  502.     if (type == 1 || type == 2) {
  503.       if (leaf.leaf_rec.parent_id != p_parent) {
  504.         SetIoErr(ISOERR_NOT_FOUND);
  505.         return NULL;
  506.       }
  507.  
  508.       memcpy (name, leaf.leaf_rec.name, leaf.leaf_rec.name_length);
  509.       name[leaf.leaf_rec.name_length] = 0;
  510.  
  511.       if (type == 1) {
  512.         if (Stricmp ((UBYTE *) p_name, (UBYTE *) name) == 0)
  513.       break;
  514.       } else {
  515.     if (Stricmp ((UBYTE *) p_name, (UBYTE *) name) == 0) {
  516.       data_fork = TRUE;
  517.       break;
  518.     }
  519.  
  520.     Cat_Extension(name,"²");
  521.     if (Stricmp ((UBYTE *) p_name, (UBYTE *) name) == 0) {
  522.       data_fork = FALSE;
  523.       break;
  524.     }
  525.      }
  526.     }
  527.     if (!HFS_Find_Next_Leaf (p_volume, &leaf))
  528.       return NULL;
  529.   }
  530.  
  531.   obj = HFS_Alloc_Obj ();
  532.   obj->directory_f = (type == 1);
  533.   obj->handler = p_volume->handler;
  534.  
  535.   OBJ(obj,data_fork) = data_fork;
  536.   OBJ(obj,parent_id) = p_parent;
  537.   strcpy (OBJ(obj,name), name);
  538.   memcpy (&OBJ(obj,cat_rec), &leaf.cat_rec, sizeof (t_catalog_record));
  539.  
  540.   return obj;
  541. }
  542.  
  543. /* Open the object with name p_name in the directory p_dir.
  544.  * p_name must not contain '/' or ':' characters.
  545.  */
  546.  
  547. CDROM_OBJ *HFS_Open_Obj_In_Directory (VOLUME *p_volume,CDROM_OBJ *p_dir, char *p_name)
  548. {
  549.   return HFS_Open_Object (p_volume, p_name, OBJ(p_dir,cat_rec).d.DirID);
  550. }
  551.  
  552. CDROM_OBJ *HFS_Find_Parent (VOLUME *p_volume,CDROM_OBJ *p_obj)
  553. {
  554.   t_leaf_record_pos leaf;
  555.  
  556.  
  557.   if (OBJ(p_obj,parent_id) == 2)
  558.     return HFS_Open_Top_Level_Directory (p_volume);
  559.  
  560.   if (!HFS_Find_Leaf_Record (p_volume, &leaf, OBJ(p_obj,parent_id)))
  561.     return NULL;
  562.  
  563.   for (;;) {
  564.     if (leaf.leaf_rec.parent_id != OBJ(p_obj,parent_id)) {
  565.       SetIoErr(ISOERR_INTERNAL);
  566.       return NULL;
  567.     }
  568.     if (leaf.cat_rec.d.type == 3) {
  569.       char buf[50];
  570.  
  571.  
  572.       memcpy (buf, leaf.cat_rec.dt.CName, leaf.cat_rec.dt.CNameLen);
  573.       buf[leaf.cat_rec.dt.CNameLen] = 0;
  574.       return HFS_Open_Object (p_volume, buf, leaf.cat_rec.dt.ParID);
  575.     }
  576.     if (!HFS_Find_Next_Leaf (p_volume, &leaf))
  577.       return NULL;
  578.   }
  579. }
  580.  
  581. void HFS_Close_Obj (CDROM_OBJ *p_obj)
  582. {
  583.   FreeMem (p_obj->obj_info, sizeof (t_hfs_obj_info));
  584.   FreeMem (p_obj, sizeof (CDROM_OBJ));
  585. }
  586.  
  587. int HFS_Read_From_File (VOLUME *p_volume,CDROM_OBJ *p_file, char *p_buffer, int p_buffer_length)
  588. {
  589.   unsigned long block;
  590.   int remain_block, remain_file, remain;
  591.   int len;
  592.   CDROM *cd;
  593.   int pos;
  594.   int buf_pos = 0;
  595.   int todo;
  596.   t_bool data_fork = OBJ(p_file,data_fork);
  597.   t_ulong first_block;
  598.   t_ulong last_block;
  599.   t_ulong data_length = (data_fork ? OBJ(p_file,cat_rec).f.LgLen :
  600.                OBJ(p_file,cat_rec).f.RLgLen);
  601.  
  602.   if (p_file->pos == data_length)
  603.     /* at end of file: */
  604.     return 0;
  605.  
  606.   cd = p_volume->cd;
  607.   first_block = AB_to_Block (p_volume,
  608.                    data_fork ?
  609.                    OBJ(p_file,cat_rec).f.ExtRec[0].StABN :
  610.                  OBJ(p_file,cat_rec).f.RExtRec[0].StABN);
  611.   block = first_block + (p_file->pos >> 9);
  612.   last_block = first_block + ((data_length-1) >> 9);
  613.   todo = p_buffer_length;
  614.  
  615.   while (todo) {
  616.     t_uchar *data;
  617.  
  618.     if (!(data = Read_Contiguous_Blocks (cd, block, last_block))) {
  619.       return -1;
  620.     }
  621.  
  622.     remain_block = 512 - (pos = (p_file->pos & 511));
  623.     remain_file = data_length - p_file->pos;
  624.     remain = (remain_block < remain_file) ? remain_block : remain_file;
  625.     len = (todo < remain) ? todo : remain;
  626.     CopyMem ((APTR) (data + pos), (APTR) (p_buffer + buf_pos), len);
  627.     buf_pos += len;
  628.     p_file->pos += len;
  629.     todo -= len;
  630.  
  631.     if (p_file->pos >= data_length)
  632.       break;
  633.  
  634.     block++;
  635.   }
  636.  
  637.   return buf_pos;
  638. }
  639.  
  640. LONG HFS_Cdrom_Info (VOLUME *p_volume,CDROM_OBJ *p_obj, CDROM_INFO *p_info)
  641. {
  642.   p_info->symlink_f = 0;
  643.   p_info->directory_f = p_obj->directory_f;
  644.   p_info->name_length = strlen (OBJ(p_obj,name));
  645.   memcpy (p_info->name, OBJ(p_obj,name), p_info->name_length);
  646.   if (p_info->directory_f) {
  647.     p_info->file_length = 0;
  648.     p_info->date = OBJ(p_obj,cat_rec).d.CrDat - TIME_DIFF;
  649.   } else {
  650.     if (OBJ(p_obj,data_fork))
  651.       p_info->file_length = OBJ(p_obj,cat_rec).f.LgLen;
  652.     else
  653.       p_info->file_length = OBJ(p_obj,cat_rec).f.RLgLen;
  654.     p_info->date = OBJ(p_obj,cat_rec).f.CrDat - TIME_DIFF;
  655.   }
  656.  
  657.   return(0);
  658. }
  659.  
  660. /*  The 'offset' is a long integer coded like this:
  661.  *
  662.  *  Bit:      |    31 ... 8  | 7   6 | 5   4   3   2   1   0
  663.  *            |              |       |
  664.  *  Contents: |  Node number |  Fork |    Record number
  665.  *
  666.  *  The "Fork" value is encoded as follows:
  667.  *
  668.  *   0 - data fork
  669.  *   1 - resource fork
  670.  *   2 - directory
  671.  *   3 - illegal
  672.  */
  673.  
  674. LONG HFS_Examine_Next (VOLUME *p_volume,CDROM_OBJ *p_obj, CDROM_INFO *p_info,
  675.              unsigned long *p_offset)
  676. {
  677.   t_leaf_record_pos leaf;
  678.   short fork = 3;
  679.  
  680.  
  681.   while (fork == 3) {
  682.     if (*p_offset == 0) {
  683.       if (!HFS_Find_Leaf_Record (p_volume, &leaf, OBJ(p_obj,cat_rec).d.DirID))
  684.         return(IoErr());
  685.     } else {
  686.       leaf.node = *p_offset >> 8;
  687.       leaf.record = *p_offset & 63;
  688.       fork = (*p_offset & 0xC0) >> 6;
  689.  
  690.       if (fork == 0) {
  691.         if (!HFS_Find_This_Leaf (p_volume, &leaf))
  692.       return(IoErr());
  693.       } else {
  694.         if (!HFS_Find_Next_Leaf (p_volume, &leaf))
  695.           return(IoErr());
  696.         if (leaf.leaf_rec.parent_id != OBJ(p_obj,cat_rec).d.DirID)
  697.           return(ERROR_NO_MORE_ENTRIES);
  698.       }
  699.     }
  700.     if (leaf.cat_rec.d.type == 1)
  701.       fork = 2;
  702.     else if (leaf.cat_rec.d.type != 2)
  703.       fork = 3;
  704.     else if (fork != 0 && leaf.cat_rec.f.LgLen > 0)
  705.       fork = 0;
  706.     else if (leaf.cat_rec.f.RLgLen > 0)
  707.       fork = 1;
  708.     else
  709.       fork = 3;
  710.  
  711.     *p_offset = ((leaf.node << 8) + (fork << 6) + leaf.record);
  712.  
  713.   }
  714.  
  715. p_info->symlink_f = 0;
  716. p_info->directory_f = (leaf.cat_rec.d.type == 1);
  717. memcpy(p_info->name,leaf.leaf_rec.name,leaf.leaf_rec.name_length);
  718. p_info->name[leaf.leaf_rec.name_length] = 0;
  719. if (p_info->directory_f)
  720. {
  721.     p_info->file_length = 0;
  722.     p_info->date = leaf.cat_rec.d.CrDat - TIME_DIFF;
  723. }
  724. else
  725. {
  726.     if (fork == 0)
  727.     {
  728.         p_info->file_length = leaf.cat_rec.f.LgLen;
  729.     }
  730.     else
  731.     {
  732.         Cat_Extension(p_info->name,"²");
  733.         p_info->file_length = leaf.cat_rec.f.RLgLen;
  734.     }
  735.     p_info->date = leaf.cat_rec.f.CrDat - TIME_DIFF;
  736. }
  737. p_info->name_length = strlen(p_info->name);
  738. p_info->suppl_info = NULL;
  739.  
  740. return(0);
  741. }
  742.  
  743. void *HFS_Clone_Obj_Info (void *p_info)
  744. {
  745.   t_hfs_obj_info *info = (t_hfs_obj_info *) p_info;
  746.   t_hfs_obj_info *new;
  747.  
  748.   new = AllocMem (sizeof (t_hfs_obj_info), MEMF_PUBLIC);
  749.   if (!new)
  750.     return NULL;
  751.  
  752.   memcpy (new, info, sizeof (t_hfs_obj_info));
  753.  
  754.   return new;
  755. }
  756.  
  757. t_bool HFS_Is_Top_Level_Obj (VOLUME *p_volume,CDROM_OBJ *p_obj)
  758. {
  759.   return OBJ(p_obj,parent_id) == 1;
  760. }
  761.  
  762. t_bool HFS_Same_Objects (CDROM_OBJ *p_obj1, CDROM_OBJ *p_obj2)
  763. {
  764.   if (OBJ(p_obj1,cat_rec).d.type != OBJ(p_obj2,cat_rec).d.type)
  765.     return FALSE;
  766.  
  767.   if (OBJ(p_obj1,cat_rec).d.type == 1)
  768.     return OBJ(p_obj1,cat_rec).d.DirID == OBJ(p_obj2,cat_rec).d.DirID;
  769.   else
  770.     return OBJ(p_obj1,cat_rec).f.FlNum == OBJ(p_obj2,cat_rec).f.FlNum;
  771. }
  772.  
  773. t_ulong HFS_Creation_Date (VOLUME *p_volume)
  774. {
  775.   return VOL(p_volume,mdb).CrDate - TIME_DIFF;
  776. }
  777.  
  778. /* Return volume size in 2048 byte blocks:
  779.  */
  780.  
  781. t_ulong HFS_Volume_Size (VOLUME *p_volume)
  782. {
  783.   return(VOL(p_volume,mdb).NmAlBlks * (VOL(p_volume,mdb).AlBlkSiz >> 9)
  784.          + VOL(p_volume,mdb).AlBlSt + 2);
  785. }
  786.  
  787. /* Return number of free blocks */
  788. t_ulong HFS_Volume_Free (VOLUME *p_volume)
  789. {
  790.   return(VOL(p_volume,mdb).FreeBks * (VOL(p_volume,mdb).AlBlkSiz >> 9)
  791.          + VOL(p_volume,mdb).AlBlSt + 2);
  792. }
  793.  
  794. t_ulong HFS_Block_Size (VOLUME *p_volume)
  795. {
  796.   return 512;
  797. }
  798.  
  799. void HFS_Volume_Id (VOLUME *p_volume, char *p_buf, int p_buf_length)
  800. {
  801.   int len = p_buf_length - 1;
  802.  
  803.   if (len > VOL(p_volume,mdb).VolNameLen)
  804.     len = VOL(p_volume,mdb).VolNameLen;
  805.  
  806.   memcpy (p_buf, VOL(p_volume,mdb).VolName, len);
  807.   p_buf[len] = 0;
  808.  
  809.   Convert_Mac_Characters (p_buf, len);
  810. }
  811.  
  812. t_ulong HFS_Location (VOLUME *p_volume,CDROM_OBJ *p_obj)
  813. {
  814.  if (p_obj->directory_f) {
  815.    if (Is_Top_Level_Object (p_volume,p_obj))
  816.      return FALSE;
  817.    return OBJ(p_obj,cat_rec).d.DirID;
  818.  } else
  819.    return OBJ(p_obj,cat_rec).f.FlNum;
  820. }
  821.  
  822. t_ulong HFS_File_Length (CDROM_OBJ *p_obj)
  823. {
  824.   return (OBJ(p_obj,data_fork) ?
  825.         OBJ(p_obj,cat_rec).f.LgLen :
  826.       OBJ(p_obj,cat_rec).f.RLgLen);
  827. }
  828.  
  829. t_handler g_hfs_handler = {
  830.   HFS_Close_Vol_Info,
  831.   HFS_Open_Top_Level_Directory,
  832.   HFS_Open_Obj_In_Directory,
  833.   HFS_Find_Parent,
  834.   HFS_Close_Obj,
  835.   HFS_Read_From_File,
  836.   HFS_Cdrom_Info,
  837.   HFS_Examine_Next,
  838.   HFS_Clone_Obj_Info,
  839.   HFS_Is_Top_Level_Obj,
  840.   HFS_Same_Objects,
  841.   HFS_Creation_Date,
  842.   HFS_Volume_Size,
  843.   HFS_Volume_Free,
  844.   HFS_Volume_Id,
  845.   HFS_Location,
  846.   HFS_File_Length,
  847.   HFS_Block_Size
  848. };
  849.