psxsdk/libpsx/src/libc.c

781 lines
13 KiB
C

/*
* small libc functions for the PSXSDK
*
* In this file, C functions either not implemented by the BIOS
* or very broken in the BIOS are implemented independently
*
* Some functions, such as printf, have both a BIOS implementation
* and an implementation here. The implementation here is prefixed
* with libc_ to not make confusion.
*/
#include <psx.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
int errno;
int __stdio_direction = STDIO_DIRECTION_BIOS;
static unsigned int __sio_cr_mapped = 0;
#define NUM_OF_FILE_STRUCTS 6
enum {SECTOR_SZ = 2048};
static FILE file_structs[NUM_OF_FILE_STRUCTS] =
{
[0] = // stdin
{
.fildes = 0,
.pos = 0,
.mode = O_RDONLY,
.dev = FDEV_CONSOLE,
.size = 0,
.used = 1,
.eof = 0,
.error = 0
},
[1] = // stdout
{
.fildes = 1,
.pos = 0,
.mode = O_WRONLY,
.dev = FDEV_CONSOLE,
.size = 0,
.used = 1,
.eof = 0,
.error = 0
},
[2] = // stderr
{
.fildes = 2,
.pos = 0,
.mode = O_WRONLY,
.dev = FDEV_CONSOLE,
.size = 0,
.used = 1,
.eof = 0,
.error = 0
},
[3 ... NUM_OF_FILE_STRUCTS - 1] =
{
.buf = (unsigned char[SECTOR_SZ]){}
}
};
FILE *const stdin = &file_structs[0];
FILE *const stdout = &file_structs[1];
FILE *const stderr = &file_structs[2];
#define IS_CONS_IN(f) (f->fildes == 0)
#define IS_CONS_OUT(f) (f->fildes == 1 || f->fildes == 2)
//unsigned char file_state[NUM_OF_FILE_STRUCTS];
int libc_get_transtbl_fname(const char *tofind, char *outstr, int outl);
unsigned int fmode_to_desmode(const char *fmode)
{
char rmode[16];
int x, y;
y = 0;
for(x=0;x<15;x++)
{
if(fmode[x] == 0)
break;
else
{
if(fmode[x] != 'b' && fmode[x] != 'f')
rmode[y++] = fmode[x];
}
}
rmode[y] = 0;
if(strcmp(rmode, "r") == 0)
{
return O_RDONLY;
}
else if(strcmp(rmode, "r+") == 0)
{
return O_RDWR;
}
else if(strcmp(rmode, "w") == 0)
{
return O_WRONLY | O_CREAT | O_TRUNC;
}
else if(strcmp(rmode, "w+") == 0)
{
return O_RDWR | O_CREAT | O_TRUNC;
}
else if(strcmp(rmode, "a") == 0)
{
dprintf("Append; open for writing. Create file if it doesn't exist.\n");
return O_WRONLY | O_APPEND;
}
else if(strcmp(rmode, "a+") == 0)
{
dprintf("Append; open for reading and writing. Create file if it doesn't exist.\n");
return O_RDWR | O_APPEND | O_CREAT;
}
else
{
return 0;
}
}
FILE *fdopen(int fildes, const char *mode)
{
// Adjust for malloc
int x;
// Find a free file structure
for(x = 0; x < NUM_OF_FILE_STRUCTS; x++)
{
FILE *const f = &file_structs[x];
if(f->used == 0)
{
f->size = 0;
f->eof = 0;
f->error = 0;
f->used = 1;
f->last_sect = 0;
f->cache_available = 0;
if (f->buf)
memset(f->buf, 0, SECTOR_SZ);
break;
}
}
// If we found no free file structure, return NULL pointer
if(x == NUM_OF_FILE_STRUCTS)
return NULL;
file_structs[x].fildes = fildes;
file_structs[x].pos = lseek(fildes, 0, SEEK_CUR);
file_structs[x].mode = fmode_to_desmode(mode);
return &file_structs[x];
}
static FILE *fopen_internal(const char *path, const char *mode, FILE *f)
{
int fd;
char *s = NULL;
if(strncmp(path, "cdromL:", 7) == 0)
{
#ifdef PSXSDK_CDROML_ENABLED
s = malloc(1024);
if(libc_get_transtbl_fname(path+7, s, 1024) == 0)
return NULL;
fd = open(s, fmode_to_desmode(mode));
#else
return NULL;
#endif
}
else
fd = open(path, fmode_to_desmode(mode));
if(fd == -1)
{
errno = ENOENT;
if(s!=NULL)free(s);
return NULL;
}
if(f == NULL)
f = fdopen(fd, mode);
else
{
f->fildes = fd;
f->pos = lseek(fd, 0, SEEK_CUR);
f->mode = fmode_to_desmode(mode);
}
if(f == NULL)
{
if(s!=NULL)free(s);
return NULL;
}
f->dev = FDEV_UNKNOWN;
if(strncmp(path, "cdrom", 5) == 0 || strncmp(path, "cdromL", 6) == 0)
f->dev = FDEV_CDROM;
else if(strncmp(path, "bu", 2) == 0)
f->dev = FDEV_MEMCARD;
// nocash bios freezes at get_real_file_size(), due
// to problems with firstfile()
if(s!=NULL)
{
f->size = get_real_file_size(s);
free(s);
}
else
f->size = get_real_file_size(path);
return f;
}
FILE *fopen(const char *path, const char *mode)
{
return fopen_internal(path, mode, NULL);
}
int fclose(FILE *stream)
{
stream->used = 0;
close(stream->fildes);
return 0;
}
static size_t cdromread(void *restrict ptr, size_t sz,
FILE *restrict const f)
{
const size_t start_sect = f->pos >> __builtin_ctz(SECTOR_SZ);
const size_t end_pos = f->pos + sz;
const size_t end_sector = end_pos >> __builtin_ctz(SECTOR_SZ);
size_t readb = 0;
int read_complete_sector = 0;
if (feof(f) || ferror(f))
return 0;
else if (f->cache_available)
{
const size_t first_sector_sz = f->pos % SECTOR_SZ;
if (f->last_sect == start_sect && f->last_sect == end_sector)
{
/* Use buffered I/O. */
size_t max;
if (end_pos >= f->size)
{
max = f->size - f->pos;
f->eof = 1;
}
else
{
max = sz;
}
memcpy(ptr, &f->buf[first_sector_sz], max);
f->pos += max;
readb = max;
}
else if (f->last_sect == start_sect)
{
/* Partial buffered I/O possible. */
const size_t firstsz = SECTOR_SZ - first_sector_sz;
memcpy(ptr, &f->buf[first_sector_sz], firstsz);
ptr = (char *)ptr + firstsz;
f->pos += firstsz;
f->cache_available = 0;
readb += firstsz;
read_complete_sector = 1;
}
}
const size_t rem = sz - readb;
if (rem)
{
const size_t whole_sectors = rem / SECTOR_SZ;
if (whole_sectors)
{
if (!read_complete_sector)
lseek(f->fildes, f->pos & ~(SECTOR_SZ - 1), SEEK_SET);
const size_t s = whole_sectors * SECTOR_SZ;
const int n = read(f->fildes, &((unsigned char *)ptr)[readb], s);
if (n >= 0)
{
readb += s;
f->pos += s;
ptr = (unsigned char *)ptr + s;
if (f->pos >= f->size)
f->eof = 1;
f->cache_available = 1;
}
}
if (f->pos < f->size)
{
int n;
if (!read_complete_sector)
lseek(f->fildes, f->pos & ~(SECTOR_SZ - 1), SEEK_SET);
n = read(f->fildes, f->buf, SECTOR_SZ);
if (n >= 0)
{
readb = n > rem ? rem : n;
memmove(ptr, f->buf, readb);
ptr = (unsigned char *)ptr + readb;
f->pos += readb;
if (f->pos >= f->size)
f->eof = 1;
f->cache_available = 1;
}
}
}
f->last_sect = end_sector;
return f->pos;
}
/*
* fread doesn't require reads to be carried in block unit
* Notice that however seeks on the CD drive will be very slow - so avoid using non block units
*
* This is done to make programming and porting easier
*/
size_t fread(void *restrict const ptr, const size_t size, const size_t nmemb,
FILE *restrict const f)
{
if (size && nmemb)
{
size_t rsize = size * nmemb;
switch (f->dev)
{
case FDEV_CDROM:
rsize = cdromread(ptr, rsize, f);
break;
case FDEV_CONSOLE:
/* Fall through. */
default:
return 0;
}
return rsize / size;
}
return 0;
}
int fgetc(FILE *f)
{
unsigned char c;
if(f->pos >= f->size)
return EOF;
fread(&c, sizeof(char), 1, f);
return (int)c;
}
int ftell(FILE *f)
{
return f->pos;
}
int fseek(FILE *f, int offset, int whence)
{
switch(whence)
{
case SEEK_SET:
f->pos = offset;
break;
case SEEK_CUR:
f->pos+= offset;
break;
case SEEK_END:
f->pos = f->size + offset;
break;
default:
f->pos = whence + offset;
break;
}
return 0;
}
void rewind(FILE *f)
{
fseek(f, 0, SEEK_SET);
clearerr(f);
}
int toupper(int c)
{
if(c >= 'a' && c <= 'z')
return (c-'a')+'A';
return c;
}
int tolower(int c)
{
if(c >= 'A' && c <= 'Z')
return (c-'A')+'a';
return c;
}
int libc_get_transtbl_fname(const char *tofind, char *outstr, int outl)
{
FILE *f;
int s;
int x;
int type = 0;
int y;
int l = strlen(tofind);
int filename_found = 0;
int exit_loop = 0;
int otfp = 0;
int tfp = 0;
char transtbl[0x4000];
char orgname[16];
char newname[256];
char rootpath[256];
bzero(transtbl, 0x4000);
strcpy(rootpath, "cdrom:\\");
f = fopen("cdrom:\\TRANS.TBL;1", "rb");
if(f == NULL)
return 0;
fseek(f, 0, SEEK_END);
s = ftell(f);
fseek(f, 0, SEEK_SET);
fread(transtbl, 1, s, f);
fclose(f);
outstr[0] = 0;
x = 0;
exit_loop = 0;
filename_found = 0;
for(tfp = 0; tofind[tfp] == '\\' || tofind[tfp] == '/'; tfp++);
otfp = tfp;
for(y = otfp; y < l; y++)
{
if(tofind[y] == '\0' || tofind[y] == '\\' || tofind[y] == '/')
break;
}
tfp = y;
while((x < s) && !exit_loop)
{
while(transtbl[x] == ' ' || transtbl[x] == '\t' || transtbl[x] == '\n' || transtbl[x] == '\r')
x++;
if(transtbl[x] == 'F')
type = 0;
else if(transtbl[x] == 'D')
type = 1;
x++;
while(transtbl[x] == ' ' || transtbl[x] == '\t' || transtbl[x] == '\n' || transtbl[x] == '\r')
x++;
y = 0;
while(!(transtbl[x] == ' ' || transtbl[x] == '\t' || transtbl[x] == '\n' || transtbl[x] == '\r'
|| transtbl[x] == 0))
orgname[y++] = transtbl[x++];
orgname[y] = 0;
//printf("orgname = %s\n", orgname);
while(transtbl[x] == ' ' || transtbl[x] == '\t' || transtbl[x] == '\n' || transtbl[x] == '\r')
x++;
y = 0;
while(!(transtbl[x] == '\n' || transtbl[x] == '\r' || transtbl[x] == 0))
newname[y++] = transtbl[x++];
newname[y] = 0;
while(transtbl[x] == ' ' || transtbl[x] == '\t' || transtbl[x] == '\n' || transtbl[x] == '\r')
x++;
//printf("newname = %s\n", newname);
if(strncasecmp(&tofind[otfp], newname, tfp-otfp) == 0)
{
if(type == 0)
{
dprintf("Filename found: %s -> %s%s\n", tofind, rootpath, orgname);
filename_found = 1;
exit_loop = 1;
strncpy(outstr, rootpath, outl);
strncat(outstr, orgname, outl-strlen(rootpath));
}
else
{
//printf("Found directory! %s\n", newname);
//printf("tfp = %d\n", tfp);
if(tfp == l || tofind[l-1] == '/'
|| tofind[l-1] == '\\')
{
dprintf("File not found. A directory was specified.\n");
exit_loop = 1;
continue;
}
//tfp++;
for(; tofind[tfp] == '\\' || tofind[tfp] == '/'; tfp++);
otfp = tfp;
for(y = otfp; y < l; y++)
{
if(tofind[y] == '\0' || tofind[y] == '\\' || tofind[y] == '/')
break;
}
tfp = y;
strcat(rootpath, orgname);
strcat(rootpath, "\\");
y = strlen(rootpath);
strcat(rootpath, "TRANS.TBL;1");
bzero(transtbl, 0x4000);
f = fopen(rootpath, "rb");
if(f == NULL)
{
dprintf("Couldn't find %s\n", rootpath);
exit_loop = 1;
continue;
}
rootpath[y] = 0;
fseek(f, 0, SEEK_END);
s = ftell(f);
fseek(f, 0, SEEK_SET);
fread(transtbl, 1, s, f);
fclose(f);
x = 0;
}
}
}
return filename_found;
}
int isupper(int c)
{
return (c >= 'A' && c <= 'Z');
}
int islower(int c)
{
return (c >= 'a' && c <= 'z');
}
int isdigit(int c)
{
return (c >= '0' && c <= '9');
}
int isxdigit(int c)
{
return ((c >= '0' && c <= '9') || (c >= 'A' && c<='F') || (c >= 'a' && c<='f'));
}
int isalpha(int c)
{
return ((c>='a' && c<='z') || (c>='A' && c<='Z'));
}
int isalnum(int c)
{
return ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'));
}
int isspace(int c)
{
return ((c == ' ') || (c == '\f') || (c == '\n') || (c == '\r') || (c == '\t') || (c == '\v'));
}
int isprint(int c)
{
return (c >= 0x20) && (c <= 0x7E);
}
int isgraph(int c)
{
return (c > 0x20) && (c <= 0x7E);
}
int iscntrl(int c)
{
return (c < 0x20);
}
int isblank(int c)
{
return ((c == ' ') || (c == '\t'));
}
void redirect_stdio_to_sio(void)
{
__stdio_direction = STDIO_DIRECTION_SIO;
}
void sio_stdio_mapcr(unsigned int setting)
{
__sio_cr_mapped = setting;
}
int sio_putchar(int c)
{
if(c == '\n' && __sio_cr_mapped)
sio_putchar('\r');
while(!SIOCheckOutBuffer());
SIOSendByte(c);
return c;
}
int sio_puts(const char *str)
{
while(*str)
sio_putchar(*(str++));
sio_putchar('\n');
return 1;
}
extern int bios_putchar(int c);
extern int bios_puts(const char *str);
int putchar(int c)
{
switch(__stdio_direction)
{
case STDIO_DIRECTION_BIOS:
return bios_putchar(c);
break;
case STDIO_DIRECTION_SIO:
return sio_putchar(c);
break;
}
return EOF;
}
int puts(const char *str)
{
switch(__stdio_direction)
{
case STDIO_DIRECTION_BIOS:
return bios_puts(str);
break;
case STDIO_DIRECTION_SIO:
return sio_puts(str);
break;
}
return EOF;
}
size_t fwrite(const void *restrict const ptr, const size_t size, const size_t nmemb,
FILE *restrict const f)
{
if(IS_CONS_OUT(f)) // stdout or stderr
{
const char *c = ptr;
int i;
for(i = 0; i < size; i++)
putchar(c[i]);
return size;
}
return 0;
}
int fputs(const char *str, FILE *stream)
{
if(IS_CONS_OUT(stream))
return puts(str);
return EOF;
}
FILE *freopen(const char *path, const char *mode, FILE *stream)
{
if(stream == NULL)
return NULL;
if(stream->used)
fclose(stream);
return fopen_internal(path, mode, stream);
}
void clearerr(FILE *stream)
{
stream->eof = 0;
stream->error = 0;
}
int feof(FILE *stream)
{
return stream->eof;
}
int ferror(FILE *stream)
{
return stream->error;
}
int fileno(FILE *stream)
{
return stream->fildes;
}