1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "ags/shared/util/data_ext.h"
#include "ags/shared/debugging/out.h"
#include "ags/shared/util/stream.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
String GetDataExtErrorText(DataExtErrorType err) {
switch (err) {
case kDataExtErr_NoError:
return "No error.";
case kDataExtErr_UnexpectedEOF:
return "Unexpected end of file.";
case kDataExtErr_BlockDataOverlapping:
return "Block data overlapping.";
case kDataExtErr_BlockNotFound:
return "Block not found.";
default: return "Unknown error.";
}
}
HError DataExtParser::OpenBlock() {
// - 1 or 4 bytes - an old-style unsigned numeric ID:
// where 0 would indicate following string ID,
// and -1 indicates end of the block list.
// - 16 bytes - string ID of an extension (if numeric ID is 0).
// - 4 or 8 bytes - length of extension data, in bytes.
_blockID = ((_flags & kDataExt_NumID32) != 0) ?
_in->ReadInt32() :
_in->ReadInt8();
if (_blockID < 0)
return HError::None(); // end of list
if (_in->EOS())
return new DataExtError(kDataExtErr_UnexpectedEOF);
if (_blockID > 0) { // old-style block identified by a numeric id
_blockLen = ((_flags & kDataExt_File64) != 0) ? _in->ReadInt64() : _in->ReadInt32();
_extID = GetOldBlockName(_blockID);
} else { // new style block identified by a string id
_extID = String::FromStreamCount(_in, 16);
_blockLen = _in->ReadInt64();
}
_blockStart = _in->GetPosition();
return HError::None();
}
void DataExtParser::SkipBlock() {
if (_blockID >= 0)
_in->Seek(_blockStart + _blockLen, kSeekBegin);
}
HError DataExtParser::PostAssert() {
const soff_t cur_pos = _in->GetPosition();
const soff_t block_end = _blockStart + _blockLen;
if (cur_pos > block_end) {
String err = String::FromFormat("Block: '%s', expected to end at offset: %llu, finished reading at %llu.",
_extID.GetCStr(), static_cast<int64>(block_end), static_cast<int64>(cur_pos));
if (cur_pos <= block_end + GetOverLeeway(_blockID))
Debug::Printf(kDbgMsg_Warn, err);
else
return new DataExtError(kDataExtErr_BlockDataOverlapping, err);
} else if (cur_pos < block_end) {
Debug::Printf(kDbgMsg_Warn, "WARNING: data blocks nonsequential, block '%s' expected to end at %llu, finished reading at %llu",
_extID.GetCStr(), static_cast<int64>(block_end), static_cast<int64>(cur_pos));
_in->Seek(block_end, Shared::kSeekBegin);
}
return HError::None();
}
HError DataExtParser::FindOne(int id) {
if (id <= 0) return new DataExtError(kDataExtErr_BlockNotFound);
HError err = HError::None();
for (err = OpenBlock(); err && !AtEnd(); err = OpenBlock()) {
if (id == _blockID)
return HError::None();
_in->Seek(_blockLen); // skip it
}
if (!err)
return err;
return new DataExtError(kDataExtErr_BlockNotFound);
}
HError DataExtReader::Read() {
HError err = HError::None();
bool read_next = true;
for (err = OpenBlock(); err && !AtEnd() && read_next; err = OpenBlock()) {
// Call the reader function to read current block's data
read_next = true;
err = ReadBlock(_blockID, _extID, _blockLen, read_next);
if (!err)
return err;
// Test that we did not read too much or too little
err = PostAssert();
if (!err)
return err;
}
return err;
}
// Generic function that saves a block and automatically adds its size into header
void WriteExtBlock(int block, const String &ext_id, const PfnWriteExtBlock &writer, int flags, Stream *out) {
const bool is_id32 = (flags & kDataExt_NumID32) != 0;
// 64-bit file offsets are written for blocks with ext_id, OR File64 flag
const bool is_file64 = (block == 0) || ((flags & kDataExt_File64) != 0);
// Write block's header
is_id32 != 0 ?
out->WriteInt32(block) :
out->WriteInt8(static_cast<int8_t>(block));
if (block == 0) // new-style string id
ext_id.WriteCount(out, 16);
soff_t sz_at = out->GetPosition();
// block size placeholder
is_file64 ?
out->WriteInt64(0) :
out->WriteInt32(0);
soff_t start_at = out->GetPosition();
// Call writer to save actual block contents
writer(out);
// Now calculate the block's size...
soff_t end_at = out->GetPosition();
soff_t block_size = (end_at - start_at);
// ...return back and write block's size in the placeholder
out->Seek(sz_at, Shared::kSeekBegin);
is_file64 ?
out->WriteInt64(block_size) :
out->WriteInt32((int32_t)block_size);
// ...and get back to the end of the file
out->Seek(0, Shared::kSeekEnd);
}
} // namespace Shared
} // namespace AGS
} // namespace AGS3
|