
/*
**  $VER: personal_sview_io.c 5.1 (27.05.96)
**
**  C source for personal_sview_io.library.
**  SAS/C source code.
**
**  Copyright © 1996 Cloanto Italia srl
**	    All Rights Reserved
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <libraries/personal_io.h>
#include <graphics/displayinfo.h>
#include <proto/graphics.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <string.h>

#include <superview/superviewbase.h>
#include <proto/superview.h>
#include <proto/superviewsupport.h>
#include <proto/svobjects.h>

/*
#include <graphics/gfx.h>
#include <graphics/gfxbase.h>
#include <intuition/intuitionbase.h>
#include <proto/intuition.h>
#include <string.h>
#include <stdlib.h>
*/

/*
 *  pragma aliases
 */
#define SysBase picinfo->SysBase
#define DOSBase picinfo->DOSBase
#define GfxBase picinfo->GfxBase
#define IntuitionBase picinfo->IntuitionBase
#define IconBase picinfo->IconBase

static UBYTE VersionStr[] = "$VER:Personal SuperViewLib IO Library 5.1 (27.05.96)";

/*
    Superview 11.7 svobject types (<L>oad, <S>ave, Name)

	L S  Name
	---------------------
	L    "SV FastILBM24"
	L S  "SV ILBM uncompressed"
	L S  "SV ILBM CmpByteRun1"
	L S  "SV ACBM"
	L S  "SV GIF 87a"
	L S  "SV GIF 89a"
	L S  "SV PCX"
	L    "SV IMG (HS 16)"   (HS = Header Size)
	L    "SV IMG (HS 18)"
	L    "SV MacPaint"
	L S  "SV Targa"
	L    "SV WPG"
	L S  "SV BMP"
	L    "SV Pictor"
	L S  "SV TIFF"
	L S  "SV YUVN"
	L S  "SV JPEG"
	L    "SV PCD"
	L S  "SV SunRaster"
	L S  "SV QRT"
	L S  "SV FBM"
	L S  "SV PNM"
	L    "SV EPS"
	L    "SV C64 Koala"
	L    "SV C64 Doodle"
	L    "SV WinIcon"
	L    "SV Unpack"
	L    "SV OS V3.00+ DataType"
*/

/*
 *  name shortening tables
 */
#define LNAME_NUM  3
static UBYTE *LongName[LNAME_NUM] =
{
	"SV ILBM uncompressed",
	"SV ILBM CmpByteRun1",
	"SV OS V3.00+ DataType"
};
static UBYTE *ShortName[LNAME_NUM] =  /* max 16 chrs */
{
	"SV ILBM NoComp.",
	"SV ILBM Comp.",
	"SV DataType"
};

static LONG DataCount;	/* counter used by query functions */
static LONG OptionCount;

/*
 *  library interface protos
 */
__asm BOOL __saveds GetFirstDataInfo(register __a0 struct DataInfo *);
__asm BOOL __saveds GetNextDataInfo(register __a0 struct DataInfo *);
__asm WORD __saveds GetPictureInfo(register __a0 struct PictureInfo *);
__asm LONG __saveds GetProgressBumps(register __a0 struct PictureInfo *);
__asm WORD __saveds ReadPicture(register __a0 struct PictureInfo *);
__asm WORD __saveds WritePicture(register __a0 struct PictureInfo *);
__asm void __saveds IOLibCleanup(register __a0 struct PictureInfo *);
__asm BOOL __saveds GetFirstOption(register __a0 struct DataInfo *, register __a1  struct DataOption *);
__asm BOOL __saveds GetNextOption(register __a0 struct DataInfo *, register __a1  struct DataOption *);
__asm void __saveds SetOption(register __a0 struct DataInfo *, register __a1  struct DataOption *);
__asm void __saveds ResetOptions(register __a0 struct DataInfo *);
__asm WORD __saveds PersonalIOPrivate1(void);
__asm WORD __saveds PersonalIOPrivate2(void);
__asm WORD __saveds PersonalIOPrivate3(void);

/*
 *  library's standard static protos
 */
static struct SVO_ObjectNode *GetDataInfo(struct DataInfo *, LONG);
static BOOL Handshake(struct DataInfo *);

/*
 *  superview-specific protos
 */
static struct SV_GfxBuffer *AllocGfxBuffer(SHORT, SHORT, UBYTE);
static void  CloseLib(void);
static BOOL  ConvertBMap(struct SV_GfxBuffer *, struct PictureInfo *);
static void  EnvFileName(UBYTE *, UBYTE *, UBYTE *, UBYTE *);
static void  FreeGfxBuffer(struct SV_GfxBuffer *);
static BOOL  GetOption(UBYTE *, struct DataOption *, LONG, BOOL);
static void  GetOptionValue(UBYTE *, struct DataOption *);
static ULONG GetSVOCode(struct SVO_ObjectNode *, LONG);
static void  GetSVOInfo(struct DataInfo *, struct SVO_ObjectNode *, ULONG, LONG);
static ULONG HasOptions(UBYTE *);
static BOOL  IsNotDataType(struct SVO_ObjectNode *);
static UBYTE *LoadEnvFile(UBYTE *, UBYTE *);
static BOOL  OpenLib(void);
static void PicInfoToGfxBuffer(struct PictureInfo *, struct SV_GfxBuffer *);
static BOOL  SearchKeyword(UBYTE *, UBYTE **);
static void  WriteOption(UBYTE *, struct DataOption *);
static UBYTE *ShortenName(UBYTE *);
static WORD  SVErrToPIOErr(ULONG, BOOL);

/*
 *  static data
 */
#define SVLIB_VER  11
static struct SuperViewBase *SuperViewBase;








/*
 *  This function opens superview.library
 *  and initialize the global SuperViewBase
 */
static BOOL OpenLib()
{
	struct ExecBase *sys = *((struct ExecBase **)4L);

	/*
	 *  this IO library requires 2.04
	 *  (AllocVec(), GetDisplayInfoData() and
	 *   other 2.04 functions are used)
	 */
	if (sys->LibNode.lib_Version < 36)
		return(FALSE);

	if (SuperViewBase = (struct SuperViewBase *)OpenLibrary("superview.library", SVLIB_VER))
		return(TRUE);
	else
		return(FALSE);
}

/*
 *  This function closes superview.library
 */
static void CloseLib()
{
	if (SuperViewBase)
	{
		CloseLibrary((struct Library *)SuperViewBase);
		SuperViewBase = NULL;
	}
}

/*
 *  This function builds and stores in 'buff' an Env/Envarc pathname
 *  based on the supplied 'svobjname'
 */
static void EnvFileName(UBYTE *env,
			UBYTE *svobjname,
			UBYTE *suffix,
			UBYTE *buff)
{
	LONG c;

	for(c = 0; *(svobjname + c) != 0; c++)
	{
		if (stricmp(svobjname + c, ".svobject") == 0)
			break;
	}
	strcpy(buff, env);
	buff += strlen(buff);
	strcpy(buff, "SuperView-Library/");
	buff += strlen(buff);
	memcpy(buff, svobjname, c);
	strcpy(buff + c, suffix);
}

/*
 *  This function loads a svobject option file
 *  (the storage area returned can be freed with FreeVec())
 */
static UBYTE *LoadEnvFile(UBYTE *svobjname, UBYTE *suffix)
{
	#undef DOSBase
	#undef SysBase
	struct FileInfoBlock __aligned finfo;
	struct DosLibrary *DOSBase;
	struct ExecBase *SysBase;
	UBYTE envname[80], *buff;
	LONG size;
	BPTR file, lock;
	BOOL ok;

	/*
	 *  local library bases (GetFirstOption() and GetNextOption()
	 *  cannot pass them: they haven't got any PictureInfo)
	 */
	SysBase = *((struct ExecBase **)4L);
	DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 0);

	EnvFileName("ENV:", svobjname, suffix, envname);
	buff = NULL;
	ok = FALSE;
	if (lock = Lock(envname, ACCESS_READ))
	{
		if (Examine(lock, &finfo))
		{
			if (finfo.fib_DirEntryType < 0 && finfo.fib_Size > 0)
			{
				size = finfo.fib_Size + 1;
				if (buff = AllocVec(size, 0))
				{
					/*
					 *  load the file in the allocated buffer
					 */
					if (file = Open(envname, MODE_OLDFILE))
					{
						if (Read(file, buff, size-1) == (size-1))
						{
							ok = TRUE;
							/* zero-terminate the string */
							*(buff + size - 1) = 0;
						}
						Close(file);
					}
				}
			}
		}
		UnLock(lock);
	}
	if (buff && !ok)
	{
		FreeVec(buff);  buff = NULL;
	}
	CloseLibrary((struct Library *)DOSBase);
	return(buff);

	#define SysBase picinfo->SysBase
	#define DOSBase picinfo->DOSBase
}

/*
 *  This function replaces, in a svobject option file, the given option.
 *  The option is added if it is not in the option file.
 */
static void WriteOption(UBYTE *svobjname, struct DataOption *option)
{
	#undef DOSBase
	#undef SysBase
	static UBYTE pad_suffix[] = ".controlpad";
	static UBYTE *env[2] = { "ENV:", "ENVARC:" };
	struct DosLibrary *DOSBase;
	struct ExecBase *SysBase;
	UBYTE envname[80], *buff, *b;
	BPTR file;
	LONG ln, c, e;
	BOOL write_new, written;

	/*
	 *  local library bases (GetFirstOption() and GetNextOption()
	 *  cannot pass them: they haven't got any PictureInfo)
	 */
	SysBase = *((struct ExecBase **)4L);
	DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 0);

	buff = LoadEnvFile(svobjname, pad_suffix);
	ln = strlen(option->Name);

	for (e = 0; e < 2; e++)	/* set options both in ENV: and ENVARC: */
	{
		EnvFileName(env[e], svobjname, pad_suffix, envname);

		if (file = Open(envname, MODE_NEWFILE))
		{
			b = buff;
			write_new = written = FALSE;
			for (;;)  /* FOREVER */
			{
				if (b)
				{
					for (c = 0; *(b+c) != 0; c++)	/* compute size of line */
					{
						if (*(b+c) == '\n')
						{
							c++; break;
						}
					}
					if (*b)
					{
						if (memcmp(b, option->Name, ln) == 0)
							write_new = TRUE;
					}
					else
					{
						b = NULL;	/* exit from loop */
						if (!written)
							write_new = TRUE;
					}
				}
				else write_new = TRUE;

				if (write_new)
				{
					write_new = FALSE;
					written = TRUE;
					Write(file, option->Name, ln);
					Write(file, "=", 1);
					Write(file, option->String, strlen(option->String));
					Write(file, "\n", 1);
				}
				else if (b != NULL && c != 0)
					Write(file, b, c);  /* write other option */
				if (b)
					b += c;
				else
					break;
			}
			Close(file);
		}
	}
	if (buff)
		FreeVec(buff);
}

/*
 *  Given an option buffer, this function searches a keyword in it
 */
static BOOL SearchKeyword(UBYTE *kwd, UBYTE **bptr)
{
	UBYTE *b = *bptr;
	LONG ln = strlen(kwd);

	while (*b)
	{
		if (memcmp(b, kwd, ln) == 0)
		{
			b += ln;
			if (*b == '=')
				++b;
			*bptr = b;
			return(TRUE);
		}
		/*
		 *  go to next line (keywords' place)
		 */
		for (; *b != '\n' && *b != 0; b++);
		if (*b == '\n') ++b;
	}
	return(FALSE);	/* keyword not found */
}

/*
 *  Given a svobjname, this function returns the DIF_SAVEOPTS DIF_LOADOPTS
 *  flag combination, describing the specific option availability
 */
static ULONG HasOptions(UBYTE *svobjname)
{
	UBYTE *buff, *b;
	ULONG flags = 0;

	if (buff = LoadEnvFile(svobjname, ".cpinfo"))
	{
		/*  WHEN=<READ|WRITE|ALWAYS|PREFS|NEVER>
		 */
		for (b = buff; SearchKeyword("WHEN", &b) != FALSE; )
		{
			if (memcmp(b, "WRITE", 5) == 0)	/* a Personal_IO option can either be load or save, not both */
				flags |= DIF_SAVEOPTS;
			else
				flags |= DIF_LOADOPTS;

			for (; *b != '\n' && *b != 0; b++);
			if (*b == '\n') ++b;
		}
		FreeVec(buff);
	}
	return(flags);
}

/*
 *  This function loads an option file, search an option in it
 *  and stores its current value in the given DataOption structure
 */
static void GetOptionValue(UBYTE *svobjname, struct DataOption *option)
{
	UBYTE *buff, *b, *s, *s1;

	if (buff = LoadEnvFile(svobjname, ".controlpad"))
	{
		b = buff;
		if (SearchKeyword(option->Name, &b))
		{
			s = option->String;
			s1 = option->String + (sizeof(option->String) - 1);
			while (*b != '\n' && *b != 0 && s < s1)
				*s++ = *b++;
			*s = 0;
		}
		FreeVec(buff);
	}
}

/*
 *  This function loads an option file and fill the DataOption argument
 *  with option data (name, value). The num'th option is searched.
 *  If 'get_default' is TRUE, the default value is taken.
 */
static BOOL GetOption(UBYTE *svobjname,
		      struct DataOption *option,
		      LONG num,
		      BOOL get_default)
{
	static struct DataOption last_option;
	static BOOL use_last_option;
	UBYTE *buff, *b, *s, *s1;
	ULONG n = 0;
	BOOL ok = FALSE;

	if (use_last_option)
	{
		use_last_option = FALSE;
		*option = last_option;
		option->Flags |= DOF_LOADOPTION;
		return(TRUE);
	}
	if (buff = LoadEnvFile(svobjname, ".cpinfo"))
	{
		for (b = buff; SearchKeyword("SECTION", &b) != FALSE; n++)
		{
			if (n == num)
			{
				option->Type = DOT_STRING;
				option->UITextStr = DOUI_NONE;
				option->Value = 0;
				option->Min = 0;
				option->Max = 0;
				option->Default = 0;
				memset(option->pad, 0, sizeof(option->pad));

				memset(option->Name, 0, sizeof(option->Name));
				s = option->Name;
				s1 = option->Name + (sizeof(option->Name) - 1);
				while (*b != '\n' && *b != 0 && s < s1)
					*s++ = *b++;

				memset(option->String, 0, sizeof(option->String));
				if (SearchKeyword("DEFAULT", &b))
				{
					s = option->String;
					s1 = option->String + (sizeof(option->String) - 1);
					while (*b != '\n' && *b != 0 && s < s1)
						*s++ = *b++;
				}

				option->Flags = 0;
				/*
				 *  Note: "WHEN=READ" makes a DOF_LOADOPTION option,
				 *        "WHEN=WRITE" makes a not-DOF_LOADOPTION option,
				 *        "WHEN=..." makes two options (one DOF_LOADOPTION, one not)
				 */
				if (SearchKeyword("WHEN", &b))
				{
					if (memcmp(b, "READ", 4) == 0)
						option->Flags |= DOF_LOADOPTION;
					else if (memcmp(b, "WRITE", 5) != 0)
						use_last_option = TRUE;		// both a READ/WRITE option: duplicate it
				}
				if (!get_default)
					GetOptionValue(svobjname, option);
				last_option = *option;
				ok = TRUE;
				break;
			}
			for (; *b != '\n' && *b != 0; b++);
			if (*b == '\n') ++b;
		}
		FreeVec(buff);
	}
	return(ok);
}

/*
 *  Name shortening function
 */
static UBYTE *ShortenName(UBYTE *name)
{
	LONG n;

	for (n = 0; n < LNAME_NUM; n++)
	{
		if (stricmp(LongName[n], name) == 0)
			return(ShortName[n]);
	}
	return(name);
}

/*
 *  Superview DatatType loader is ignored
 *  (it's buggy and catches every file type,
 *   breaking the file recognition chain)
 */
static BOOL IsNotDataType(struct SVO_ObjectNode *node)
{
	return((BOOL)(strstr(node->svo_TypeID, "DataType") == 0));
}

/*
 *  This function returns a svobject code from a given node.
 *
 *  subtype = -1 returns a load-only code
 *  subtype >= 0 returns the subtype'th code
 */
static ULONG GetSVOCode(struct SVO_ObjectNode *node, LONG subtype)
{
	ULONG code;

	if (node->svo_SubTypeNum <= 1)	/* just one subtype:       */
		subtype = 0;		/* return a load/save code */

	code = (subtype >= 0) ? node->svo_SubTypeCode[subtype] : node->svo_TypeCode;
	return(code);
}

/*
 *  This function fills the given DataInfo with SVO data
 *  from a given node.
 *
 *  subtype = -1 makes a load-only DataInfo
 *  subtype >= 0 makes a specific load/save DataInfo
 */
static void GetSVOInfo(struct DataInfo *dinfo,
		       struct SVO_ObjectNode *node,
		       ULONG flags,
		       LONG subtype)
{
	#define SVBUFF_SIZE  (4+32+16)

	UBYTE *b, buff[SVBUFF_SIZE];

	if (node->svo_SubTypeNum <= 1)
	{
		/* just one subtype: make a load/save dinfo */
		subtype = 0;
		flags = node->svo_Flags;
	}
	dinfo->Code = GetSVOCode(node, subtype);
	dinfo->Flags = (DIF_CONVERTED | DIF_CUSTOMIO) | HasOptions(node->svo_FileName);

	if (flags & SVO_FLAG_READS_TO_BUFFER)
		dinfo->Flags |= (DIF_LOADFILE | DIF_LOAD | DIF_LOAD_24 | DIF_RECOGNIZE);

	if (flags & SVO_FLAG_WRITES_FROM_BUFFER)
		dinfo->Flags |= (DIF_SAVEFILE | DIF_SAVE);

	dinfo->ProcessData = (PCDT_FORMAT | PCDT_IMAGE | PCDT_PALETTE | PCDT_DISPID);
	dinfo->Priority = 9001;

	b = buff;
	*b++ = 'S';
	*b++ = 'V';
	*b++ = ' ';
	strcpy(b, node->svo_TypeID);

	if (subtype >= 0 && node->svo_SubTypeNum > 1)
	{
		b += strlen(b);
		*b++ = ' ';
		strcpy(b, node->svo_SubTypeID[subtype]);
	}
	strncpy(dinfo->Name, buff, sizeof(dinfo->Name));
	strncpy(dinfo->ShortName, ShortenName(buff), sizeof(dinfo->ShortName));
	dinfo->Name[sizeof(dinfo->Name)-1] = 0;
	dinfo->ShortName[sizeof(dinfo->ShortName)-1] = 0;

	dinfo->FileSuffix[0][0] = 0;
	dinfo->FileSuffix[1][0] = 0;
	dinfo->FileSuffix[2][0] = 0;
	memset(dinfo->pad, 0, sizeof(dinfo->pad));
}

/*
 *  Query data dispatcher:
 *  the n'th SV picture type is copied to 'dinfo'.
 *
 *  If 'n' is < 0, the SVO_ObjectNode matching 'dinfo->Code' is returned.
 */
static struct SVO_ObjectNode *GetDataInfo(struct DataInfo *dinfo, LONG n)
{
	struct List *obj_list;
	struct SVO_ObjectNode *svo_node;
	ULONG flags;
	LONG i, s;
	BOOL find_svo;

	obj_list = &SuperViewBase->svb_SVObjectList;
	find_svo = (n < 0);
	i = 0;
	/*
	 *  Cycle through the available SV picture types,
	 *  until the n'th type is reached
	 *  or the 'dinfo->Code' SVO_ObjectNode has been found.
	 */
	for(svo_node = (APTR)obj_list->lh_Head;
	    svo_node != NULL && svo_node != (APTR)&(obj_list->lh_Tail);
	    svo_node = (APTR)((struct Node *)svo_node)->ln_Succ)
	{

		if ((svo_node->svo_ObjectType == SVO_OBJECTTYPE_INTERNAL ||
		     svo_node->svo_ObjectType == SVO_OBJECTTYPE_EXTERNAL) &&
		     svo_node->svo_SubTypeNum != 0xFFFFFFFF &&
		    (svo_node->svo_Flags & (SVO_FLAG_READS_TO_BUFFER | SVO_FLAG_WRITES_FROM_BUFFER)) != 0 &&
		    IsNotDataType(svo_node))
		{

			if (svo_node->svo_SubTypeNum > 1 &&
			    (svo_node->svo_Flags & SVO_FLAG_READS_TO_BUFFER))
			{
				/*
				 *  Multiple dinfo creation (e.g. we find
				 *  the subtypes "GIF87a" and "GIF89a",
				 *  but we create one "GIF" load-dinfo
				 *  and two "GIF8?a" save-dinfo.
				 */
				if (find_svo)
				{
					if (dinfo->Code == GetSVOCode(svo_node, -1))
						return(svo_node);
				}
				else if (i == n)
				{
					GetSVOInfo(dinfo, svo_node, SVO_FLAG_READS_TO_BUFFER, -1);	// load-only-dinfo
					return(svo_node);
				}
				i++;
			}
			for(s = 0; s < svo_node->svo_SubTypeNum; s++)
			{
				flags = svo_node->svo_Flags;
				/*
				 *  Note: we find the load-only "IMG-HS16"
				 *  and "IMG-HS18": we just need one 'dinfo'
				 *  (made above) for them.
				 */
				if (svo_node->svo_SubTypeNum > 1)
					flags &= SVO_FLAG_WRITES_FROM_BUFFER;

				if ((flags & (SVO_FLAG_READS_TO_BUFFER | SVO_FLAG_WRITES_FROM_BUFFER)) == 0)
					continue;

				if (find_svo)
				{
					if (dinfo->Code == GetSVOCode(svo_node, s))
						return(svo_node);
				}
				else if (i == n)
				{
					GetSVOInfo(dinfo, svo_node, flags, s);
					return(svo_node);
				}
				i++;
			}
		}
	}
	return(NULL);
}

/*
 *  Standard handshake function:
 *  FALSE is returned if handshaking fails,
 *  TRUE if it succeeds.
 */
static BOOL Handshake(struct DataInfo *dinfo)
{
	static UBYTE hs_text[] = "Cloanto®";

	if (dinfo->Version >= 5)
	{
		if (dinfo->HandshakeText == NULL)
			return(FALSE);
		if (strcmp(dinfo->HandshakeText, hs_text) != 0)
			return(FALSE);
		/*
		 * the library internal string is passed
		 */
		dinfo->HandshakeText = hs_text;
	}
	return(TRUE);
}

/*
 *  DataInfo query function:
 *  the "SV AUTO" picture type (global loader) is returned.
 */
__asm BOOL __saveds GetFirstDataInfo(register __a0 struct DataInfo *dinfo)
{
	if (Handshake(dinfo))
	{
		if (OpenLib())  /* is superview available? */
		{
			dinfo->Code = SV_SUBTYPE_ILLEGAL;
			dinfo->Flags = (DIF_CONVERTED | DIF_CUSTOMIO |
					DIF_LOADFILE | DIF_LOAD |
					DIF_LOAD_24 | DIF_RECOGNIZE);

			dinfo->ProcessData = (PCDT_FORMAT | PCDT_IMAGE | PCDT_PALETTE | PCDT_DISPID);
			dinfo->Priority = 9000;		// the first one
			strcpy(dinfo->Name, "SV AUTOMATIC");
			strcpy(dinfo->ShortName, "SV AUTO");
			dinfo->FileSuffix[0][0] = 0;
			dinfo->FileSuffix[1][0] = 0;
			dinfo->FileSuffix[2][0] = 0;
			memset(dinfo->pad, 0, sizeof(dinfo->pad));
			CloseLib();
			DataCount = 0;
			return(TRUE);
		}
	}
	return(FALSE);
}

/*
 *  DataInfo query function:
 *  the SV picture types are returned.
 */
__asm BOOL __saveds GetNextDataInfo(register __a0 struct DataInfo *dinfo)
{
	BOOL ok = FALSE;

	if (Handshake(dinfo))
	{
		if (OpenLib())
		{
			ok = (GetDataInfo(dinfo, DataCount++) != NULL);
			CloseLib();
		}
	}
	return(ok);
}

/*
 *  This function translates SVERR_ codes into PIOERR_ codes.
 */
static WORD SVErrToPIOErr(ULONG sverr, BOOL load)
{
	switch (sverr)
	{
		case SVERR_NO_ERROR:
			return(PIOERR_OK);
		case SVERR_UNKNOWN_FILE_FORMAT:
			return(PIOERR_BADTYPE);
		case SVERR_FILE_NOT_FOUND:
		case SVERR_NO_FILE:
			return(PIOERR_FILE_ERR);
		case SVERR_NO_MEMORY:
			return(PIOERR_NOMEMORY);
		case SVERR_NO_HANDLE:
			return(PIOERR_NOMEMORY);
		case SVERR_IFFPARSE_ERROR:
			return(PIOERR_BADDATA);
		case SVERR_UNKNOWN_PARAMETERS:
		case SVERR_HAM_DATA:
			return(PIOERR_UIMGTYP);
		case SVERR_ACTION_NOT_SUPPORTED:
			/*
			 * the client program translates 1000 (unknown error)
			 * into "Operation impossible"
			 */
			return((WORD)(load ? PIOERR_UIMGTYP : 1000));

		default:
			/* the SV jpeg module correctly writes
			 * QUANTZATION=5 images, but reports
			 * the error 23 after the image has been saved
			 */
			if (sverr > SVERR_HAM_DATA)
				return(PIOERR_OK);
			return((WORD)(load ? PIOERR_NODATA : PIOERR_FILE_ERR));
	}
}

/*
 *  File recognition function:
 *
 *  the superview system cannot give image format information
 *  (i.e. width, height and depth) without actually loading the image.
 *  The GetPictureInfo() function must give an almost immediate
 *  response about the picture type; it will be called again
 *  to give the required format information.
 */
__asm WORD __saveds GetPictureInfo(register __a0 struct PictureInfo *picinfo)
{
	struct SV_GfxBuffer *gfxbuff;
	struct List *obj_list;
	struct SVO_ObjectNode *svo_node;
	APTR handle, data_handle;
	ULONG retval, filetype;
	WORD rc;

	if (picinfo->Version < 3)
		return(PIOERR_BADVER);

	if (!OpenLib())
		return(PIOERR_BADTYPE);  /* the file is submitted to the other IO libraries */

	rc = PIOERR_OK;
	retval = SVERR_NO_ERROR;

	/*
	 *  First step: detect the picture type
	 */
	if (picinfo->User1 == (APTR)FALSE)  /* first call? */
	{
		handle = SVL_AllocHandle(NULL);
		if (handle)
		{
			if (!(retval = SVL_InitHandleAsDOS(handle, NULL)))
				retval = SVL_GetFileType(handle, picinfo->FileName, &filetype);
			SVL_FreeHandle(handle);
		}
		else retval = SVERR_NO_HANDLE;

		if (retval == SVERR_NO_ERROR && filetype != SVO_OBJECTTYPE_UNKNOWN)
		{
			/*
			 *  SV successfully recognized the picture file:
			 *  let's get to the 'filetype' svo_node.
			 */
			obj_list = &SuperViewBase->svb_SVObjectList;
			for (svo_node = (APTR)obj_list->lh_Head;
			     svo_node != NULL && svo_node != (APTR)&(obj_list->lh_Tail);
			     svo_node = (APTR)((struct Node *)svo_node)->ln_Succ)
			{
				if (svo_node->svo_TypeCode == filetype)
				{
					if ((picinfo->Data.Code == DIC_ANYDATA ||
					     picinfo->Data.Code == SV_SUBTYPE_ILLEGAL ||  /* "SV Auto" loading */
					     picinfo->Data.Code == GetSVOCode(svo_node, -1)) &&  /* specific SV type loading */
					    IsNotDataType(svo_node))
					{
						/*
						 *  store the picture type
						 *  data into picinfo->Data
						 */
						GetSVOInfo(&picinfo->Data, svo_node, SVO_FLAG_READS_TO_BUFFER, -1);
					}
					else rc = PIOERR_BADTYPE;
				}
			}
			if (rc == PIOERR_OK)
				picinfo->User1 = (APTR)TRUE;  /* set the Inspected flag */
		}
		else rc = PIOERR_BADTYPE;  /* SV couldn't recognize the file */
	}
	/*
	 *  Second step: load the image to get its format information
	 *  (the image data won't be freed, but used by ReadPicture();
	 *   the IOLibCleanup() function will free such data and close
	 *   the SV library).
	 */
	else
	{
		picinfo->User1 = (APTR)FALSE;  /* clear the Inspected flag */

		data_handle = SVL_AllocHandle(NULL);
		if (data_handle)
		{
			if (!(retval = SVL_InitHandleAsDOS(data_handle, NULL)))
			{
				if (!(retval = SVL_ReadToGfxBuffer(data_handle, picinfo->FileName)))
				{
					if (!(retval = SVL_GetGfxBuffer(data_handle, &gfxbuff, NULL)))
					{
						if (gfxbuff->svgfx_NativeDIPF & (DIPF_IS_HAM | DIPF_IS_DUALPF))
							retval = SVERR_ACTION_NOT_SUPPORTED;
						else
						{
							picinfo->Width = gfxbuff->svgfx_Width;
							picinfo->Height = gfxbuff->svgfx_Height;
							picinfo->Depth = (gfxbuff->svgfx_ColorDepth <= 8) ? gfxbuff->svgfx_ColorDepth : 24;
							picinfo->PreviewDepth = (picinfo->Depth == 24) ? 24 : 0;
							picinfo->ProcessedData = PCDT_FORMAT;
						}
					}
				}
			}
		}
		else retval = SVERR_NO_HANDLE;

		if (retval)  /* error: free the image data */
		{
			if (data_handle)
				SVL_FreeHandle(data_handle);

			rc = SVErrToPIOErr(retval, TRUE);
		}
		picinfo->User2 = data_handle;
	}
	if (picinfo->User2 == NULL)  /* we're not holding data: close the SV library */
		CloseLib();

	return(rc);
}

/*
 *  Progress requester information function.
 */
__asm LONG __saveds GetProgressBumps(register __a0 struct PictureInfo *picinfo)
{
	/*
	 *  We have no control on the data loading process
	 *  (e.g. call the picinfo->BumpProgress() function for
	 *   every image row loaded).
	 *  The value 0 also makes the operation uninterruptible
	 *  (no progress requester or a "ghosted" one).
	 */
	return(0);
}

/*
 *  This function converts the SV_GfxBuffer's image data
 *  into Personal_IO bitmap data.
 *
 *  SV data format supported:
 *  - planar bitmap (up to 256 colors - palette based)
 *  - chunky bitmap (up to 256 colors - palette based)
 *  - chunky bitmap (32768 colors - hicolor)
 *  - chunky bitmap (16.7 million of colors - truecolor)
 */
static BOOL ConvertBMap(struct SV_GfxBuffer *gfxbuff,
			struct PictureInfo *picinfo)
{
	struct BitMap *bmap;
	UBYTE *buff, *dest, *rplane, *gplane, *bplane;
	LONG bpr, b, p, x, y;
	USHORT data;

	bmap = picinfo->BMap;
	buff = gfxbuff->svgfx_Buffer;

	if (gfxbuff->svgfx_BufferType == SVGFX_BUFFERTYPE_BITPLANE)
	{
		/*
		 *  planar bitmap (up to 256 colors - palette based)
		 */
		if (bmap->Depth != gfxbuff->svgfx_ColorDepth ||
		    bmap->BytesPerRow < gfxbuff->svgfx_BytesPerLine ||
		    bmap->Rows != gfxbuff->svgfx_Height)
			return(FALSE);

		bpr = gfxbuff->svgfx_BytesPerLine;
		for (p = 0; p < bmap->Depth; p++)
		{
			dest = bmap->Planes[p];
			for (y = gfxbuff->svgfx_Height; --y >= 0; )
			{
				for (b = 0; b < bpr; b++)
					*(dest + b) = *buff++;
				dest += bmap->BytesPerRow;
			}
		}
	}
	else if (gfxbuff->svgfx_BufferType == SVGFX_BUFFERTYPE_ONEPLANE)
	{
		if (gfxbuff->svgfx_PixelBits == 8)
		{
			/*
			 *  chunky bitmap (up to 256 colors - palette based)
			 */
			if (bmap->Depth != gfxbuff->svgfx_ColorDepth ||
			    picinfo->Width != gfxbuff->svgfx_Width ||
			    picinfo->Height != gfxbuff->svgfx_Height)
				return(FALSE);
			for (y = 0 ; y < gfxbuff->svgfx_Height; y++)
			{
				picinfo->FastWritePix(bmap, FBMP_INIT,0,0);
				for (x = 0; x < gfxbuff->svgfx_Width; x++)
					picinfo->FastWritePix(bmap, x, y, *buff++);

				picinfo->FastWritePix(bmap, FBMP_FLUSH,0,0);
			}
		}
		else if (gfxbuff->svgfx_PixelBits == 16)
		{
			/*
			 *  chunky bitmap (32768 colors - hicolor)
			 *  bit format 5:5:5:1 (R+G+B + 1 Bit Alpha Channel)
			 */
			if (bmap->Depth != 24 ||
			    picinfo->Width != gfxbuff->svgfx_Width ||
			    picinfo->Height != gfxbuff->svgfx_Height)
				return(FALSE);
			rplane = bmap->Planes[0];
			gplane = bmap->Planes[1];
			bplane = bmap->Planes[2];
			for (y = 0 ; y < gfxbuff->svgfx_Height; y++)
			{
				for (x = 0; x < gfxbuff->svgfx_Width; x++, buff += 2)
				{
					data = (((USHORT)*buff) << 8) | ((USHORT)*buff);
					data >>= 1;		// alpha bit
					*(bplane + x) = data & 0x1F;
					data >>= 5;
					*(gplane + x) = data & 0x1F;
					data >>= 5;
					*(rplane + x) = data & 0x1F;
				}
				if (picinfo->PreviewDepth)  /* feed one more preview row */
					picinfo->PreviewRow(bmap, y);
				rplane += bmap->BytesPerRow;
				gplane += bmap->BytesPerRow;
				bplane += bmap->BytesPerRow;
			}
		}
		else if (gfxbuff->svgfx_PixelBits == 24)  // 8:8:8 (R:G:B)
		{
			/*
			 *  chunky bitmap (16.7 million of colors - truecolor)
			 *  bit format 8:8:8 (R:G:B)
			 */
			if (bmap->Depth != 24 ||
			    picinfo->Width != gfxbuff->svgfx_Width ||
			    picinfo->Height != gfxbuff->svgfx_Height)
				return(FALSE);
			rplane = bmap->Planes[0];
			gplane = bmap->Planes[1];
			bplane = bmap->Planes[2];
			for (y = 0 ; y < gfxbuff->svgfx_Height; y++)
			{
				for (x = 0; x < gfxbuff->svgfx_Width; x++)
				{
					*(rplane + x) = *buff++;
					*(gplane + x) = *buff++;
					*(bplane + x) = *buff++;
				}
				if (picinfo->PreviewDepth)  /* feed one more preview row */
					picinfo->PreviewRow(bmap, y);
				rplane += bmap->BytesPerRow;
				gplane += bmap->BytesPerRow;
				bplane += bmap->BytesPerRow;
			}
		}
	}
	return(TRUE);
}

/*
 *  Image loading function
 */
__asm WORD __saveds ReadPicture(register __a0 struct PictureInfo *picinfo)
{
	struct SV_GfxBuffer *gfxbuff;
	struct Color *col;
	ULONG retval;
	SHORT nc, c;

	/*
	 *  The image data has already been loaded by GetPictureInfo()
	 */
	if (SuperViewBase == NULL || picinfo->User2 == NULL)
		return(PIOERR_NODATA);

	if (!(retval = SVL_GetGfxBuffer(picinfo->User2, &gfxbuff, NULL)))
	{
		/*
		 *  copy to 'picinfo' the required pieces of information
		 *  and set the coresponding PCDT bits
		 */
		if ((picinfo->ProcessData & PCDT_DISPID) != 0 &&
		    gfxbuff->svgfx_ViewMode32 != 0)
		{
			picinfo->ProcessedData |= PCDT_DISPID;
			picinfo->DisplayID = gfxbuff->svgfx_ViewMode32;
		}

		if ((picinfo->ProcessData & PCDT_PALETTE) && picinfo->Palette &&
		    gfxbuff->svgfx_ColorDepth <= 8)
		{
			picinfo->ProcessedData |= PCDT_PALETTE;
			nc = 1 << picinfo->Depth;
			for (c = 0, col = picinfo->Palette; c < nc; c++, col++)
			{
				col->Red   = gfxbuff->svgfx_Colors[c][0];
				col->Green = gfxbuff->svgfx_Colors[c][1];
				col->Blue  = gfxbuff->svgfx_Colors[c][2];
				col->EFlag = 0;
			}
		}

		if ((picinfo->ProcessData & PCDT_IMAGE) && picinfo->BMap)
		{
			if (ConvertBMap(gfxbuff, picinfo))
				picinfo->ProcessedData |= PCDT_IMAGE;
		}
	}
	return(PIOERR_OK);
}

/*
 *  This function allocates and initializes a SV_GfxBuffer structure
 *  (image data storage area)
 */
static struct SV_GfxBuffer *AllocGfxBuffer(SHORT width, SHORT height, UBYTE depth)
{
	struct SV_GfxBuffer *gfxbuff;

	if ((gfxbuff = AllocVec(sizeof(struct SV_GfxBuffer), MEMF_CLEAR)) == NULL)
		return(NULL);

	gfxbuff->svgfx_BytesPerLine = ((width + 7) >> 3);
	gfxbuff->svgfx_BufferSize = (gfxbuff->svgfx_BytesPerLine * height) * depth;

	if ((gfxbuff->svgfx_Buffer = AllocVec(gfxbuff->svgfx_BufferSize, 0)) == NULL)
	{
		FreeVec(gfxbuff);
		return(NULL);
	}
	gfxbuff->svgfx_Version = SVGFX_ACT_VERSION;
	gfxbuff->svgfx_BufferType = SVGFX_BUFFERTYPE_BITPLANE;
	gfxbuff->svgfx_Width = width;
	gfxbuff->svgfx_Height = height;
	gfxbuff->svgfx_ColorDepth = depth;

	return(gfxbuff);
}

/*
 *  This function releases a SV_GfxBuffer structure
 */
static void FreeGfxBuffer(gfxbuff)
struct SV_GfxBuffer *gfxbuff;
{
	if (gfxbuff != NULL)
	{
		if (gfxbuff->svgfx_Buffer)
			FreeVec(gfxbuff->svgfx_Buffer);
		FreeVec(gfxbuff);
	}
}

/*
 *  This function converts the PictureInfo's image information
 *  (palette, displayID and bitmap) into SV_GfxBuffer data.
 */
static void PicInfoToGfxBuffer(picinfo, gfxbuff)
struct PictureInfo *picinfo;
struct SV_GfxBuffer *gfxbuff;
{
	struct DisplayInfo dinfo;
	struct BitMap *bmap;
	struct Color *col;
	UBYTE *buff, *src;
	LONG b, bpr;
	SHORT c, nc, y, p;

	if ((picinfo->ProcessData & PCDT_PALETTE) && picinfo->Palette)
	{
		nc = 1 << picinfo->Depth;
		for (c = 0, col = picinfo->Palette; c < nc; c++, col++)
		{
			gfxbuff->svgfx_Colors[c][0] = col->Red;
			gfxbuff->svgfx_Colors[c][1] = col->Green;
			gfxbuff->svgfx_Colors[c][2] = col->Blue;
		}
		picinfo->ProcessedData |= PCDT_PALETTE;
	}
	if (picinfo->ProcessData & PCDT_DISPID)
	{
		gfxbuff->svgfx_ViewMode32 = picinfo->DisplayID;

		if (GetDisplayInfoData(NULL, (UBYTE *)&dinfo, sizeof(struct DisplayInfo), DTAG_DISP, picinfo->DisplayID) > 0)
			gfxbuff->svgfx_NativeDIPF = dinfo.PropertyFlags;

		picinfo->ProcessedData |= PCDT_DISPID;
	}
	if ((picinfo->ProcessData & PCDT_IMAGE) && picinfo->BMap)
	{
		/*
		 *  planar bitmaps only are saved
		 */
		bmap = picinfo->BMap;
		buff = gfxbuff->svgfx_Buffer;
		bpr = gfxbuff->svgfx_BytesPerLine;

		for (p = 0; p < bmap->Depth; p++)
		{
			src = bmap->Planes[p];
			for (y = picinfo->Height; --y >= 0; )
			{
				for (b = 0; b < bpr; b++)
					*buff++ = *(src + b);
				src += bmap->BytesPerRow;
			}
		}
		picinfo->ProcessedData |= PCDT_IMAGE;
	}
}

/*
 *  Dummy patch function (pre V5 interface only)
 */
static LONG DummySeekFile(WORD mode, LONG bytes) { return(0); }

/*
 *  Image saving function
 */
__asm WORD __saveds WritePicture(register __a0 struct PictureInfo *picinfo)
{
	struct SV_GfxBuffer *gfxbuff;
	APTR handle;
	ULONG retval;
	WORD rc;

	if (!OpenLib())
		return(PIOERR_BADVER);

	if (gfxbuff = AllocGfxBuffer(picinfo->Width, picinfo->Height, picinfo->Depth))
	{
		/*  store image information in 'gfxbuff'
		 */
		PicInfoToGfxBuffer(picinfo, gfxbuff);
		retval = SVERR_NO_ERROR;
		handle = SVL_AllocHandle(NULL);
		if (handle)
		{
			if (!(retval = SVL_InitHandleAsDOS(handle, NULL)))
			{
				if (!(retval = SVL_SetWriteType(handle, picinfo->Data.Code, NULL)))
				{
					if (!(retval  = SVL_SetWriteName(handle, picinfo->FileName, NULL)))
					{
						if (!(retval = SVL_SetGfxBuffer(handle, gfxbuff, NULL)))
							retval = SVL_SuperWrite(handle);  /* write data */
					}
				}
			}
			SVL_FreeHandle(handle);
		}
		else retval = SVERR_NO_HANDLE;

		rc = SVErrToPIOErr(retval, FALSE);
		FreeGfxBuffer(gfxbuff);
	}
	else rc = PIOERR_NOMEMORY;

	CloseLib();

	/* fix custom_io bug of pre-V5 interface
	*/
	if (picinfo->Version < 5)
		picinfo->SeekFile = DummySeekFile;

	return(rc);
}

/*
 *  Image loading cleanup function (see GetPictureInfo() function)
 */
__asm void __saveds IOLibCleanup(register __a0 struct PictureInfo *picinfo)
{
	if (picinfo->User2)
	{
		SVL_FreeHandle(picinfo->User2);
		picinfo->User2 = NULL;
	}
	if (SuperViewBase)
		CloseLib();
}

/*
 *  Option query function
 */
__asm BOOL __saveds GetFirstOption(register __a0 struct DataInfo *dinfo,
				   register __a1 struct DataOption *option)
{
	struct SVO_ObjectNode *node;
	BOOL ok = FALSE;

	if (OpenLib())
	{
		node = GetDataInfo(dinfo, -1);
		OptionCount = 0;
		if (node)
			ok = GetOption(node->svo_FileName, option, OptionCount++, FALSE);
		CloseLib();
	}
	return(ok);
}

/*
 *  Option query function
 */
__asm BOOL __saveds GetNextOption(register __a0 struct DataInfo *dinfo,
				  register __a1 struct DataOption *option)
{
	struct SVO_ObjectNode *node;
	BOOL ok = FALSE;

	if (OpenLib())
	{
		node = GetDataInfo(dinfo, -1);
		if (node)
			ok = GetOption(node->svo_FileName, option, OptionCount++, FALSE);
		CloseLib();
	}
	return(ok);
}

/*
 *  Option set function
 */
__asm void __saveds SetOption(register __a0 struct DataInfo *dinfo,
			      register __a1 struct DataOption *option)
{
	struct SVO_ObjectNode *node;

	if (OpenLib())
	{
		node = GetDataInfo(dinfo, -1);
		if (node)
			WriteOption(node->svo_FileName, option);
		CloseLib();
	}
}

/*
 *  Option reset function
 *
 *  No action is performed: this library modifies system-wide
 *  (ENV: and ENVARC:) settings, which should not be involved
 *  in the client program option reset mechanism
 */
__asm void __saveds ResetOptions(register __a0 struct DataInfo *dinfo)
{
}

/*
 *  Private PersonalIO functions
 */
__asm WORD __saveds PersonalIOPrivate1()
{
	return(PIOERR_CANCEL);
}

__asm WORD __saveds PersonalIOPrivate2()
{
	return(PIOERR_CANCEL);
}

__asm WORD __saveds PersonalIOPrivate3()
{
	return(PIOERR_CANCEL);
}
