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