File: data_ext.cpp

package info (click to toggle)
scummvm 2.9.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 450,580 kB
  • sloc: cpp: 4,299,825; asm: 28,322; python: 12,901; sh: 11,302; java: 9,289; xml: 7,895; perl: 2,639; ansic: 2,465; yacc: 1,670; javascript: 1,020; makefile: 933; lex: 578; awk: 275; objc: 82; sed: 11; php: 1
file content (159 lines) | stat: -rw-r--r-- 5,363 bytes parent folder | download | duplicates (2)
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