/* generic.c:
 *
 * Generic interface to the protocol handlers.
 *
 * Modified by Nicola Salmoria.
 *
 * ----------------------------------------------------------------------
 * This code is (C) Copyright 1993,1994 by Frank Munkert.
 * All rights reserved.
 * This software may be freely distributed and redistributed for
 * non-commercial purposes, provided this notice is included.
 * ----------------------------------------------------------------------
 * [History removed]
 */

#include <stdlib.h>
#include <string.h>

#include <dos/dos.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/utility.h>
#include <proto/input.h>

#include "cdrom.h"
#include "generic.h"
#include "iso9660.h"
#include "rock.h"
#include "hfs.h"

/* note that vol may be either a VOLUME * or a CDROM_OBJ *  */
#define HAN(vol,func) (*(vol)->handler->func)

#define HANX(vol,func) ((vol)->handler->func)



/* WhichProtocol - examines which protocol is used.
 *
 * Input Variables:
 *  p_cdrom - CDROM descriptor
 *
 * Output Variables:
 *  p_skip - skip length for RockRidge disks
 *  p_offset - block offset of last session for multisession disks (ISO only)
 *
 * Result:
 *  PRO_ISO, PRO_HIGH_SIERRA, PRO_RR, PRO_HFS or PRO_UNKNOWN.
 */

t_protocol Which_Protocol (CDROM *p_cdrom,int *p_skip,t_ulong *p_offset)
{
BOOL hfs;


if (p_cdrom->model == MODEL_TOSHIBA_3401)
{
	if (Is_XA_Mode_Disk(p_cdrom))
		Mode_Select(p_cdrom, 0x81, 2048);
	else
		Mode_Select(p_cdrom, 0x00, 2048);
}


/* check for HFS first to speed up floppy access */
if (hfs = Uses_HFS_Protocol(p_cdrom, p_skip))
{
/* if a shift key is pressed, open this partition */
	if (PeekQualifier() & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
		return(PRO_HFS);
}
/* don't waste time trying the other file systems if it failed because of an error */
else if (IoErr() != ERROR_NOT_A_DOS_DISK) return(PRO_UNKNOWN);

if (Uses_Iso_Protocol(p_cdrom, p_offset))
{
	VOLUME tmp_vol; /* temporary volume descriptor */
	t_bool rr;


	tmp_vol.cd = p_cdrom;
	tmp_vol.protocol = PRO_ISO;
	Iso_Init_Vol_Info(&tmp_vol, 0, *p_offset,0);
	rr = Uses_Rock_Ridge_Protocol(&tmp_vol, p_skip);
	HAN(&tmp_vol, close_vol_info)(&tmp_vol);
	return(rr ? PRO_ROCK : PRO_ISO);
}
else if (hfs)
/* no ISO partition, so open the HFS partition */
	return(PRO_HFS);
else if (Uses_High_Sierra_Protocol(p_cdrom))
	return(PRO_HIGH_SIERRA);
else return(PRO_UNKNOWN);
}



VOLUME *Open_Volume(CDROM *p_cdrom,t_bool lowercase)
{
  VOLUME *res;
  int skip;
  t_ulong offset;

  res = AllocMem (sizeof (VOLUME), MEMF_PUBLIC);
  if (!res) {
    SetIoErr(ERROR_NO_FREE_STORE);
    return NULL;
  }

  res->cd = p_cdrom;

  res->protocol = Which_Protocol (p_cdrom,&skip,&offset);

  switch (res->protocol) {
  case PRO_ISO:
    if (!Iso_Init_Vol_Info (res, skip, offset, lowercase)) {
      FreeMem (res, sizeof (VOLUME));
      return NULL;
    }
    break;
  case PRO_ROCK:
    if (!Iso_Init_Vol_Info (res, skip, offset, 0)) {
      FreeMem (res, sizeof (VOLUME));
      return NULL;
    }
    break;
  case PRO_HFS:
    if (!HFS_Init_Vol_Info (res, skip)) {
      FreeMem (res, sizeof (VOLUME));
      return NULL;
    }
    break;
  default:
    FreeMem (res, sizeof (VOLUME));
    return NULL;
  }

  return res;
}

void Close_Volume (VOLUME *p_volume)
{
  HAN(p_volume, close_vol_info)(p_volume);
  FreeMem (p_volume, sizeof (VOLUME));
}

CDROM_OBJ *Open_Top_Level_Directory (VOLUME *p_volume)
{
return(HAN(p_volume, open_top_level_directory)(p_volume));
}

CDROM_OBJ *Open_Object(VOLUME *p_volume,CDROM_OBJ *p_current_dir, char *p_path_name)
{
  char *cp = p_path_name;
  CDROM_OBJ *obj, *new;
  char *np;
  char name[100];

  if (*cp == ':') {
    obj = HAN(p_volume, open_top_level_directory)(p_volume);
    cp++;
  } else {
    obj = Clone_Object (p_current_dir);
    if (!obj) {
      SetIoErr(ERROR_NO_FREE_STORE);
      return NULL;
    }
    while (*cp == '/') {
      new = HAN(p_volume, find_parent)(p_volume,obj);
      if (!new)
        return NULL;
      Close_Object(obj);
      obj = new;
      cp++;
    }
  }

  while (*cp) {
    for (np = name; *cp && *cp != '/'; )
      *np++ = *cp++;
    *np = 0;

    if (*cp)
      cp++;

    if (new = HAN(p_volume, open_obj_in_directory)(p_volume,obj, name)) {
      Close_Object(obj);
      if (*cp && new->symlink_f) {
	Close_Object(new);
	SetIoErr(ISOERR_IS_SYMLINK);
	return NULL;
      }
      if (*cp && !new->directory_f) {
	Close_Object(new);
	SetIoErr(ISOERR_NOT_FOUND);
	return NULL;
      }
      obj = new;
    } else {
      Close_Object(obj);
      return NULL;
    }
  }

return(obj);
}

void Close_Object(CDROM_OBJ *p_object)
{
HAN(p_object,close_obj)(p_object);
}

int Read_From_File (VOLUME *p_volume,CDROM_OBJ *p_file, char *p_buffer, int p_buffer_length)
{
  return HAN(p_volume, read_from_file)(p_volume,p_file, p_buffer, p_buffer_length);
}

LONG CDROM_Info (VOLUME *p_volume,CDROM_OBJ *p_obj, CDROM_INFO *p_info)
{
  return HAN(p_volume, cdrom_info)(p_volume,p_obj, p_info);
}

LONG Examine_Next (VOLUME *p_volume,CDROM_OBJ *p_dir, CDROM_INFO *p_info, unsigned long *p_offset)
{
  return HAN(p_volume, examine_next)(p_volume,p_dir, p_info, p_offset);
}

CDROM_OBJ *Clone_Object (CDROM_OBJ *p_object)
{
CDROM_OBJ *new;


if (!(new = AllocMem(sizeof(CDROM_OBJ),MEMF_PUBLIC)))
	return NULL;

memcpy(new,p_object,sizeof(CDROM_OBJ));

if (!(new->obj_info = HAN(p_object,clone_obj_info)(p_object->obj_info)))
{
	FreeMem(new,sizeof(CDROM_OBJ));
	return(NULL);
}

return(new);
}



CDROM_OBJ *Find_Parent (VOLUME *p_volume,CDROM_OBJ *p_object)
{
return(HAN(p_volume, find_parent)(p_volume,p_object));
}

t_bool Is_Top_Level_Object (VOLUME *p_volume,CDROM_OBJ *p_object)
{
  return HAN(p_volume, is_top_level_obj)(p_volume,p_object);
}

t_bool Same_Objects (CDROM_OBJ *p_object1, CDROM_OBJ *p_object2)
{
if (p_object1->handler != p_object2->handler) return(FALSE);
else return(HAN(p_object1,same_objects)(p_object1,p_object2));
}

t_ulong Volume_Creation_Date (VOLUME *p_volume)
{
  if (!HANX(p_volume, creation_date))
    return 0;
  return HAN(p_volume, creation_date)(p_volume);
}

t_ulong Volume_Size (VOLUME *p_volume)
{
  return HAN(p_volume, volume_size)(p_volume);
}

t_ulong Volume_Free (VOLUME *p_volume)
{
  return HAN(p_volume, volume_free)(p_volume);
}

t_ulong Block_Size (VOLUME *p_volume)
{
  return HAN(p_volume, block_size)(p_volume);
}

void Volume_ID (VOLUME *p_volume, char *p_buffer, int p_buf_length)
{
  HAN(p_volume, volume_id)(p_volume, p_buffer, p_buf_length);
}

t_ulong Location (VOLUME *p_volume,CDROM_OBJ *p_object)
{
  return HAN(p_volume, location)(p_volume,p_object);
}

/* Find a position in a file.
 */

LONG Seek_Position (CDROM_OBJ *p_object, long p_offset, int p_mode)
{
t_ulong new_pos;
t_ulong max_len;


max_len = HAN(p_object,file_length)(p_object);

if (p_object->directory_f) return(ERROR_OBJECT_WRONG_TYPE);

switch (p_mode)
{
case OFFSET_BEGINNING:
	if (p_offset < 0 || p_offset > max_len) return(ERROR_SEEK_ERROR);
	new_pos = p_offset;
	break;

case OFFSET_CURRENT:
	if ((p_offset < 0 && -p_offset > p_object->pos) ||
			(p_offset > 0 && p_object->pos + p_offset > max_len))
		return(ERROR_SEEK_ERROR);
	new_pos = p_object->pos + p_offset;
	break;

case OFFSET_END:
	if (p_offset > 0 || -p_offset > max_len) return(ERROR_SEEK_ERROR);
	new_pos = max_len + p_offset;
	break;

default:
	return(ERROR_SEEK_ERROR);
	break;
}

p_object->pos = new_pos;
return(0);
}

/* Find path name of an object:
 *
 */

int Full_Path_Name (VOLUME *p_volume,CDROM_OBJ *p_obj, char *p_buf, int p_buf_length)
{
  int len = 0;
  int free_chars = p_buf_length - 1;
  CDROM_OBJ *obj = p_obj;
  CDROM_OBJ *new;
  CDROM_INFO info;

  while (!Is_Top_Level_Object (p_volume,obj)) {
    if (!CDROM_Info (p_volume,obj, &info))
      return 0;

    if (info.name_length+1 > free_chars)
      return 0;

    memmove (p_buf+1+info.name_length, p_buf, len);
    memcpy (p_buf+1, info.name, info.name_length);
    p_buf[0] = '/';
    free_chars -= info.name_length + 1;
    len += info.name_length + 1;

    new = Find_Parent (p_volume,obj);
    if (!new)
      return 0;
    if (obj != p_obj)
      Close_Object(obj);
    obj = new;
  }

  p_buf[0] = ':';
  p_buf[len ? len : 1] = 0;

  if (obj != p_obj)
    Close_Object(obj);

  return 1;
}
