707 lines
12 KiB
C
707 lines
12 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>
|
|
|
|
char onesec_buf[2048];
|
|
int errno;
|
|
int __stdio_direction = STDIO_DIRECTION_BIOS;
|
|
static unsigned int __sio_cr_mapped = 0;
|
|
|
|
#define NUM_OF_FILE_STRUCTS 259
|
|
|
|
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
|
|
},
|
|
};
|
|
|
|
FILE *stdin = &file_structs[0];
|
|
FILE *stdout = &file_structs[1];
|
|
FILE *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++)
|
|
{
|
|
if(file_structs[x].used == 0)
|
|
{
|
|
bzero(&file_structs[x], sizeof(FILE));
|
|
file_structs[x].used = 1;
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
|
|
int fread(void *ptr, int size, int nmemb, FILE *f)
|
|
{
|
|
int rsize = size * nmemb;
|
|
int csize = rsize;
|
|
int max;
|
|
int nsect = (f->pos + rsize) >> 11;
|
|
nsect -= f->pos >> 11;
|
|
nsect++;
|
|
|
|
//printf("f->dev = %d, f->pos = %d, rsize = %d\n", f->dev, f->pos, rsize);
|
|
|
|
if(f->dev == FDEV_CDROM)
|
|
{
|
|
// First sector
|
|
lseek(f->fildes, f->pos & (~0x7ff), SEEK_SET);
|
|
read(f->fildes, onesec_buf, 2048);
|
|
|
|
max = 2048 - (f->pos & 2047);
|
|
|
|
//printf("ptr(FIRST) = %d, %x\n", ptr, ptr);
|
|
printf("rsize = %d\n", rsize);
|
|
|
|
memcpy(ptr, onesec_buf + (f->pos & 2047), (rsize > max) ? max : rsize);
|
|
|
|
// Middle sector
|
|
ptr += max;
|
|
|
|
//printf("ptr(MIDDLEsex) = %d, %x\n", ptr, ptr);
|
|
nsect--;
|
|
csize -= max;
|
|
|
|
if(nsect > 1)
|
|
{
|
|
//lseek(f->fildes, (f->pos & (~0x7ff)) + 2048, SEEK_SET);
|
|
|
|
//#warning "Check correctness of this calculation."
|
|
|
|
/*if(rsize & 2047)
|
|
sect_num = (rsize|2047)+1;
|
|
else
|
|
sect_num = rsize;
|
|
|
|
sect_num -= 4096;*/
|
|
|
|
//printf("read_middle=%d, sect_num = %d\n", read(f->fildes, ptr, sect_num), sect_num);
|
|
|
|
read(f->fildes, ptr, (nsect - 1) * 2048);
|
|
|
|
ptr += (nsect - 1) * 2048;
|
|
csize -= (nsect - 1) * 2048;
|
|
nsect = 1;
|
|
}
|
|
|
|
//printf("ptr(LAST) = %d, %x\n", ptr, ptr);
|
|
|
|
if(nsect == 1)
|
|
{
|
|
// Last sector
|
|
read(f->fildes, onesec_buf, 2048);
|
|
|
|
memcpy(ptr, onesec_buf, csize);
|
|
}
|
|
}
|
|
else if(f->dev == FDEV_CONSOLE)
|
|
return 0;
|
|
|
|
|
|
if(f->dev != FDEV_CONSOLE)
|
|
{
|
|
f->pos+= rsize;
|
|
f->eof = (f->pos > f->size);
|
|
|
|
if(f->eof)
|
|
f->pos = f->size;
|
|
}
|
|
|
|
return rsize;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int fwrite(void *ptr, int size, int nmemb, FILE *f)
|
|
{
|
|
if(IS_CONS_OUT(f)) // stdout or stderr
|
|
{
|
|
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;
|
|
}
|