/* ファイル関係 */

#include <string.h>
#include "bootpack.h"

#define MODE_READ	0x0001
#define MODE_WRITE	0x0002
#define	MODE_ADD	0x0004

char *toupper(char *s)
{
	int i;
	char c;

	for (i = 0; i < strlen(s); i++) {
		c = s[i];
		if ('a' <= c && c <= 'z')
			s[i] = c - ('a' - 'A');
	}

	return s;
}

FILE *fopen(const char *filename, const char *mode)
{
	int controller, deviceNo;
	static char fname[15];
	FILE *fp;
	IDEDriver *driver;
	PrimaryVolumeDescriptor *pvd;
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;

	/* IDE初期化 */
	driver = (IDEDriver *) memman_alloc_4k(memman, sizeof(IDEDriver));
	IDEDriver_IDEDriver(driver, IDE_IRQ_PRIMARY, IDE_IRQ_SECONDARY);

	/* ATAPI デバイスの検索 */
	if (!IDEDriver_findDevice(driver, DEVICE_ATAPI, 0x05, &controller, &deviceNo))
		return NULL;

	/* ATAPI デバイスの使用開始 */
	if (!IDEDriver_selectDevice(driver, controller, deviceNo))
		return NULL;

	/* 基本ボリューム記述子の読み込み */
	pvd = (PrimaryVolumeDescriptor*) memman_alloc_4k(memman, sizeof(PrimaryVolumeDescriptor));
	IDEDriver_read(driver, 16, pvd, SECTOR_SIZE);	/* 16セクターまではブート領域なので */
	if (pvd->type[0] != ISO_PRIMARY_VOLUME_DESCRIPTOR || strncmp("CD001", pvd->id, 5) != 0)
		return NULL;

	strcpy(fname, filename);
	toupper(fname);

	/* ディレクトリエントリを読み込む */
	byte *buffer;
	dword sz, position;
	DirectoryEntry *root, *dir;

	root = (DirectoryEntry *)(pvd->root_directory_record);
	sz = ((dword)((root->size_l + SECTOR_SIZE - 1) / SECTOR_SIZE)) * SECTOR_SIZE;
	buffer = (byte *) memman_alloc_4k(memman, sz);

	IDEDriver_read(driver, root->extent_l, buffer, sz);

	for (position = 0; position < sz;) {
		dir = (DirectoryEntry *) (buffer + position);
		if (dir->size_l == 0) {
			memman_free_4k(memman, (int) buffer, sz);
			return NULL;
		} else if (dir->directory == 0) {
			/* ファイル名が一致したら */
			if (strncmp(dir->name, fname, strlen(fname)) == 0) {
				fp = (FILE *) memman_alloc_4k(memman, sizeof(FILE));
				break;
			}
		}
		position += dir->length;
	}
	memman_free_4k(memman, (int) buffer, sz);


	/* FILEポインタの値セット */
	fp->size = dir->size_l;
	fp->driver = driver;
	fp->pvd = pvd;
	fp->name = fname;

	if (!strncmp(mode, "r", 1)) {
		fp->mode = MODE_READ;
	}
	if (!strncmp(mode, "w", 1)) {
		fp->mode = MODE_WRITE;
	}
	if (!strcmp(mode, "a")) {
		fp->mode = MODE_ADD;
	}
	if (!strncmp(mode, "rw", 2)) {
		fp->mode = MODE_READ | MODE_WRITE;
	}

	return fp;
}

int fclose(FILE *fp)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	memman_free_4k(memman, (int) fp->driver, sizeof(IDEDriver));
	memman_free_4k(memman, (int) fp, sizeof(FILE));
	return 1;
}

int fread(void *buf, int size, int count, FILE *fp)
{
	/* count = 1以外サポートしない */
	if (count != 1)
	{
		return 0;
	}
	/* ぬるぽ */
	if (fp == NULL)
	{
		/* ガッ */
		return 0;
	}

	/* ディレクトリエントリを読み込む */
	byte *buffer;
	dword sz, s, position;
	DirectoryEntry *root, *dir;
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;

	root = (DirectoryEntry *)(fp->pvd->root_directory_record);
    sz = ((dword)((root->size_l + SECTOR_SIZE - 1) / SECTOR_SIZE)) * SECTOR_SIZE;
    buffer = (byte *) memman_alloc_4k(memman, sz);

    IDEDriver_read(fp->driver, root->extent_l, buffer, sz);

    for (position = 0; position < sz;)
	{
        dir = (DirectoryEntry *) (buffer + position);
        if (dir->size_l == 0)
		{
            break;
        }
		else if (dir->directory == 0)
		{
			/* ファイル名が一致したら */
            if (strncmp(dir->name, fp->name, strlen(fp->name)) == 0)
			{
				/* 大きさを求める */
				if (dir->size_l > size)
					s = size;
				else
					s = dir->size_l;
				/* バッファにカキコφ(..) */
                IDEDriver_read(fp->driver, dir->extent_l, buf, s);
                return 1;
            }
        }
        position += dir->length;
    }
    memman_free_4k(memman, (int) buffer, sz);
	return 0;
}

void file_readfat(int *fat, unsigned char *img)
/* ディスクイメージ内のFATの圧縮をとく */
{
	int i, j = 0;
	for (i = 0; i < 2880; i += 2) {
		fat[i + 0] = (img[j + 0]      | img[j + 1] << 8) & 0xfff;
		fat[i + 1] = (img[j + 1] >> 4 | img[j + 2] << 4) & 0xfff;
		j += 3;
	}
	return;
}

void file_loadfile(int clustno, int size, char *buf, int *fat, char *img)
{
	int i;
	for (;;) {
		if (size <= 512) {
			for (i = 0; i < size; i++) {
				buf[i] = img[clustno * 512 + i];
			}
			break;
		}
		for (i = 0; i < 512; i++) {
			buf[i] = img[clustno * 512 + i];
		}
		size -= 512;
		buf += 512;
		clustno = fat[clustno];
	}
	return;
}

struct FILEINFO *file_search(char *name, struct FILEINFO *finfo, int max)
{
	int i, j;
	char s[12];
	for (j = 0; j < 11; j++) {
		s[j] = ' ';
	}
	j = 0;
	for (i = 0; name[i] != 0; i++) {
		if (j >= 11) { return 0; /* 見つからなかった */ }
		if (name[i] == '.' && j <= 8) {
			j = 8;
		} else {
			s[j] = name[i];
			if ('a' <= s[j] && s[j] <= 'z') {
				/* 小文字は大文字に直す */
				s[j] -= 0x20;
			} 
			j++;
		}
	}
	for (i = 0; i < max; ) {
		if (finfo->name[0] == 0x00) {
			break;
		}
		if ((finfo[i].type & 0x18) == 0) {
			for (j = 0; j < 11; j++) {
				if (finfo[i].name[j] != s[j]) {
					goto next;
				}
			}
			return finfo + i; /* ファイルが見つかった */
		}
next:
		i++;
	}
	return 0; /* 見つからなかった */
}

char *file_loadfile2(int clustno, int *psize, int *fat)
{
	int size = *psize, size2;
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	char *buf, *buf2;
	buf = (char *) memman_alloc_4k(memman, size);
	file_loadfile(clustno, size, buf, fat, (char *) (ADR_DISKIMG + 0x003e00));
	if (size >= 17) {
		size2 = tek_getsize(buf);
		if (size2 > 0) {	/* tek圧縮がかかっていた */
			buf2 = (char *) memman_alloc_4k(memman, size2);
			tek_decomp(buf, buf2, size2);
			memman_free_4k(memman, (int) buf, size);
			buf = buf2;
			*psize = size2;
		}
	}
	return buf;
}
