/* * 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 #include #include #include #include #include #include #include #include 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; }