aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/libpsxcd/cdsearchfile.c
blob: 60e43777292cc8816eef1087db21ea613fe7c37c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
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;
}