diff options
| author | John Wilbert M. Villamor <lameguy64@gmail.com> | 2019-11-22 09:11:12 +0800 |
|---|---|---|
| committer | John Wilbert M. Villamor <lameguy64@gmail.com> | 2019-11-22 09:11:12 +0800 |
| commit | ea46d05aed0343c20d8fdfaa0e67d54d51e8e2a0 (patch) | |
| tree | 43e2a53f4e5f7f55b075cfc9d6dc7a652a7b0837 /libpsn00b/libpsxcd/cdsearchfile.c | |
| parent | d80d92e13330d527ddb94420b19f9e21bf0e74eb (diff) | |
| download | psn00bsdk-ea46d05aed0343c20d8fdfaa0e67d54d51e8e2a0.tar.gz | |
Added long awaited CD-ROM library and loads of fixes, see changelog for details
Diffstat (limited to 'libpsn00b/libpsxcd/cdsearchfile.c')
| -rw-r--r-- | libpsn00b/libpsxcd/cdsearchfile.c | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/libpsn00b/libpsxcd/cdsearchfile.c b/libpsn00b/libpsxcd/cdsearchfile.c new file mode 100644 index 0000000..60e4377 --- /dev/null +++ b/libpsn00b/libpsxcd/cdsearchfile.c @@ -0,0 +1,663 @@ +#include <stdio.h> +#include <malloc.h> +#include <string.h> +#include <psxgpu.h> +#include <psxsio.h> +#include "psxcd.h" + +// Uncommend to enable debug output +//#define DEBUG + +#pragma pack(push, 1) + +/// Structure of a double-endian unsigned short word +typedef struct ISO_USHORT_PAIR +{ + unsigned short lsb; /// LSB format 16-bit word + unsigned short msb; /// MSB format 16-bit word +} ISO_USHORT_PAIR; + +/// Structure of a double-endian unsigned int word +typedef struct ISO_UINT_PAIR +{ + unsigned int lsb; /// LSB format 32-bit word + unsigned int msb; /// MSB format 32-bit word +} ISO_UINT_PAIR; + +/// ISO descriptor header structure +typedef struct ISO_DESCRIPTOR_HEADER +{ + unsigned char type; /// Volume descriptor type (1 is descriptor, 255 is descriptor terminator) + char id[5]; /// Volume descriptor ID (always CD001) + unsigned short version; /// Volume descriptor version (always 0x01) +} ISO_DESCRIPTOR_HEADER; + +/// Structure of a date stamp for ISO_DIR_ENTRY structure +typedef struct ISO_DATESTAMP +{ + unsigned char year; /// number of years since 1900 + unsigned char month; /// month, where 1=January, 2=February, etc. + unsigned char day; /// day of month, in the range from 1 to 31 + unsigned char hour; /// hour, in the range from 0 to 23 + unsigned char minute; /// minute, in the range from 0 to 59 + unsigned char second; /// Second, in the range from 0 to 59 + unsigned char GMToffs; /// Greenwich Mean Time offset +} ISO_DATESTAMP; + +/// Structure of an ISO path table entry (specifically for the cd::IsoReader class) +typedef struct ISO_PATHTABLE_ENTRY +{ + unsigned char nameLength; /// Name length (or 1 for the root directory) + unsigned char extLength; /// Number of sectors in extended attribute record + unsigned int dirOffs; /// Number of the first sector in the directory, as a double word + unsigned short dirLevel; /// Index of the directory record's parent directory + /// If nameLength is odd numbered, a padding byte will be present after the identifier text. +} ISO_PATHTABLE_ENTRY; + +typedef struct ISO_DIR_ENTRY +{ + unsigned char entryLength; // Directory entry length (variable, use for parsing through entries) + unsigned char extLength; // Extended entry data length (always 0) + ISO_UINT_PAIR entryOffs; // Points to the LBA of the file/directory entry + ISO_UINT_PAIR entrySize; // Size of the file/directory entry + ISO_DATESTAMP entryDate; // Date & time stamp of entry + unsigned char flags; // File flags (0x02 for directories, 0x00 for files) + unsigned char fileUnitSize; // Unit size (usually 0 even with Form 2 files such as STR/XA) + unsigned char interleaveGapSize; // Interleave gap size (usually 0 even with Form 2 files such as STR/XA) + ISO_USHORT_PAIR volSeqNum; // Volume sequence number (always 1) + unsigned char identifierLen; // Identifier (file/directory name) length in bytes +} ISO_DIR_ENTRY; + +typedef struct ISO_ROOTDIR_HEADER +{ + unsigned char entryLength; // Always 34 bytes + unsigned char extLength; // Always 0 + ISO_UINT_PAIR entryOffs; // Should point to LBA 22 + ISO_UINT_PAIR entrySize; // Size of entry extent + ISO_DATESTAMP entryDate; // Record date and time + unsigned char flags; // File flags + unsigned char fileUnitSize; + unsigned char interleaveGapSize; + ISO_USHORT_PAIR volSeqNum; + unsigned char identifierLen; // 0x01 + unsigned char identifier; // 0x00 +} ISO_ROOTDIR_HEADER; + +// ISO descriptor structure +typedef struct ISO_DESCRIPTOR +{ + + // ISO descriptor header + ISO_DESCRIPTOR_HEADER header; + // System ID (always PLAYSTATION) + char systemID[32]; + // Volume ID (or label, can be blank or anything) + char volumeID[32]; + // Unused null bytes + unsigned char pad2[8]; + // Size of volume in sector units + ISO_UINT_PAIR volumeSize; + // Unused null bytes + unsigned char pad3[32]; + // Number of discs in this volume set (always 1 for single volume) + ISO_USHORT_PAIR volumeSetSize; + // Number of this disc in volume set (always 1 for single volume) + ISO_USHORT_PAIR volumeSeqNumber; + // Size of sector in bytes (always 2048 bytes) + ISO_USHORT_PAIR sectorSize; + // Path table size in bytes (applies to all the path tables) + ISO_UINT_PAIR pathTableSize; + // LBA to Type-L path table + unsigned int pathTable1Offs; + // LBA to optional Type-L path table (usually a copy of the primary path table) + unsigned int pathTable2Offs; + // LBA to Type-L path table but with MSB format values + unsigned int pathTable1MSBoffs; + // LBA to optional Type-L path table but with MSB format values (usually a copy of the main path table) + unsigned int pathTable2MSBoffs; + // Directory entry for the root directory (similar to a directory entry) + ISO_ROOTDIR_HEADER rootDirRecord; + // Volume set identifier (can be blank or anything) + char volumeSetIdentifier[128]; + // Publisher identifier (can be blank or anything) + char publisherIdentifier[128]; + // Data preparer identifier (can be blank or anything) + char dataPreparerIdentifier[128]; + // Application identifier (always PLAYSTATION) + char applicationIdentifier[128]; + // Copyright file in the file system identifier (can be blank or anything) + char copyrightFileIdentifier[37]; + // Abstract file in the file system identifier (can be blank or anything) + char abstractFileIdentifier[37]; + // Bibliographical file identifier in the file system (can be blank or anything) + char bibliographicFilelIdentifier[37]; + // Volume create date (in text format YYYYMMDDHHMMSSMMGG) + char volumeCreateDate[17]; + // Volume modify date (in text format YYYYMMDDHHMMSSMMGG) + char volumeModifyDate[17]; + // Volume expiry date (in text format YYYYMMDDHHMMSSMMGG) + char volumeExpiryDate[17]; + // Volume effective date (in text format YYYYMMDDHHMMSSMMGG) + char volumeEffeciveDate[17]; + // File structure version (always 1) + unsigned char fileStructVersion; + // Padding + unsigned char dummy0; + // Application specific data (says CD-XA001 at [141], the rest are null bytes) + unsigned char appData[512]; + // Padding + unsigned char pad4[653]; + +} ISO_DESCRIPTOR; + +// Leave non-aligned structure packing +#pragma pack(pop) + +extern char _cd_media_changed; +int _cd_iso_last_dir_lba; + +u_char _cd_iso_descriptor_buff[2048]; +u_char *_cd_iso_pathtable_buff=NULL; +u_char *_cd_iso_directory_buff=NULL; +int _cd_iso_directory_len; + +int _CdReadIsoDescriptor(int session_offs) +{ + int i; + CdlLOC loc; + ISO_DESCRIPTOR *descriptor; + + // Seek to volume descriptor location + CdIntToPos(16+session_offs, &loc); + if( !CdControl(CdlSetloc, (u_char*)&loc, 0) ) + { +#ifdef DEBUG + printf("psxcd: Could not set seek destination.\n"); +#endif + return -1; + } + + // Read volume descriptor + CdRead(1, (u_int*)_cd_iso_descriptor_buff, CdlModeSpeed); + if( CdReadSync(0, 0) ) + { +#ifdef DEBUG + printf("psxcd: Error reading ISO volume descriptor.\n"); +#endif + return -1; + } + + + // Verify if volume descriptor is present + descriptor = (ISO_DESCRIPTOR*)_cd_iso_descriptor_buff; + if( strncmp("CD001", descriptor->header.id, 5) ) + { +#ifdef DEBUG + printf("psxcd: Disc does not have a ISO9660 file system.\n"); +#endif + return -1; + } + +#ifdef DEBUG + printf("psxcd_dbg: Path table LBA = %d\n", descriptor->pathTable1Offs); + printf("psxcd_dbg: Path table len = %d\n", descriptor->pathTableSize.lsb); +#endif + + // Allocate path table buffer + i = ((2047+descriptor->pathTableSize.lsb)>>11)<<11; + if( _cd_iso_pathtable_buff ) + { + free(_cd_iso_pathtable_buff); + } + _cd_iso_pathtable_buff = (u_char*)malloc(i); + +#ifdef DEBUG + printf("psxcd_dbg: Allocated %d bytes for path table.\n", i); +#endif + + // Read path table + CdIntToPos(descriptor->pathTable1Offs, &loc); + CdControl(CdlSetloc, (u_char*)&loc, 0); + CdRead(i>>11, (u_int*)_cd_iso_pathtable_buff, CdlModeSpeed); + if( CdReadSync(0, 0) ) + { +#ifdef DEBUG + printf("psxcd: Error reading ISO path table.\n"); +#endif + return -1; + } + + _cd_iso_last_dir_lba = 0; + + return 0; +} + +int _CdReadIsoDirectory(int lba) +{ + int i; + CdlLOC loc; + ISO_DIR_ENTRY *direntry; + + if( lba == _cd_iso_last_dir_lba ) + { + return 0; + } + + CdIntToPos(lba, &loc); + i = CdPosToInt(&loc); +#ifdef DEBUG + printf("psxcd_dbg: Seek to sector %d\n", i); +#endif + if( !CdControl(CdlSetloc, (u_char*)&loc, 0) ) + { +#ifdef DEBUG + printf("psxcd: Could not set seek destination.\n"); +#endif + return -1; + } + + if( _cd_iso_directory_buff ) + { + free(_cd_iso_directory_buff); + } + + // Read first sector of directory record + _cd_iso_directory_buff = (u_char*)malloc(2048); + CdRead(1, (u_int*)_cd_iso_directory_buff, CdlModeSpeed); + if( CdReadSync(0, 0) ) + { +#ifdef DEBUG + printf("psxcd: Error reading initial directory record.\n"); +#endif + return -1; + } + + direntry = (ISO_DIR_ENTRY*)_cd_iso_directory_buff; + _cd_iso_directory_len = direntry->entrySize.lsb; + +#ifdef DEBUG + printf("psxcd_dbg: Location of directory record = %d\n", direntry->entryOffs.lsb); + printf("psxcd_dbg: Size of directory record = %d\n", _cd_iso_directory_len); +#endif + + if( _cd_iso_directory_len > 2048 ) + { + if( !CdControl(CdlSetloc, (u_char*)&loc, 0) ) + { +#ifdef DEBUG + printf("psxcd: Could not set seek destination.\n"); +#endif + return -1; + } + + free(_cd_iso_directory_buff); + i = ((2047+_cd_iso_directory_len)>>11)<<11; + _cd_iso_directory_buff = (u_char*)malloc(i); +#ifdef DEBUG + printf("psxcd_dbg: Allocated %d bytes for directory record.\n", i); +#endif + + CdRead(i>>11, (u_int*)_cd_iso_directory_buff, CdlModeSpeed); + if( CdReadSync(0, 0) ) + { +#ifdef DEBUG + printf("psxcd: Error reading initial directory record.\n"); +#endif + return -1; + } + } + + _cd_iso_last_dir_lba = lba; + + return 0; +} + +#ifdef DEBUG + +void dump_directory(void) +{ + int i; + int dir_pos; + ISO_DIR_ENTRY *dir_entry; + char namebuff[16]; + + printf("psxcd_dbg: Cached directory record contents:\n"); + + i = 0; + dir_pos = 0; + while(1) + { + dir_entry = (ISO_DIR_ENTRY*)(_cd_iso_directory_buff+dir_pos); + + strncpy(namebuff, + _cd_iso_directory_buff+dir_pos+sizeof(ISO_DIR_ENTRY), dir_entry->identifierLen); + + printf("P:%d L:%d %s\n", dir_pos, dir_entry->identifierLen, namebuff); + + dir_pos += dir_entry->entryLength; + i++; + + // Check if padding is reached (end of record sector) + if( _cd_iso_directory_buff[dir_pos] == 0 ) + { + // Snap it to next sector + dir_pos = ((dir_pos+2047)>>11)<<11; + + // Break if exceeds length of directory buffer (end) + if( dir_pos >= _cd_iso_directory_len ) + { + break; + } + } + } + + printf("--\n"); + +} + +void dump_pathtable(void) +{ + u_char *tbl_pos; + ISO_PATHTABLE_ENTRY *tbl_entry; + ISO_DESCRIPTOR *descriptor; + char namebuff[16]; + + printf("psxcd_dbg: Path table entries:\n"); + + descriptor = (ISO_DESCRIPTOR*)_cd_iso_descriptor_buff; + + tbl_pos = _cd_iso_pathtable_buff; + tbl_entry = (ISO_PATHTABLE_ENTRY*)tbl_pos; + + while( (int)(tbl_pos-_cd_iso_pathtable_buff) < + descriptor->pathTableSize.lsb ) + { + strncpy(namebuff, + tbl_pos+sizeof(ISO_PATHTABLE_ENTRY), + tbl_entry->nameLength); + + printf("psxcd_dbg: %s\n", namebuff); + + // Advance to next entry + tbl_pos += sizeof(ISO_PATHTABLE_ENTRY) + +(2*((tbl_entry->nameLength+1)/2)); + + tbl_entry = (ISO_PATHTABLE_ENTRY*)tbl_pos; + } + +} + +#endif + +int get_pathtable_entry(int entry, ISO_PATHTABLE_ENTRY *tbl, char *namebuff) +{ + int i; + u_char *tbl_pos; + ISO_PATHTABLE_ENTRY *tbl_entry; + ISO_DESCRIPTOR *descriptor; + + descriptor = (ISO_DESCRIPTOR*)_cd_iso_descriptor_buff; + + tbl_pos = _cd_iso_pathtable_buff; + tbl_entry = (ISO_PATHTABLE_ENTRY*)tbl_pos; + + i = 0; + while( (int)(tbl_pos-_cd_iso_pathtable_buff) < + descriptor->pathTableSize.lsb ) + { + if( i == (entry-1) ) + { + if( namebuff ) + { + strncpy(namebuff, + tbl_pos+sizeof(ISO_PATHTABLE_ENTRY), + tbl_entry->nameLength); + } + + if( tbl ) + { + *tbl = *tbl_entry; + } + + return 0; + } + + // Advance to next entry + tbl_pos += sizeof(ISO_PATHTABLE_ENTRY) + +(2*((tbl_entry->nameLength+1)/2)); + + tbl_entry = (ISO_PATHTABLE_ENTRY*)tbl_pos; + i++; + } + + if( entry <= 0 ) + { + return i+1; + } + + return -1; +} + +char *resolve_pathtable_path(int entry, char *rbuff) +{ + char namebuff[16]; + ISO_PATHTABLE_ENTRY tbl_entry; + + *rbuff = 0; + + do + { + if( get_pathtable_entry(entry, &tbl_entry, namebuff) ) + { + return NULL; + } + + rbuff -= tbl_entry.nameLength; + memcpy(rbuff, namebuff, tbl_entry.nameLength); + rbuff--; + *rbuff = '\\'; + + // Parse to the parent + entry = tbl_entry.dirLevel; + + } while( entry > 1 ); + + return rbuff; +} + +int find_dir_entry(const char *name, ISO_DIR_ENTRY *dirent) +{ + int i; + int dir_pos; + ISO_DIR_ENTRY *dir_entry; + char namebuff[16]; + +#ifdef DEBUG + printf("psxcd_dbg: Locating file %s.\n", name); +#endif + + i = 0; + dir_pos = 0; + while(dir_pos < _cd_iso_directory_len) + { + dir_entry = (ISO_DIR_ENTRY*)(_cd_iso_directory_buff+dir_pos); + + if( !(dir_entry->flags & 0x2) ) + { + strncpy(namebuff, + _cd_iso_directory_buff+dir_pos+sizeof(ISO_DIR_ENTRY), + dir_entry->identifierLen); + + if( strcmp(namebuff, name) == 0 ) + { + *dirent = *dir_entry; + return 0; + } + } + + dir_pos += dir_entry->entryLength; + i++; + + // Check if padding is reached (end of record sector) + if( _cd_iso_directory_buff[dir_pos] == 0 ) + { + // Snap it to next sector + dir_pos = ((dir_pos+2047)>>11)<<11; + + } + } + + return -1; +} + +char *get_pathname(char *path, const char *filename) +{ + char *c; + c = strrchr(filename, '\\'); + + if(( c == filename ) || ( !c )) + { + path[0] = '\\'; + path[1] = 0; + return NULL; + } + + strncpy(path, filename, (int)(c-filename)); + return path; +} + +char *get_filename(char *name, const char *filename) +{ + char *c; + c = strrchr(filename, '\\'); + + if(( c == filename ) || ( !c )) + { + strcpy(name, filename+1); + return name; + } + + c++; + strcpy(name, c); + return name; +} + +CdlFILE *CdSearchFile(CdlFILE *fp, const char *filename) +{ + int i,j,found_dir,num_dirs; + int dir_len; + char tpath_rbuff[128]; + char search_path[128]; + char *rbuff; + ISO_PATHTABLE_ENTRY tbl_entry; + ISO_DIR_ENTRY dir_entry; + + // Read ISO descriptor if changed flag is set + if( _cd_media_changed ) + { + // Read ISO descriptor and path table + if( _CdReadIsoDescriptor(0) ) + { +#ifdef DEBUG + printf("psxcd: Could not read ISO file system.\n"); +#endif + return NULL; + } +#ifdef DEBUG + printf("psxcd: ISO file system cache updated.\n"); +#endif + _cd_media_changed = 0; + } + + // Get number of directories in path table + num_dirs = get_pathtable_entry(0, NULL, NULL); + +#ifdef DEBUG + printf("psxcd_dbg: Directories in path table: %d\n", num_dirs); + + rbuff = resolve_pathtable_path(num_dirs-1, tpath_rbuff+127); + + if( !rbuff ) + { + printf("psxcd_dbg: Could not resolve path.\n"); + } + else + { + printf("psxcd_dbg: Longest path: %s|\n", rbuff); + } +#endif + + if( get_pathname(search_path, filename) ) + { +#ifdef DEBUG + printf("psxcd_dbg: Search path = %s|\n", search_path); +#endif + } + + // Search the pathtable for a matching path + found_dir = 0; + for(i=1; i<num_dirs; i++) + { + rbuff = resolve_pathtable_path(i, tpath_rbuff+127); +#ifdef DEBUG + printf("psxcd_dbg: Found = %s|\n", rbuff); +#endif + if( rbuff ) + { + if( strcmp(search_path, rbuff) == 0 ) + { + found_dir = i; + break; + } + } + } + + if( !found_dir ) + { +#ifdef DEBUG + printf("psxcd_dbg: Directory path not found.\n"); +#endif + return NULL; + } + +#ifdef DEBUG + printf("psxcd_dbg: Found directory at record %d!\n", found_dir); +#endif + + get_pathtable_entry(found_dir, &tbl_entry, NULL); + +#ifdef DEBUG + printf("psxcd_dbg: Directory LBA = %d\n", tbl_entry.dirOffs); +#endif + + _CdReadIsoDirectory(tbl_entry.dirOffs); + + get_filename(fp->name, filename); + + // Add version number if not specified + if( !strchr(fp->name, ';') ) + { + strcat(fp->name, ";1"); + } + +#ifdef DEBUG + dump_directory(); +#endif + + if( find_dir_entry(fp->name, &dir_entry) ) + { +#ifdef DEBUG + printf("psxcd: Could not find file.\n"); +#endif + return NULL; + } + +#ifdef DEBUG + printf("psxcd_dbg: Located file at LBA %d.\n", dir_entry.entryOffs.lsb); +#endif + + CdIntToPos(dir_entry.entryOffs.lsb, &fp->loc); + fp->size = dir_entry.entrySize.lsb; + + return fp; +} |
