scummvm/scumm/util.cpp

275 lines
6.2 KiB
C++
Raw Normal View History

/* ScummVM - Scumm Interpreter
2005-01-01 16:09:25 +00:00
* 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