/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/


#include	<stdlib.h>
#include	<fcntl.h>
#include	<errno.h>
#include	<sys/types.h>
#include	"r64.h"
#include	"r64_types.h"
#include	"change_endian.h"
#include	"machine/include.h"

R64_FILE *
r64_open_file(
	char * filename,
	int ctl,
	int mode,
	char filetype,
	int width,
	int height)
{
int fd;
R64_FILE * f;
int er;
int dsize;
int i,j;
R64_CACHE_ENT * ce;
unsigned int level_ofs;
int w,h;

retry:
	fd = open64(filename,ctl,mode);
	if ( fd < 0 ) {
		if ( errno == EAGAIN )
			goto retry;
		r64_error = E_SYS;
		return 0;
	}
	f = malloc(sizeof(*f));
	if ( f == 0 ) {
		r64_error = E_NOMEM;
		close(fd);
		return 0;
	}
	f->fd = fd;
	er = read(fd,(char*)&f->header,sizeof(R64_HEADER)) ;
	if ( er < 0 ) {
		r64_error = E_SYS;
		goto err0;
	}
	if ( er != sizeof(R64_HEADER) ) {
		if ( er == 0 && (ctl&(O_RDWR|O_RDONLY)) == O_RDWR ) {
			memcpy(f->header.preamble,"R64",3);
			f->header.type = filetype;
			f->header.width = width;
			f->header.height = height;
			change_endian_s(f->header.height);
			change_endian_s(f->header.width);
			write(f->fd,(char*)&f->header,sizeof(f->header));
		}
		else {
			r64_error = E_DFILE;
			goto err0;
		}
	}
	change_endian_s(f->header.height);
	change_endian_s(f->header.width);
	if ( memcmp(f->header.preamble,"R64",3) ) {
		r64_error = E_INVFILE;
		goto err0;
	}
	switch ( f->header.type ) {
	case 'B':
		dsize = 8*64;
		break;
	case 'P':
		dsize = 64*64*3;
		break;
	case 'G':
		dsize = 64*64;
		break;
	default:
		r64_error = E_INVFILE;
		return 0;
	}
	f->free_list = &f->cache[1];
	for ( i = 1 ; i < ENT_LEN ; i ++ ) {
		f->cache[i].buf = malloc(dsize);
		if ( f->cache[i].buf == 0 )
			goto err1;
		if ( i+1 != ENT_LEN )
			f->cache[i].next = &f->cache[i+1];
		else	f->cache[i].next = 0;
	}
	f->cache[0].next = f->cache[0].prev = &f->cache[0];
	f->fctl = ctl&(O_RDONLY|O_RDWR);
	level_ofs = sizeof(R64_HEADER);
	w = f->header.width;
	h = f->header.height;
	if ( w == 0 || h == 0 ) {
		r64_error = E_INVFILE;
		return 0;
	}
	for ( i = 0 ; i < LEVEL_OFS_LEN ; i ++ ) {
		f->level_ofs[i] = level_ofs;
		f->level_w[i] = w;
		f->level_h[i] = h;
		level_ofs += w*h*ENT_UNIT_VOL(f);
		w = (w>>1)+(w&1);
		h = (h>>1)+(h&1);
		if ( w <= 1 || h <= 1 )
			break;
	}
	for ( ; i < LEVEL_OFS_LEN ; i ++ ) {
		f->level_ofs[i] = 0;
		f->level_w[i] = 0;
		f->level_h[i] = 0;
	}
	return f;
err1:
	i --;
	for ( ; i > 0 ; i -- )
		free(f->cache[i].buf);
err0:
	close(f->fd);
	free(f);
	return 0;
}
