mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-27 20:28:27 +00:00
1160e09fdc
svn-id: r16677
275 lines
6.2 KiB
C++
275 lines
6.2 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2002-2005 The ScummVM project
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* $Header$
|
|
*
|
|
*/
|
|
|
|
#include "scumm/util.h"
|
|
#include "common/util.h"
|
|
|
|
namespace Scumm {
|
|
|
|
#pragma mark -
|
|
#pragma mark --- ScummFile ---
|
|
#pragma mark -
|
|
|
|
|
|
ScummFile::ScummFile() : _encbyte(0), _subFileStart(0), _subFileLen(0) {
|
|
}
|
|
|
|
void ScummFile::setEnc(byte value) {
|
|
_encbyte = value;
|
|
}
|
|
|
|
void ScummFile::setSubfileRange(uint32 start, uint32 len) {
|
|
// TODO: Add sanity checks
|
|
const uint32 fileSize = File::size();
|
|
assert(start <= fileSize);
|
|
assert(start + len <= fileSize);
|
|
_subFileStart = start;
|
|
_subFileLen = len;
|
|
seek(0, SEEK_SET);
|
|
}
|
|
|
|
void ScummFile::resetSubfile() {
|
|
_subFileStart = 0;
|
|
_subFileLen = 0;
|
|
seek(0, SEEK_SET);
|
|
}
|
|
|
|
bool ScummFile::open(const char *filename, AccessMode mode) {
|
|
if (File::open(filename, mode)) {
|
|
resetSubfile();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool ScummFile::openSubFile(const char *filename) {
|
|
assert(isOpen());
|
|
|
|
// Disable the XOR encryption and reset any current subfile range
|
|
setEnc(0);
|
|
resetSubfile();
|
|
|
|
// Read in the filename table and look for the specified file
|
|
|
|
unsigned long file_off, file_len;
|
|
char file_name[0x20+1];
|
|
unsigned long i;
|
|
|
|
// Get the length of the data file to use for consistency checks
|
|
const uint32 data_file_len = size();
|
|
|
|
// Read offset and length to the file records */
|
|
const uint32 file_record_off = readUint32BE();
|
|
const uint32 file_record_len = readUint32BE();
|
|
|
|
// Do a quick check to make sure the offset and length are good
|
|
if (file_record_off + file_record_len > data_file_len) {
|
|
return false;
|
|
}
|
|
|
|
// Do a little consistancy check on file_record_length
|
|
if (file_record_len % 0x28) {
|
|
return false;
|
|
}
|
|
|
|
// Scan through the files
|
|
for (i = 0; i < file_record_len; i += 0x28) {
|
|
// read a file record
|
|
seek(file_record_off + i, SEEK_SET);
|
|
file_off = readUint32BE();
|
|
file_len = readUint32BE();
|
|
read(file_name, 0x20);
|
|
file_name[0x20] = 0;
|
|
|
|
assert(file_name[0]);
|
|
//debug(7, " extracting \'%s\'", file_name);
|
|
|
|
// Consistency check. make sure the file data is in the file
|
|
if (file_off + file_len > data_file_len) {
|
|
return false;
|
|
}
|
|
|
|
if (scumm_stricmp(file_name, filename) == 0) {
|
|
// We got a match!
|
|
setSubfileRange(file_off, file_len);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool ScummFile::eof() {
|
|
return _subFileLen ? (pos() >= _subFileLen) : File::eof();
|
|
}
|
|
|
|
uint32 ScummFile::pos() {
|
|
return File::pos() - _subFileStart;
|
|
}
|
|
|
|
uint32 ScummFile::size() {
|
|
return _subFileLen ? _subFileLen : File::size();
|
|
}
|
|
|
|
void ScummFile::seek(int32 offs, int whence) {
|
|
if (_subFileLen) {
|
|
// Constrain the seek to the subfile
|
|
switch (whence) {
|
|
case SEEK_END:
|
|
offs = _subFileStart + _subFileLen - offs;
|
|
break;
|
|
case SEEK_SET:
|
|
offs += _subFileStart;
|
|
break;
|
|
case SEEK_CUR:
|
|
offs += File::pos();
|
|
break;
|
|
}
|
|
assert((int32)_subFileStart <= offs && offs <= (int32)(_subFileStart + _subFileLen));
|
|
whence = SEEK_SET;
|
|
}
|
|
File::seek(offs, whence);
|
|
}
|
|
|
|
uint32 ScummFile::read(void *ptr, uint32 len) {
|
|
uint32 realLen;
|
|
|
|
if (_subFileLen) {
|
|
// Limit the amount we read by the subfile boundaries.
|
|
const uint32 curPos = pos();
|
|
assert(_subFileLen >= curPos);
|
|
uint32 newPos = curPos + len;
|
|
if (newPos > _subFileLen) {
|
|
len = _subFileLen - curPos;
|
|
_ioFailed = true;
|
|
}
|
|
}
|
|
|
|
realLen = File::read(ptr, len);
|
|
|
|
|
|
// If an encryption byte was specified, XOR the data we just read by it.
|
|
// This simple kind of "encryption" was used by some of the older SCUMM
|
|
// games.
|
|
if (_encbyte) {
|
|
byte *p = (byte *)ptr;
|
|
byte *end = p + realLen;
|
|
while (p < end)
|
|
*p++ ^= _encbyte;
|
|
}
|
|
|
|
return realLen;
|
|
}
|
|
|
|
uint32 ScummFile::write(const void *, uint32) {
|
|
error("ScummFile does not support writing!");
|
|
return 0;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark --- Utilities ---
|
|
#pragma mark -
|
|
|
|
void checkRange(int max, int min, int no, const char *str) {
|
|
if (no < min || no > max) {
|
|
char buf[256];
|
|
snprintf(buf, sizeof(buf), str, no);
|
|
error("Value %d is out of bounds (%d,%d) (%s)", no, min, max, buf);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert an old style direction to a new style one (angle),
|
|
*/
|
|
int newDirToOldDir(int dir) {
|
|
if (dir >= 71 && dir <= 109)
|
|
return 1;
|
|
if (dir >= 109 && dir <= 251)
|
|
return 2;
|
|
if (dir >= 251 && dir <= 289)
|
|
return 0;
|
|
return 3;
|
|
}
|
|
|
|
/**
|
|
* Convert an new style (angle) direction to an old style one.
|
|
*/
|
|
int oldDirToNewDir(int dir) {
|
|
assert(0 <= dir && dir <= 3);
|
|
const int new_dir_table[4] = { 270, 90, 180, 0 };
|
|
return new_dir_table[dir];
|
|
}
|
|
|
|
/**
|
|
* Convert an angle to a simple direction.
|
|
*/
|
|
int toSimpleDir(int dirType, int dir) {
|
|
if (dirType) {
|
|
const int16 directions[] = { 22, 72, 107, 157, 202, 252, 287, 337 };
|
|
for (int i = 0; i < 7; i++)
|
|
if (dir >= directions[i] && dir <= directions[i+1])
|
|
return i+1;
|
|
} else {
|
|
const int16 directions[] = { 71, 109, 251, 289 };
|
|
for (int i = 0; i < 3; i++)
|
|
if (dir >= directions[i] && dir <= directions[i+1])
|
|
return i+1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Convert a simple direction to an angle.
|
|
*/
|
|
int fromSimpleDir(int dirType, int dir) {
|
|
if (dirType)
|
|
return dir * 45;
|
|
else
|
|
return dir * 90;
|
|
}
|
|
|
|
/**
|
|
* Normalize the given angle - that means, ensure it is positive, and
|
|
* change it to the closest multiple of 45 degree by abusing toSimpleDir.
|
|
*/
|
|
int normalizeAngle(int angle) {
|
|
int temp;
|
|
|
|
temp = (angle + 360) % 360;
|
|
|
|
return toSimpleDir(1, temp) * 45;
|
|
}
|
|
|
|
const char *tag2str(uint32 tag) {
|
|
static char str[5];
|
|
str[0] = (char)(tag >> 24);
|
|
str[1] = (char)(tag >> 16);
|
|
str[2] = (char)(tag >> 8);
|
|
str[3] = (char)tag;
|
|
str[4] = '\0';
|
|
return str;
|
|
}
|
|
|
|
} // End of namespace Scumm
|