scummvm/engines/gob/dbase.cpp
2011-01-31 20:42:41 +00:00

196 lines
4.6 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "gob/dbase.h"
namespace Gob {
dBase::dBase() : _recordData(0) {
clear();
}
dBase::~dBase() {
clear();
}
bool dBase::load(Common::SeekableReadStream &stream) {
clear();
uint32 startPos = stream.pos();
_version = stream.readByte();
if ((_version != 0x03) && (_version != 0x83))
// Unsupported version
return false;
// TODO: Add support for memo files. A memo file is an external data file
// .DBT, segmented into "blocks". Each memo field in a record is an
// index this file.
_hasMemo = (_version & 0x80) != 0;
_lastUpdate.tm_year = stream.readByte();
_lastUpdate.tm_mon = stream.readByte() - 1;
_lastUpdate.tm_mday = stream.readByte();
_lastUpdate.tm_hour = 0;
_lastUpdate.tm_min = 0;
_lastUpdate.tm_sec = 0;
uint32 recordCount = stream.readUint32LE();
uint32 headerSize = stream.readUint16LE();
uint32 recordSize = stream.readUint16LE();
stream.skip(20); // Reserved
// Read all field descriptions, 0x0D is the end marker
uint32 fieldsLength = 0;
while (!stream.eos() && !stream.err() && (stream.readByte() != 0x0D)) {
Field field;
stream.seek(-1, SEEK_CUR);
field.name = readString(stream, 11);
field.type = (Type) stream.readByte();
stream.skip(4); // Field data address
field.size = stream.readByte();
field.decimals = stream.readByte();
fieldsLength += field.size;
stream.skip(14); // Reserved and/or useless for us
_fields.push_back(field);
}
if (stream.eos() || stream.err())
return false;
if ((stream.pos() - startPos) != headerSize)
// Corrupted file / unknown format
return false;
if (recordSize != (fieldsLength + 1))
// Corrupted file / unknown format
return false;
_recordData = new byte[recordSize * recordCount];
if (stream.read(_recordData, recordSize * recordCount) != (recordSize * recordCount))
return false;
if (stream.readByte() != 0x1A)
// Missing end marker
return false;
uint32 fieldCount = _fields.size();
// Create the records array
_records.resize(recordCount);
for (uint32 i = 0; i < recordCount; i++) {
Record &record = _records[i];
const byte *data = _recordData + i * recordSize;
char status = *data++;
if ((status != ' ') && (status != '*'))
// Corrupted file / unknown format
return false;
record.deleted = status == '*';
record.fields.resize(fieldCount);
for (uint32 j = 0; j < fieldCount; j++) {
record.fields[j] = data;
data += _fields[j].size;
}
}
return true;
}
void dBase::clear() {
memset(&_lastUpdate, 0, sizeof(_lastUpdate));
_version = 0;
_hasMemo = false;
_fields.clear();
_records.clear();
delete[] _recordData;
_recordData = 0;
}
byte dBase::getVersion() const {
return _version;
}
TimeDate dBase::getLastUpdate() const {
return _lastUpdate;
}
const Common::Array<dBase::Field> &dBase::getFields() const {
return _fields;
}
const Common::Array<dBase::Record> &dBase::getRecords() const {
return _records;
}
Common::String dBase::getString(const Record &record, int field) const {
assert(_fields[field].type == kTypeString);
uint32 fieldLength = stringLength(record.fields[field], _fields[field].size);
return Common::String((const char *) record.fields[field], fieldLength);
}
// String fields are padded with spaces. This finds the real length.
inline uint32 dBase::stringLength(const byte *data, uint32 max) {
while (max-- > 0)
if ((data[max] != 0x20) && (data[max] != 0x00))
return max + 1;
return 0;
}
// Read a constant-length string out of a stream.
inline Common::String dBase::readString(Common::SeekableReadStream &stream, int n) {
Common::String str;
char c;
while (n-- > 0) {
if ((c = stream.readByte()) == '\0')
break;
str += c;
}
if (n > 0)
stream.skip(n);
return str;
}
} // End of namespace Gob