/* 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 . * */ #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(block_end), static_cast(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(block_end), static_cast(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(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