/* ファイル関係 */

#include "bootpack.h"

void fat_read(int *fat, unsigned char *img)
{
	int i, j = 0;

	for(i = 0; i < 2880; i += 2) {
		fat[i] = (img[j] | img[j + 1] << 8) & 0xfff;
		fat[i + 1] = (img[j + 1] >> 4 | img[j + 2] << 4) & 0xfff;
		j += 3;
	}
	return;
}

void fat_write(int no, int data)
{
	unsigned char *img = (unsigned char *) FAT_ADDR;
	int i = no / 2, j, k;

	/* イメージにデータを書き込む */
	fat[no] = data;

	if((no & 1) == 0) {	/* noが偶数なら前のものと一緒に更新 */
		j = fat[no - 1];
		k = fat[no];
	} else {	/* noが奇数なら後ろのものと一緒に更新 */
		j = fat[no];
		k = fat[no + 1];
	}

	/* マスタを更新 */
	img[i * 3] = (unsigned char) (j & 0x0ff);
	img[i * 3 + 1] = (unsigned char) ((k & 0x00f) << 4 | (j & 0xf00) >> 8);
	img[i * 3 + 2] = (unsigned char) ((k & 0xff0) >> 4);

	/* スレーブを更新 */
	img[2880 + i * 3] = img[i * 3];
	img[2880 + i * 3 + 1] = img[i * 3 + 1];
	img[2880 + i * 3 + 2] = img[i * 3 + 1];
	return;
}

int fat_flush(void)
{
	int i, j;

	/* I/Oに処理要求 */
	for(i = 1; i <= 18; i++) {
		io_req(IO_WRITE, i);
	}

	/* I/Oからの処理状態取得 */
	for(i = 1; i <= 18; i++) {
		j = io_stat(IO_WRITE, i);
		if(j == 1) {
			return 1;
		}
	}
	return 0;
}

int fat_get(void)
{
	int i;

	for(i = 0; i < 2880; i++) {
		if(fat[i] == 0x000) {
			fat[i] = 0x1000;
			return i;
		}
	}
	return 0x000;
}

struct file *file_search(unsigned char *name)
{
	struct file *file = (struct file *) FILE_ENTRY_ADDR;
	int i, j;
	char s[11];

	/* ファイル名の準備 */
	for(i = 0; i < 11; i++) {
		s[i] = ' ';
	}
	j = 0;
	for(i = 0; i < 11 && name[j]; j++) {
		if(name[j] == '.' && i <= 8) {
			i = 8;
		} else {
			s[i] = name[j];
			if('a' <= s[i] && s[i] <= 'z')	/* 大文字へ変換 */
				s[i] -= 0x20;
			i++;
		}
	}

	/* ファイルの検索 */
	for(i = 0; i < 224; i++) {
		if((file[i].name[0] != 0x00) && ((file[i].type&0x18) == 0)) {
			for(j = 0;j < 11;j++) {
				if(s[j] != file[i].name[j]) {
					break;
				}
			}
			if(j == 11) {
				return &file[i];
			}
		} else {
			return 0;
		}
	}
	return 0;
}

struct file *file_create(unsigned char *name)
{
	struct file *file;
	unsigned short cno;
	int i, j, k;
	char s[11];

	for(i = 0; i < 12 && name[i] != 0; i++) {	/* ファイル名の正当性をチェック */
		if(name[i] == 0) {	/* 終端 */
			if(i > 0) {
				break;
			} else {	/* 無名のファイル */
				return (struct file *) -3;
			}
		} else if(name[i] == '\\') { return (struct file *) -4; }
		else if(name[i] == '/') { return (struct file *) -4; }
		else if(name[i] == ':') { return (struct file *) -4; }
		else if(name[i] == ',') { return (struct file *) -4; }
		else if(name[i] == ';') { return (struct file *) -4; }
		else if(name[i] == '*') { return (struct file *) -4; }
		else if(name[i] == '?') { return (struct file *) -4; }
		else if(name[i] == '\"') { return (struct file *) -4; }
		else if(name[i] == '>') { return (struct file *) -4; }
		else if(name[i] == '<') { return (struct file *) -4; }
		else if(name[i] == '|') { return (struct file *) -4; }
	}
	file = file_search(name);
	if(file != 0) {
		return (struct file *) -1;
	} else {
		/* エントリを検索 */
		file = (struct file *) FILE_ENTRY_ADDR;
		for(i = 0; i < 224; i++) {
			if((file[i].name[0] == 0x00) || (file[i].name[0] == 0xe5)) {
				break;
			}
		}
		if(i == 224) {
			return (struct file *) -2;
		}
		k = i;
		file = &file[k];

		/* ファイル名の準備 */
		for(i = 0; i < 11; i++) {
			s[i] = ' ';
		}
		j = 0;
		for(i = 0; i < 11 && name[j]; ) {
			if(name[j] == '.' && i <= 8) {
				i = 8;
				j++;
			} else {
				s[i] = name[j];
				if('a' <= s[i] && s[i] <= 'z') {/* 大文字へ変換 */
					s[i] -= 0x20;
				}
				i++;
				j++;
			}
		}
		for(i = 0; i < 11; i++) {
			file->name[i] = s[i];
		}

		/* 内容の初期化 */
		file->type = FILE_DEFAULT;
		cno = fat_get();
		file->cno = cno;
		fat_write(cno, 0x0fff);
		file->size = 0;
		i = fat_flush();
		if(i != 0) {
			return 0;
		}

		/* 時刻の初期化 */
		file_settime(file);
		return file;
	}

	return 0;
}

int file_delete(struct file *file)
{
	int cno, ncno, i;

	/* ファイルエントリの削除 */
	file->name[0] = 0xe5;

	/* FATへのマーク解除 */
	ncno = file->cno;
	for(; fat[ncno] >= 0x0ff8;) {
		cno = ncno;
		ncno = fat[cno];
		fat_write(cno, 0x0000);
	}
	i = fat_flush();
	if(i != 0) {
		return 1;
	}
	return 0;
}

char *file_read(int cno, int *s)
{
	int size = *s, size2, i;
	char *b, *b2;

	b = (char *) memory_alloc(mem, size);
	i = file_readsub(b, cno, size);
	if(i != 0) {
		return 0;
	}
	if(size >= 17) {
		size2 = tek_getsize(b);
		if(size2 > 0) {	/* tek圧縮がかかっていた */
			b2 = (char *) memory_alloc(mem, size2);
			tek_decomp(b, b2, size2);
			memory_free(mem, (int) b, size);
			b = b2;
			*s = size2;
		}
	}
	return b;
}

int file_write(struct fhandle *fh, int *fat, int size)
{
 	char *img = (char *) FILE_DATA_ADDR;
	int wsize = 0, ncno = fh->cno, cno = 0, size0 = size, i;
	char *buf;

	buf = fh->buf;
	for(;;) {
		cno = ncno;
		/* 最後の512b以下ならしっかり処理をして、抜ける */
		if(size <= 512) {
			for(i = 0; i < size; i++) {
				img[cno * 512 + i] = buf[i];
			}
			wsize += i;
			if(fat[cno] == 0x1000) {
				fat_write(cno, 0x0fff);
			}
			/* I/Oに処理要求 */
			io_req(IO_WRITE, 31 + cno);
			break;
		}

		/* イメージへ書き込む */
		for(i = 0; i < 512; i++) {
			img[cno * 512 + i] = buf[i];
		}
		buf += 512;
		size -= 512;
		wsize += 512;

		/* I/Oに処理要求 */
		io_req(IO_WRITE, 31 + cno);

		/* FATが足りないなら付け足す */
		if(fat[cno] == 0x000 || fat[cno] >= 0xff8) {
			fat_write(cno, 0x0fff);
			ncno = fat_get();
			fat_write(cno, ncno);
		} else {
			ncno = fat[cno];
		}
	}

	/* I/Oからの処理状態取得 */
	cno = fh->cno;
	for(;;) {
		if(io_stat(IO_WRITE, 31 + cno) == 1) {
			return 1;
		}
		size0 -= 512;
		if(size0 <= 0) {
			break;
		}
		cno = fat[cno];
	}

	/* FATを更新 */
	i = fat_flush();
	if(i != 0) {
		return -1;
	}

	/* ファイルエントリを更新 */
	file_settime(fh->f);
	if(fh->f->size < fh->pos + wsize) {
		fh->size += wsize - fh->f->size % 512;
		fh->f->size = fh->size;
	}
	i = ((int) fh->f - DISKIMG_ADDR) / 512;
	io_req(IO_WRITE, i);
	if(io_stat(IO_WRITE, i) == 1) {
		return 1;
	}

	/* バッファを再初期化 */
	fh->bpos = 0;
	file_setbuf(fh, fat, cno);
	return wsize;
}

int file_readsub(char *buf, int cno, int size)
{
	char *img = (char *) FILE_DATA_ADDR;
	int cno2 = cno, size2 = size, i;

	/* I/Oに処理要求 */
	for(;;) {
		io_req(IO_READ, 31 + cno2);
		size2 -= 512;
		if(size2 <= 0) {
			break;
		}
		cno2 = fat[cno2];
	}

	for(;;) {
		/* I/Oからの処理状態取得 */
		if(io_stat(IO_READ, 31 + cno) == 1) {
			return 1;
		}

		/* バッファへ格納 */
		if(size <= 512) {
			for(i = 0; i < size; i++) {
				buf[i] = img[cno * 512 + i];
			}
			return 0;
		}
		for(i = 0; i < 512; i++) {
			buf[i] = img[cno * 512 + i];
		}
		size -= 512;
		buf += 512;
		cno = fat[cno];
	}
}

void file_setbuf(struct fhandle *fh, int *fat, int sect)
{
	int i;
	char *img = (char *) FILE_DATA_ADDR;

	/* 切り替える前に今の内容をフラッシュする */
	if(fh->bpos != 0) {
		file_write(fh, fat, fh->bpos);
	}

	/* 4kのバッファを保障する。セクタのデータを入れて
	   おくことで処理が簡素化される。 */
	memory_free(mem, (int) fh->buf, fh->bsize);
	fh->buf = (char *) memory_alloc(mem, 4096);
	fh->bsize = 4096;
	fh->cno = sect;
	for(i = 0; i < 512; i++) {
		fh->buf[i] = img[fh->cno * 512 + i];
	}
	return;
}

void file_settime(struct file *file)
{
	struct clock clck;

	clock_get(&clck);
	file->time = (clck.hour - 9) << 9 | clck.min << 5 | clck.sec >> 2;
	file->date = (clck.year - 1980) << 11 | clck.month << 5 | clck.day;
	return;
}

void file_gettime(struct file *file, struct clock *clck)
{
	clck->sec = file->time & 0x1f;
	clck->min = (file->time >> 5) & 0x0f;
	clck->hour = (file->time >> 9) + 9;
	clck->day = file->date & 0x1f;
	clck->month = (file->date >> 5) & 0x3f;
	clck->year = (file->date >> 11) + 1980;
	return;
}
