1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 // Disable symbol overrides so that we can use "FILE"
24 #define FORBIDDEN_SYMBOL_EXCEPTION_FILE
25 #define FORBIDDEN_SYMBOL_EXCEPTION_printf
26 #define FORBIDDEN_SYMBOL_EXCEPTION_abort
27 #define FORBIDDEN_SYMBOL_EXCEPTION_exit
28 
29 #include "backends/platform/ps2/fileio.h"
30 
31 #include <tamtypes.h>
32 #include <kernel.h>
33 #include <fileio.h>
34 #include <assert.h>
35 #include <string.h>
36 
37 #include "common/config-manager.h"
38 #include "common/file.h"
39 #include "engines/engine.h"
40 #include "backends/platform/ps2/asyncfio.h"
41 #include "backends/platform/ps2/eecodyvdfs.h"
42 #include "backends/platform/ps2/ps2debug.h"
43 #include "backends/platform/ps2/systemps2.h"
44 
45 #define __PS2_FILE_SEMA__ 1
46 
47 AsyncFio fio;
48 
Ps2File()49 Ps2File::Ps2File() {
50 	_fd = -1;
51 	_fileSize = 0;
52 	_filePos = 0;
53 	_cacheSize = 0;
54 	_cachePos = 0;
55 	_eof = false;
56 	_err = false;
57 
58 	_cacheBuf = (uint8 *)memalign(64, CACHE_SIZE * 2);
59 
60 	_cacheOpRunning = 0;
61 	_filePos = _physFilePos = _cachePos = 0;
62 	_fileSize = _bytesInCache = _cacheOfs = 0;
63 	_cacheOpRunning = false;
64 	_readBytesBlock = 0;
65 	_stream = true;
66 
67 #ifdef __PS2_FILE_SEMA__
68 	ee_sema_t newSema;
69 	newSema.init_count = 1;
70 	newSema.max_count = 1;
71 	_sema = CreateSema(&newSema);
72 	assert(_sema >= 0);
73 #endif
74 }
75 
~Ps2File()76 Ps2File::~Ps2File() {
77 	uint32 w;
78 	if (_fd >= 0) {
79 
80 		if (_mode != O_RDONLY) {
81 			fio.seek(_fd, 0, SEEK_SET);
82 			fio.write(_fd, _cacheBuf, _filePos);
83 			w = fio.sync(_fd);
84 			dbg_printf("flushed wbuf: %x of %x\n", w, _filePos);
85 		}
86 
87 		fio.close(_fd);
88 		uint32 r = fio.sync(_fd);
89 		dbg_printf("close [%d] - sync'd = %d\n", _fd, r);
90 	}
91 
92 	free(_cacheBuf);
93 
94 #ifdef __PS2_FILE_SEMA__
95 	DeleteSema(_sema);
96 #endif
97 }
98 
open(const char * name,int mode)99 bool Ps2File::open(const char *name, int mode) {
100 #if 1
101 	_fd = fio.open(name, mode);
102 
103 	dbg_printf("open %s [%d]\n", name, _fd);
104 
105 	if (_fd >= 0) {
106 		_mode = mode;
107 		_filePos = 0;
108 
109 		if (_mode == O_RDONLY) {
110 			_fileSize = fio.seek(_fd, 0, SEEK_END);
111 			fio.seek(_fd, 0, SEEK_SET);
112 		}
113 		else
114 			_fileSize = 0;
115 
116 		dbg_printf("  _mode = %x\n", _mode);
117 		dbg_printf("  _fileSize = %d\n", _fileSize);
118 		// dbg_printf("  _filePos = %d\n", _filePos);
119 
120 		return true;
121 	}
122 
123 	return false;
124 #else
125 	uint32 r;
126 
127 	// hack: FIO does not reports size for RW (?)
128 	_fd = fio.open(name, O_RDONLY);
129 	if (_fd >= 0) {
130 		_fileSize = fio.seek(_fd, 0, SEEK_END);
131 		fio.seek(_fd, 0, SEEK_SET); /* rewind ! */
132 
133 		if (_fileSize && mode != O_RDONLY) {
134 			fio.read(_fd, _cacheBuf, _fileSize);
135 			r = fio.sync(_fd);
136 			dbg_printf(" sz=%d, read=%d\n", _fileSize, r);
137 			assert(r == _fileSize);
138 		}
139 
140 		fio.close(_fd);
141 	}
142 	else
143 		_fileSize = 0; /* new file */
144 
145 	_fd = fio.open(name, mode);
146 
147 	dbg_printf("open %s [%d]\n", name, _fd);
148 
149 	if (_fd >= 0) {
150 		_mode = mode;
151 		_filePos = 0;
152 
153 		if (_fileSize) { /* existing data */
154 			if (mode == O_RDONLY) {
155 				/* DANGER: for w* modes it will truncate your fine files */
156 				fio.seek(_fd, 0, SEEK_SET);
157 			}
158 			else if (_mode & O_APPEND) {
159 				fio.seek(_fd, 0, _fileSize);
160 				_filePos = _fileSize;
161 			}
162 			#if 0 /* file already trunc'd when opened as w* -> moved up */
163 			if (mode != O_RDONLY) {
164 				fio.read(_fd, _cacheBuf, _fileSize);
165 				r = fio.sync(_fd);
166 				dbg_printf(" sz=%d, read=%d\n", _fileSize, r);
167 				assert(r == _fileSize);
168 				// _fileSize = fio.seek(_fd, 0, SEEK_END);
169 			}
170 			#endif
171 		}
172 
173 		dbg_printf("  _mode = %x\n", _mode);
174 		dbg_printf("  _fileSize = %d\n", _fileSize);
175 		dbg_printf("  _filePos = %d\n", _filePos);
176 
177 		return true;
178 	} else
179 		return false;
180 #endif
181 }
182 
tell()183 int32 Ps2File::tell() {
184 #ifdef __PS2_FILE_SEMA__
185 	WaitSema(_sema);
186 #endif
187 	int32 res = _filePos;
188 #ifdef __PS2_FILE_SEMA__
189 	SignalSema(_sema);
190 #endif
191 	return res;
192 }
193 
size()194 int32 Ps2File::size() {
195 #ifdef __PS2_FILE_SEMA__
196 	WaitSema(_sema);
197 #endif
198 	int32 res = _fileSize;
199 #ifdef __PS2_FILE_SEMA__
200 	SignalSema(_sema);
201 #endif
202 	return res;
203 }
204 
eof()205 bool Ps2File::eof() {
206 #ifdef __PS2_FILE_SEMA__
207 	WaitSema(_sema);
208 #endif
209 	bool res = _eof; // (_filePos == _fileSize);
210 	// bool res = (_filePos >= _fileSize);
211 #ifdef __PS2_FILE_SEMA__
212 	SignalSema(_sema);
213 
214 	// dbg_printf(" EOF [%d] : %d of %d  -> %d\n", _fd, _filePos, _fileSize, res);
215 #endif
216 	return res;
217 }
218 
getErr()219 bool Ps2File::getErr() {
220 	return _err;
221 }
222 
setErr(bool err)223 void Ps2File::setErr(bool err) {
224 	_err = err;
225 	_eof = err;
226 }
227 
seek(int32 offset,int origin)228 int Ps2File::seek(int32 offset, int origin) {
229 #ifdef __PS2_FILE_SEMA__
230 	WaitSema(_sema);
231 #endif
232 	int seekDest;
233 	int res = -1;
234 	switch (origin) {
235 		case SEEK_SET:
236 			seekDest = offset;
237 			break;
238 		case SEEK_CUR:
239 			seekDest = _filePos + offset;
240 			break;
241 		case SEEK_END:
242 			seekDest = _fileSize + offset;
243 			break;
244 		default:
245 			seekDest = -1;
246 			break;
247 	}
248 	if ((seekDest >= 0) && (seekDest <= (int)_fileSize)) {
249 		// uint32 _rseek = fio.sync(_fd);
250 		_filePos = seekDest;
251 		// fio.seek(_fd, _filePos, SEEK_SET);
252 		// fio.sync(_fd);
253 		// _cacheSize = 0;
254 		_eof = false;
255 		res = 0;
256 	}
257 	else {
258 		_eof = true;
259 	}
260 
261 	// dbg_printf("seek [%d]  %d  %d\n", _fd, offset, origin);
262 	// dbg_printf("  res = %d\n", res);
263 
264 #ifdef __PS2_FILE_SEMA__
265 	SignalSema(_sema);
266 #endif
267 
268 	return res;
269 }
270 
cacheReadAhead()271 void Ps2File::cacheReadAhead() {
272 	if (_cacheOpRunning) {
273 		// there's already some cache read running
274 		if (fio.poll(_fd)) // did it finish?
275 			cacheReadSync(); // yes.
276 	}
277 	if ((!_cacheOpRunning) && ((_readBytesBlock >= CACHE_READ_THRESHOLD) || _stream) && fio.fioAvail()) {
278 		// the engine seems to do sequential reads and there are no other I/Os going on. read ahead.
279 		uint32 cachePosEnd = _cachePos + _bytesInCache;
280 
281 		if (_cachePos > _filePos)
282 			return; // there was a seek in the meantime, don't cache.
283 		if (cachePosEnd - _filePos >= CACHE_FILL_MIN)
284 			return; // cache is full enough.
285 		if (cachePosEnd == _fileSize)
286 			return; // can't read beyond EOF.
287 
288 		assert(cachePosEnd < _fileSize);
289 
290 		if (_cachePos + _bytesInCache <= _filePos) {
291 			_cacheOfs = _bytesInCache = 0;
292 			_cachePos = cachePosEnd = _filePos & ~READ_ALIGN_MASK;
293 			assert(_filePos == _physFilePos);
294 		} else {
295 			uint32 cacheDiff = _filePos - _cachePos;
296 			assert(_bytesInCache >= cacheDiff);
297 			cacheDiff &= ~READ_ALIGN_MASK;
298 			_bytesInCache -= cacheDiff;
299 			_cachePos += cacheDiff;
300 			_cacheOfs = (_cacheOfs + cacheDiff) % CACHE_SIZE;
301 		}
302 
303 		if (_physFilePos != cachePosEnd) {
304 			sioprintf("unexpected _physFilePos %d cache %d %d\n", _physFilePos, _cacheOfs, _bytesInCache);
305 			// assert(!(cachePosEnd & READ_ALIGN_MASK)); // romeo
306 			_physFilePos = fio.seek(_fd, cachePosEnd, SEEK_SET);
307 			if (_physFilePos != cachePosEnd) {
308 				sioprintf("cache seek error: seek to %d instead of %d, fs = %d\n", _physFilePos, cachePosEnd, _fileSize);
309 				return;
310 			}
311 		}
312 
313 		uint32 cacheDest = (_cacheOfs + _bytesInCache) % CACHE_SIZE;
314 		uint32 cacheRead = CACHE_SIZE - _bytesInCache;
315 		if (cacheDest + cacheRead > CACHE_SIZE)
316 			cacheRead = CACHE_SIZE - cacheDest;
317 		if (cacheRead > MAX_READ_STEP)
318 			cacheRead = MAX_READ_STEP;
319 
320 		assert((!(cacheRead & READ_ALIGN_MASK)) && cacheRead);
321 
322 		_cacheOpRunning = true;
323 		fio.read(_fd, _cacheBuf + cacheDest, cacheRead);
324 	}
325 }
326 
cacheReadSync()327 void Ps2File::cacheReadSync() {
328 	if (_cacheOpRunning) {
329 		int res = fio.sync(_fd);
330 		assert(res >= 0);
331 		_bytesInCache += res;
332 		_physFilePos += res;
333 		_cacheOpRunning = false;
334 	}
335 }
336 
read(void * dest,uint32 len)337 uint32 Ps2File::read(void *dest, uint32 len) {
338 	// uint32 r=0, d=0, ds=0, sz=0;
339 #ifdef __PS2_FILE_SEMA__
340 	WaitSema(_sema);
341 #endif
342 
343 #ifdef __PS2_FILE_DEBUG__
344 	dbg_printf("read (1) : _filePos = %d\n", _filePos);
345 	dbg_printf("read (1) : _cachePos = %d\n", _cachePos);
346 #endif
347 
348 	if (len == 0) {
349 #ifdef __PS2_FILE_SEMA__
350 		SignalSema(_sema);
351 #endif
352 		return 0;
353 	}
354 
355 	if (_filePos >= _fileSize) {
356 		_eof = true;
357 #ifdef __PS2_FILE_SEMA__
358 		SignalSema(_sema);
359 #endif
360 		return 0;
361 	}
362 
363 	if ((_filePos+len) > _fileSize) {
364 		len = _fileSize-_filePos;
365 		_eof = true;
366 	}
367 
368 	uint8 *destBuf = (uint8 *)dest;
369 	if ((_filePos < _cachePos) || (_filePos + len > _cachePos + _bytesInCache))
370 		cacheReadSync(); // we have to read from CD, sync cache.
371 
372 	while (len && (_filePos != _fileSize)) {
373 		if ((_filePos >= _cachePos) && (_filePos < _cachePos + _bytesInCache)) { // read from cache
374 			uint32 staPos = (_cacheOfs + (_filePos - _cachePos)) % CACHE_SIZE;
375 			uint32 cpyLen = _bytesInCache - (_filePos - _cachePos);
376 			if (cpyLen > len)
377 				cpyLen = len;
378 			if (staPos + cpyLen > CACHE_SIZE)
379 				cpyLen = CACHE_SIZE - staPos;
380 
381 			assert(cpyLen);
382 			memcpy(destBuf, _cacheBuf + staPos, cpyLen);
383 			_filePos += cpyLen;
384 			destBuf += cpyLen;
385 			_readBytesBlock += len;
386 			len -= cpyLen;
387 		} else { // cache miss
388 			assert(!_cacheOpRunning);
389 			if (_physFilePos != _filePos) {
390 				if ((_filePos < _physFilePos) || (_filePos > _physFilePos + (CACHE_SIZE / 2)))
391 					_readBytesBlock = 0; // reset cache hit count
392 
393 				_physFilePos = _filePos & ~READ_ALIGN_MASK;
394 				if (fio.seek(_fd, _physFilePos, SEEK_SET) != (int)_physFilePos)
395 					break; // read beyond EOF
396 			}
397 
398 			int doRead = len + (_filePos - _physFilePos);
399 			doRead = (doRead + READ_ALIGN_MASK) & ~READ_ALIGN_MASK;
400 
401 			if (doRead > MAX_READ_STEP)
402 				doRead = MAX_READ_STEP;
403 			if (doRead < 2048)
404 				doRead = 2048;
405 
406 			fio.read(_fd, _cacheBuf, doRead);
407 			_cachePos = _physFilePos;
408 			_cacheOfs = 0;
409 			_bytesInCache = fio.sync(_fd);
410 			_physFilePos += _bytesInCache;
411 			if (!_bytesInCache)
412 				break; // EOF
413 		}
414 	}
415 #ifndef ENABLE_PROFILING
416 	// doesn't play nice with -pg
417 	cacheReadAhead();
418 #endif
419 #ifdef __PS2_FILE_SEMA__
420 	SignalSema(_sema);
421 #endif
422 	return destBuf - (uint8 *)dest;
423 }
424 
write(const void * src,uint32 len)425 uint32 Ps2File::write(const void *src, uint32 len) {
426 #ifdef __PS2_FILE_SEMA__
427 	WaitSema(_sema);
428 #endif
429 
430 	memcpy(&_cacheBuf[_filePos], src, len);
431 	_filePos += len;
432 
433 #ifdef __PS2_FILE_SEMA__
434 	SignalSema(_sema);
435 #endif
436 
437 	return len;
438 }
439 
440 
makeFromPath(const Common::String & path,bool writeMode)441 PS2FileStream *PS2FileStream::makeFromPath(const Common::String &path, bool writeMode) {
442 	Ps2File *file = new Ps2File();
443 
444 	int mode = writeMode ? (O_WRONLY | O_CREAT) : O_RDONLY;
445 
446 	if (file->open(path.c_str(), mode))
447 		return new PS2FileStream(file);
448 
449 	delete file;
450 	return 0;
451 }
452 
PS2FileStream(Ps2File * handle)453 PS2FileStream::PS2FileStream(Ps2File *handle) : _handle(handle) {
454 	assert(handle);
455 }
456 
~PS2FileStream()457 PS2FileStream::~PS2FileStream() {
458 	delete _handle;
459 }
460 
seek(int32 offs,int whence)461 bool PS2FileStream::seek(int32 offs, int whence) {
462 	return _handle->seek(offs, whence) == 0;
463 }
464 
pos() const465 int32 PS2FileStream::pos() const {
466 	return _handle->tell();
467 }
468 
eos() const469 bool PS2FileStream::eos() const {
470 	return _handle->eof();
471 }
472 
read(void * ptr,uint32 len)473 uint32 PS2FileStream::read(void *ptr, uint32 len) {
474 	return _handle->read(ptr, len);
475 }
476 
write(const void * ptr,uint32 len)477 uint32 PS2FileStream::write(const void *ptr, uint32 len) {
478 	return _handle->write(ptr, len);
479 }
480 
flush()481 bool PS2FileStream::flush() {
482 	// dbg_printf("flush not implemented\n");
483 	return true;
484 }
485 
err() const486 bool PS2FileStream::err() const {
487 	bool errVal = _handle->getErr();
488 
489 	if (errVal) {
490 		dbg_printf("ferror -> %d\n", errVal);
491 	}
492 
493 	return errVal;
494 }
495 
clearErr()496 void PS2FileStream::clearErr() {
497 	_handle->setErr(false);
498 }
499 
size() const500 int32 PS2FileStream::size() const {
501 	return _handle->size();
502 }
503 
504 
505 
ps2_fopen(const char * fname,const char * mode)506 FILE *ps2_fopen(const char *fname, const char *mode) {
507 	Ps2File *file = new Ps2File();
508 	int _mode = O_RDONLY;
509 
510 	dbg_printf("fopen(%s, %s)\n", fname, mode);
511 
512 	if (mode[0] == 'r' && mode [1] == 'w')
513 		_mode = O_RDWR;
514 	else if (mode[0] == 'w')
515 		_mode = O_WRONLY | O_CREAT;
516 	else if (mode[0] == 'a')
517 		_mode = O_RDWR | O_CREAT | O_APPEND;
518 
519 	if (file->open(fname, _mode))
520 		return (FILE *)file;
521 
522 	delete file;
523 	return NULL;
524 }
525 
ps2_fclose(FILE * stream)526 int ps2_fclose(FILE *stream) {
527 	Ps2File *file = (Ps2File *)stream;
528 
529 	delete file;
530 
531 	return 0;
532 }
533 
534 
ps2_fread(void * buf,size_t r,size_t n,FILE * stream)535 size_t ps2_fread(void *buf, size_t r, size_t n, FILE *stream) {
536 	assert(r != 0);
537 	return ((Ps2File *)stream)->read(buf, r * n) / r;
538 }
539 
ps2_fwrite(const void * buf,size_t r,size_t n,FILE * stream)540 size_t ps2_fwrite(const void *buf, size_t r, size_t n, FILE *stream) {
541 	assert(r != 0);
542 	return ((Ps2File *)stream)->write(buf, r * n) / r;
543 }
544