1 /////////////////////////////////////////////////////////////////////////////
2 //
3 // filecache - code for background file reading/caching
4 //
5 /////////////////////////////////////////////////////////////////////////////
6 
7 #include <stdio.h>
8 #include <string.h>
9 #include "utils.h"
10 #include "packfile.h"
11 #include "filecache.h"
12 #include "openbor.h"
13 
14 #ifdef PSP
15 #include <pspsuspend.h>
16 #endif
17 
18 #ifndef XBOX
19 #include <unistd.h>
20 #endif
21 
22 #ifdef PS2
23 #include <eekernel.h>
24 #include <sifdev.h>
25 #endif
26 
27 /////////////////////////////////////////////////////////////////////////////
28 
29 static int filecache_blocksize;
30 static int filecache_blocks;
31 static int max_vfds;
32 
33 //static int default_minimum_run_bytes = 98304;
34 static int default_minimum_run_bytes = 131072;
35 //static int default_minimum_run_bytes = 262144;
36 
37 /////////////////////////////////////////////////////////////////////////////
38 
39 // fd for pak file
40 // lba for pak file (if negative)
41 static int real_pakfd;
42 
43 // total number of blocks in the pak
44 static int total_pakblocks;
45 
46 // BLOCKSIZE * BLOCKS; be sure to 64-byte-align
47 static unsigned char *filecache;
48 static unsigned char *filecache_head = NULL;
49 
50 // which pakblock is cached in each cacheblock?
51 // -1 means invalid
52 static int *filecache_pakmap;
53 
54 // where is this pakblock cached?
55 // a value of FILECACHE_BLOCKS (255) means not cached.
56 // one byte for every block in the entire pak.
57 static unsigned char *where_is_this_pakblock_cached;
58 
59 /////////////////////////////////////////////////////////////////////////////
60 
61 // one per cacheblock
62 static unsigned *cacheblock_mru;
63 
64 static unsigned cacheblock_lastused = 0;
65 static unsigned cacheblock_mru_counter = 0;
66 
cacheblock_mark_used(unsigned n)67 static void cacheblock_mark_used(unsigned n)
68 {
69 	if(n >= filecache_blocks) return;
70 	if(n == cacheblock_lastused) return;
71 	if(cacheblock_mru_counter == 0xFFFFFFFF)
72 	{
73 		unsigned i;
74 		for(i = 0; i < filecache_blocks; i++) { cacheblock_mru[i] >>= 1; }
75 		cacheblock_mru_counter >>= 1;
76 	}
77 	cacheblock_mru[n] = ++cacheblock_mru_counter;
78 	cacheblock_lastused = n;
79 }
80 
81 /////////////////////////////////////////////////////////////////////////////
82 
83 // current read pointer (round down)
84 // -1 if not open
85 static int *vfd_readptr_pakblock;
86 // desired readahead in blocks
87 static int *vfd_desired_readahead_blocks;
88 // starting block of each open vfd
89 // these blocks are immune to being replaced in the cache
90 static int *vfd_startptr_pakblock;
91 
92 // THIS IS TEMPORARY; DO NOT RELY ON THIS VALUE
93 static int *vfd_blocks_available;
94 
95 // requested read block
96 static int request_read_pakblock = -1;
97 
98 // avoid going off the end of the track for gdroms
99 static int filecache_maxcdsectors;
100 
101 /////////////////////////////////////////////////////////////////////////////
102 //
103 // make sure that the total of all desired readahead is _less_ than the cache size!
104 // just to make things work more smoothly
105 //
106 // when finding a freeable cacheblock, select the one with the greatest extraneity
107 // defined as the greatest distance past the desired readahead for any vfd
108 //
109 
110 /////////////////////////////////////////////////////////////////////////////
111 //
112 // find which cacheblock is the least useful
113 // this means: least recently used, and is not immune
114 //
find_least_useful_cacheblock(void)115 int find_least_useful_cacheblock(void)
116 {
117 	int i, vfd;
118 	int leastcacheblock = -1;
119 	for(i = 0; i < filecache_blocks; i++)
120 	{
121 		int pakblock = filecache_pakmap[i];
122 		if(pakblock < 0) return i;
123 		// start and read pointers of any open files are both immune
124 		for(vfd = 0; vfd < max_vfds; vfd++)
125 		{
126 			if(vfd_readptr_pakblock[vfd] < 0) continue;
127 			if(vfd_startptr_pakblock[vfd] == pakblock) break;
128 			if(vfd_readptr_pakblock[vfd] == pakblock) break;
129 			if(vfd_desired_readahead_blocks[vfd] > 0) {
130 			if((pakblock >= vfd_readptr_pakblock[vfd]) &&
131 			   (pakblock < (vfd_readptr_pakblock[vfd] + vfd_desired_readahead_blocks[vfd]))) break;
132 		}
133 	}
134 	if(vfd < max_vfds) continue;
135 	if((leastcacheblock < 0) ||
136 	   (cacheblock_mru[i] < cacheblock_mru[leastcacheblock])) { leastcacheblock = i; }
137 	}
138 	if(leastcacheblock < 0) leastcacheblock = 0;
139 	return leastcacheblock;
140 }
141 
142 /////////////////////////////////////////////////////////////////////////////
143 //
144 // get the number of blocks available for this vfd
145 // (short loop)
146 //
get_vfd_blocks_available(int vfd)147 static int get_vfd_blocks_available(int vfd)
148 {
149 	int i;
150 	// can't have more blocks than what exists in the cache
151 	int max = filecache_blocks;
152 	int ptr = vfd_readptr_pakblock[vfd];
153 	if(ptr < 0) return 0; // no blocks available for a vfd that doesn't exist
154 	max += ptr;
155 	if(max > total_pakblocks) { max = total_pakblocks; }
156 	for(i = ptr; i < max; i++) if(where_is_this_pakblock_cached[i] >= filecache_blocks) break;
157 	return i - ptr;
158 }
159 
160 /////////////////////////////////////////////////////////////////////////////
161 //
162 // top priority: emergency stream reads
163 //   any vfds with desired readahead > 0, and less than (some number) blocks available
164 //   the vfd with the least available blocks is serviced first
165 // normal priority: blocks needed for read calls
166 //   any vfd blocked on a read call (must have 0 blocks available) is serviced
167 //   ideally this is a first-come first-serve queue, but they can probably just
168 //   be serviced in any order
169 // low priority: stream reads
170 //   any vfds with desired readahead > 0, and less than that many bytes available
171 //   the vfd with the least available blocks is serviced first
172 //
which_pakblock_to_read(int * suggested_min_run)173 int which_pakblock_to_read(int *suggested_min_run)
174 {
175 	int vfd;
176 	int least_avail;
177 	int percent_available;
178 	int least_percent_available;
179 	int pakblock;
180 
181 	if(suggested_min_run) *suggested_min_run = default_minimum_run_bytes / filecache_blocksize;
182 
183 	// top priority: emergency stream reads
184 	//   any vfds with desired readahead > 0, and less than 1/4 blocks available
185 	//   the vfd with the least available blocks is serviced first
186 	least_avail = -1;
187 	least_percent_available = 100;
188 	for(vfd = 0; vfd < max_vfds; vfd++)
189 	{
190 		if(vfd_readptr_pakblock[vfd] >= 0 && vfd_desired_readahead_blocks[vfd] > 0)
191 		{
192 			percent_available = (100*vfd_blocks_available[vfd])/vfd_desired_readahead_blocks[vfd];
193 			if(percent_available < 25 && percent_available < least_percent_available)
194 			{
195 				pakblock = vfd_readptr_pakblock[vfd] + vfd_blocks_available[vfd];
196 				if(pakblock >= 0 && pakblock < total_pakblocks)
197 				{
198 					least_percent_available = percent_available;
199 					least_avail = vfd;
200 				}
201 			}
202 		}
203 	}
204 	if(least_avail >= 0)
205 	{
206 		if(suggested_min_run) *suggested_min_run = vfd_desired_readahead_blocks[vfd] / 4;
207 		return vfd_readptr_pakblock[least_avail] + vfd_blocks_available[least_avail];
208 	}
209 
210 	// normal priority: blocks needed for read calls
211 	//   any vfd blocked on a read call (must have 0 blocks available) is serviced
212 	//   ideally this is a first-come first-serve queue, but they can probably just
213 	//   be serviced in any order
214 	if(request_read_pakblock >= 0) return request_read_pakblock;
215 
216 	// low priority: stream reads
217 	//   any vfds with desired readahead > 0, and less than that many bytes available
218 	//   the vfd with the least available blocks is serviced first
219 	least_avail = -1;
220 	least_percent_available = 100;
221 	for(vfd = 0; vfd < max_vfds; vfd++)
222 	{
223 		if(vfd_readptr_pakblock[vfd] >= 0 && vfd_desired_readahead_blocks[vfd] > 0)
224 		{
225 			percent_available = (100*vfd_blocks_available[vfd])/vfd_desired_readahead_blocks[vfd];
226 			if(percent_available < least_percent_available)
227 			{
228 				pakblock = vfd_readptr_pakblock[vfd] + vfd_blocks_available[vfd];
229 				if(pakblock >= 0 && pakblock < total_pakblocks)
230 				{
231 					least_percent_available = percent_available;
232 					least_avail = vfd;
233 				}
234 			}
235 		}
236 	}
237 	if(least_avail >= 0)
238 	{
239 		return vfd_readptr_pakblock[least_avail] + vfd_blocks_available[least_avail];
240 	}
241 
242 	// nothing needed to read
243 	return -1;
244 }
245 
246 /////////////////////////////////////////////////////////////////////////////
247 
248 static int last_cacheblock_read = -1;
249 static int last_pakblock_read = -1;
250 
251 static int filecache_ready = 0;
252 
253 static int pakblock_run_ptr = 0;
254 static int pakblock_run_len = 0;
255 static int pakblock_run_min = 1;
256 
filecache_process(void)257 void filecache_process(void)
258 {
259 	int vfd;
260 	int least_useful_cacheblock;
261 	int cacheblock_read;
262 	int pakblock_read;
263 
264 #ifdef PS2
265 	int busy;
266 #endif
267 
268 	if(!filecache_ready) return;
269 
270 #ifdef PS2
271 	busy = 1;
272 	sceIoctl(real_pakfd, SCE_FS_EXECUTING, &busy);
273 	if(busy) return;
274 #elif DC
275 	// busy?
276 	if(real_pakfd < 0)
277 	{
278 		if(gdrom_poll()) return;
279 	}
280 #endif
281 
282 	cacheblock_read = last_cacheblock_read;
283 	pakblock_read = last_pakblock_read;
284 
285 	// if we just updated the cache, reflect the new changes
286 	if(cacheblock_read >= 0 && pakblock_read >= 0)
287 	{
288 		filecache_pakmap[cacheblock_read] = pakblock_read;
289 		where_is_this_pakblock_cached[pakblock_read] = cacheblock_read;
290 		cacheblock_mark_used(cacheblock_read);
291 		cacheblock_read = -1;
292 		pakblock_read = -1;
293 	}
294 
295 	// make sure request_read_pakblock isn't out of range
296 	if(request_read_pakblock >= total_pakblocks) request_read_pakblock = 0;
297 
298 	// if the requested read block is available, signal so
299 	if(request_read_pakblock >= 0 && where_is_this_pakblock_cached[request_read_pakblock] < filecache_blocks)
300 	{
301 		request_read_pakblock = -1;
302 	}
303 
304 	// get the least useful cacheblock
305 	least_useful_cacheblock = find_least_useful_cacheblock();
306 
307 	// get how many blocks are available to each vfd
308 	for(vfd = 0; vfd < max_vfds; vfd++)
309 	{
310 		vfd_blocks_available[vfd] = get_vfd_blocks_available(vfd);
311 	}
312 
313 	//
314 	// now decide what pakblock to read next
315 	//
316 	if(pakblock_run_len >= pakblock_run_min) { pakblock_run_len = 0; }
317 	if((pakblock_run_len > 0) && ((pakblock_run_ptr+1) < total_pakblocks))
318 	{
319 		pakblock_run_len++;
320 		pakblock_read = ++pakblock_run_ptr;
321 	}
322 	else
323 	{
324 		int mymin = default_minimum_run_bytes / filecache_blocksize;
325 		pakblock_run_min = mymin;
326 		pakblock_run_len = 1;
327 		pakblock_read = which_pakblock_to_read(&pakblock_run_min);
328 		pakblock_run_ptr = pakblock_read;
329 		if(pakblock_run_min < mymin) pakblock_run_min = mymin;
330 		if(pakblock_read < 0) pakblock_run_len = 0;
331 	}
332 
333 	//
334 	// nullify pakblock_read if it's out of bounds or already cached
335 	//
336 	// if pakblock_read is out of range, nullify it
337 	if(pakblock_read >= 0) { if(pakblock_read >= total_pakblocks) { pakblock_read = -1; } }
338 	// if the pakblock is already cached, don't read it!
339 	if(pakblock_read >= 0 && where_is_this_pakblock_cached[pakblock_read] < filecache_blocks) { pakblock_read = -1; }
340 
341 	// if we're reading a pakblock, read it into the least useful cacheblock
342 	// and invalidate that part of the cache
343 	if(pakblock_read >= 0)
344 	{
345 		int oldpak;
346 		cacheblock_read = least_useful_cacheblock;
347 		oldpak = filecache_pakmap[cacheblock_read];
348 		if(oldpak >= 0 && oldpak < total_pakblocks)
349 		{
350 			where_is_this_pakblock_cached[oldpak] = filecache_blocks;
351 		}
352 		filecache_pakmap[cacheblock_read] = -1;
353 	}
354 	else
355 	{
356 		cacheblock_read = -1;
357 	}
358 
359 	last_pakblock_read = pakblock_read;
360 	last_cacheblock_read = cacheblock_read;
361 
362 	// if we wanted to read something, read it
363 	if(pakblock_read >= 0 && cacheblock_read >= 0)
364 	{
365 #ifdef DC
366 		if(real_pakfd < 0)
367 		{
368 			void *dest = filecache + (cacheblock_read * filecache_blocksize);
369 			int lba = (pakblock_read * filecache_blocksize) / 2048;
370 			int n = filecache_blocksize / 2048;
371 			// definitely do not go out of bounds here
372 			if((lba+n) > filecache_maxcdsectors) n = filecache_maxcdsectors - lba;
373 			// gdrom reads are non-blocking
374 			gdrom_readsectors(dest, (-real_pakfd) + lba, n);
375 		}
376 		else
377 #endif
378 		{
379 #ifdef PS2
380 	        sceLseek(real_pakfd, pakblock_read * filecache_blocksize, SCE_SEEK_SET);
381 		    busy = 1; while(busy) sceIoctl(real_pakfd, SCE_FS_EXECUTING, &busy);
382 		    sceRead(real_pakfd, filecache + (cacheblock_read * filecache_blocksize), filecache_blocksize);
383 #else
384 			int disCcWarns;
385 		    lseek(real_pakfd, pakblock_read * filecache_blocksize, SEEK_SET);
386 		    disCcWarns = read(real_pakfd, (char*) filecache + (cacheblock_read * filecache_blocksize), filecache_blocksize);
387 #endif
388 		}
389 	}
390 }
391 
392 /////////////////////////////////////////////////////////////////////////////
393 //
394 // attempt to read a block
395 // returns the number of bytes read or 0 on error
396 //
filecache_readpakblock(unsigned char * dest,int pakblock,int startofs,int bytes,int blocking)397 int filecache_readpakblock(unsigned char *dest, int pakblock, int startofs, int bytes, int blocking)
398 {
399 	int cacheblock;
400 	if(pakblock < 0 || pakblock >= total_pakblocks) return 0;
401 	if(bytes < 0) return 0;
402 	if(startofs < 0) return 0;
403 	if(startofs >= filecache_blocksize) return 0;
404 	if((startofs+bytes) > filecache_blocksize) bytes = filecache_blocksize - startofs;
405 
406 	for(;;)
407 	{
408 		// see if we can copy from the cache
409 		cacheblock = where_is_this_pakblock_cached[pakblock];
410 		if(cacheblock < filecache_blocks)
411 		{
412 			cacheblock_mark_used(cacheblock);
413 			memcpy(dest, filecache + (cacheblock * filecache_blocksize) + startofs, bytes);
414 			return bytes;
415 		}
416 
417 		// it didn't work
418 		// if we're nonblocking, return failure
419 		if(!blocking) return 0;
420 
421 		// otherwise, demand a block
422 		request_read_pakblock = pakblock;
423 
424 		filecache_process();
425 	}
426 	return bytes;
427 }
428 
429 /////////////////////////////////////////////////////////////////////////////
430 //
431 // set up where the vfd pointers are
432 //
filecache_setvfd(int vfd,int start,int block,int readahead)433 void filecache_setvfd(int vfd, int start, int block, int readahead)
434 {
435 	if(vfd < 0 || vfd >= max_vfds) return;
436 	vfd_startptr_pakblock[vfd] = start;
437 	vfd_readptr_pakblock[vfd] = block;
438 	vfd_desired_readahead_blocks[vfd] = readahead;
439 }
440 
441 /////////////////////////////////////////////////////////////////////////////
442 //
443 // Release All Allocations
444 //
filecache_term()445 void filecache_term()
446 {
447 	filecache_blocksize       = 32768;
448 	filecache_blocks          = 96;
449 	max_vfds                  = 8;
450 	default_minimum_run_bytes = 131072;
451 	real_pakfd                = 0;
452 	total_pakblocks           = 0;
453 	cacheblock_lastused       = 0;
454 	cacheblock_mru_counter    = 0;
455 	request_read_pakblock     = -1;
456 	filecache_maxcdsectors    = 0;
457 	last_cacheblock_read      = -1;
458 	last_pakblock_read        = -1;
459 	filecache_ready           = 0;
460 	pakblock_run_ptr          = 0;
461 	pakblock_run_len          = 0;
462 	pakblock_run_min          = 1;
463 	if(vfd_blocks_available != NULL)
464 	{
465 		free(vfd_blocks_available);
466 		vfd_blocks_available = NULL;
467 	}
468 	if(cacheblock_mru != NULL)
469 	{
470 		free(cacheblock_mru);
471 		cacheblock_mru = NULL;
472 	}
473 	if(vfd_desired_readahead_blocks != NULL)
474 	{
475 		free(vfd_desired_readahead_blocks);
476 		vfd_desired_readahead_blocks = NULL;
477 	}
478 	if(vfd_startptr_pakblock != NULL)
479 	{
480 		free(vfd_startptr_pakblock);
481 		vfd_startptr_pakblock = NULL;
482 	}
483 	if(vfd_readptr_pakblock != NULL)
484 	{
485 		free(vfd_readptr_pakblock);
486 		vfd_readptr_pakblock = NULL;
487 	}
488 	if(where_is_this_pakblock_cached != NULL)
489 	{
490 		free(where_is_this_pakblock_cached);
491 		where_is_this_pakblock_cached = NULL;
492 	}
493 	if(filecache_pakmap != NULL)
494 	{
495 		free(filecache_pakmap);
496 		filecache_pakmap = NULL;
497 	}
498 #ifdef PSP
499 	// Release 4 MBytes of reserved space by PSP.
500 	if(sceKernelVolatileMemUnlock(0))
501 	{
502 		printf("Error allocation filecache!\n");
503 		exit(0);
504 	}
505 #elif DC
506 	filecache_head = NULL;
507 #else
508 	if(filecache_head)
509 	{
510 		free(filecache_head);
511 		filecache_head = NULL;
512 	}
513 #endif
514 }
515 
516 /////////////////////////////////////////////////////////////////////////////
517 //
518 // BLOCKS MUST BE 255 OR LESS
519 //
filecache_init(int realfd,int pakcdsectors,int blocksize,unsigned char blocks,int vfds)520 void filecache_init(int realfd, int pakcdsectors, int blocksize, unsigned char blocks, int vfds)
521 {
522 	int i;
523 
524 #ifdef PSP
525 	int size;
526 #endif
527 
528 	real_pakfd = realfd;
529 	total_pakblocks = ((pakcdsectors*2048)+(blocksize-1))/blocksize;
530 	filecache_blocksize = blocksize;
531 	filecache_blocks = blocks;
532 	max_vfds = vfds;
533 	filecache_maxcdsectors = pakcdsectors;
534 
535 	// allocate everything
536 #ifdef PSP
537 	// This will give us the extra 4 MBytes of reserved space by PSP.
538 	if(sceKernelVolatileMemLock(0, (void*)&filecache_head, &size))
539 	{
540   	    printf("Error allocation filecache!\n");
541 	    exit(0);
542 	}
543 #elif DC
544 	filecache_head = (void*)(0xA5500000 - 64);
545 #else
546 	filecache_head = malloc(filecache_blocksize * filecache_blocks + 64);
547 #endif
548 
549 	filecache = filecache_head;
550 
551 	// align the filecache
552 	// we can lose this pointer since it'll never be freed anyway and can be reused while running bor
553 	// When we exit with sceKernalExitGame All resources are Freed up prior to returning to PSP OS.
554 	filecache += 0x40 - (((size_t)filecache) & 0x3F);
555 
556 	// pakmap: all values should be -1
557 	filecache_pakmap = malloc(sizeof(int) * filecache_blocks);
558 	for(i = 0; i < filecache_blocks; i++) filecache_pakmap[i] = -1;
559 
560 	// where_is_this_pakblock_cached: all values should be filecache_blocks
561 	where_is_this_pakblock_cached = malloc(total_pakblocks);
562 	for(i = 0; i < total_pakblocks; i++) where_is_this_pakblock_cached[i] = filecache_blocks;
563 
564 	// vfd read pointers: init to -1
565 	vfd_readptr_pakblock = malloc(sizeof(int) * max_vfds);
566 	for(i = 0; i < max_vfds; i++) vfd_readptr_pakblock[i] = -1;
567 
568 	// vfd starting pointers: init to -1
569 	vfd_startptr_pakblock = malloc(sizeof(int) * max_vfds);
570 	for(i = 0; i < max_vfds; i++) vfd_startptr_pakblock[i] = -1;
571 
572 	// desired readahead: init to 0
573 	vfd_desired_readahead_blocks = malloc(sizeof(int) * max_vfds);
574 	for(i = 0; i < max_vfds; i++) vfd_desired_readahead_blocks[i] = -1;
575 
576 	// cache mru: init to 0
577 	cacheblock_mru = malloc(sizeof(unsigned) * filecache_blocks);
578 	for(i = 0; i < filecache_blocks; i++) cacheblock_mru[i] = 0;
579 
580 	// blocks available: needs no init
581 	vfd_blocks_available = malloc(sizeof(int) * max_vfds);
582 
583 	filecache_ready = 1;
584 }
585 
586 /////////////////////////////////////////////////////////////////////////////
587 //
588 // quick and dirty
589 //
filecache_wait_for_prebuffer(int vfd,int nblocks)590 void filecache_wait_for_prebuffer(int vfd, int nblocks)
591 {
592 	if(vfd_readptr_pakblock[vfd] < 0) return;
593 	if((vfd_readptr_pakblock[vfd]+nblocks) > total_pakblocks)
594 	{
595 		nblocks = total_pakblocks - vfd_readptr_pakblock[vfd];
596 	}
597 	while(get_vfd_blocks_available(vfd) < nblocks) filecache_process();
598 }
599 
600 /////////////////////////////////////////////////////////////////////////////
601