xoreos  0.0.5
gdafile.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 /* See the GDA description on the Dragon Age toolset wiki
26  * (<http://social.bioware.com/wiki/datoolset/index.php/GDA>).
27  */
28 
29 #include <cassert>
30 
31 #include "src/common/error.h"
32 #include "src/common/readstream.h"
33 #include "src/common/hash.h"
34 #include "src/common/strutil.h"
35 
36 #include "src/aurora/gdafile.h"
37 #include "src/aurora/gff4file.h"
38 
39 static const uint32 kG2DAID = MKTAG('G', '2', 'D', 'A');
40 static const uint32 kVersion01 = MKTAG('V', '0', '.', '1');
41 static const uint32 kVersion02 = MKTAG('V', '0', '.', '2');
42 
43 namespace Aurora {
44 
45 const size_t GDAFile::kInvalidColumn;
46 const size_t GDAFile::kInvalidRow;
47 
48 GDAFile::GDAFile(Common::SeekableReadStream *gda) : _columns(0), _rowCount(0) {
49  assert(gda);
50 
51  load(gda);
52 }
53 
55 }
56 
57 size_t GDAFile::getColumnCount() const {
58  return _columns->size();
59 }
60 
61 size_t GDAFile::getRowCount() const {
62  return _rowCount;
63 }
64 
66  return _headers;
67 }
68 
69 bool GDAFile::hasRow(size_t row) const {
70  return getRow(row) != 0;
71 }
72 
73 const GFF4Struct *GDAFile::getRow(size_t row) const {
74  assert(_rowStarts.size() == _rows.size());
75 
76  /* To find the correct GFF4 for this row, we go through
77  * the list of row start indices in reverse, until we
78  * found one that's not bigger than the row we want.
79  */
80 
81  for (ptrdiff_t i = _rowStarts.size() - 1; i >= 0; i--) {
82  if (_rowStarts[i] <= row) {
83  row -= _rowStarts[i];
84 
85  if (row >= _rows[i]->size())
86  return 0;
87 
88  return (*_rows[i])[row];
89  }
90  }
91 
92  return 0;
93 }
94 
95 size_t GDAFile::findRow(uint32 id) const {
96  size_t idColumn = findColumn("ID");
97  if (idColumn == kInvalidColumn)
98  return kInvalidRow;
99 
100  // Go through all rows of all GFF4s, and look for the ID
101 
102  size_t gff4 = 0;
103  for (size_t i = 0, j = 0; i < _rowCount; i++, j++) {
104  if (j >= _rows[gff4]->size()) {
105  if (++gff4 >= _rows.size())
106  break;
107 
108  j = 0;
109  }
110 
111  if ((*_rows[gff4])[j] && ((*_rows[gff4])[j]->getUint(idColumn) == id))
112  return i;
113  }
114 
115  return kInvalidRow;
116 }
117 
118 size_t GDAFile::findColumn(const Common::UString &name) const {
119  ColumnNameMap::const_iterator c = _columnNameMap.find(name);
120  if (c != _columnNameMap.end())
121  return c->second;
122 
124  _columnNameMap[name] = column;
125 
126  return column;
127 }
128 
129 size_t GDAFile::findColumn(uint32 hash) const {
130  ColumnHashMap::const_iterator c = _columnHashMap.find(hash);
131  if (c != _columnHashMap.end())
132  return c->second;
133 
134  for (size_t i = 0; i < _columns->size(); i++) {
135  if (!(*_columns)[i])
136  continue;
137 
138  if ((*_columns)[i]->getUint(kGFF4G2DAColumnHash) == hash) {
139  _columnHashMap[hash] = kGFF4G2DAColumn1 + i;
140 
141  return kGFF4G2DAColumn1 + i;
142  }
143  }
144 
146  return kInvalidColumn;
147 }
148 
149 const GFF4Struct *GDAFile::getRowColumn(size_t row, uint32 hash, size_t &column) const {
150  const GFF4Struct *gdaRow = getRow(row);
151  if (!gdaRow || ((column = findColumn(hash)) == kInvalidColumn))
152  return 0;
153 
154  return gdaRow;
155 }
156 
157 const GFF4Struct *GDAFile::getRowColumn(size_t row, const Common::UString &name, size_t &column) const {
158  const GFF4Struct *gdaRow = getRow(row);
159  if (!gdaRow || ((column = findColumn(name)) == kInvalidColumn))
160  return 0;
161 
162  return gdaRow;
163 }
164 
165 Common::UString GDAFile::getString(size_t row, uint32 columnHash, const Common::UString &def) const {
166  size_t gdaColumn;
167  const GFF4Struct *gdaRow = getRowColumn(row, columnHash, gdaColumn);
168  if (!gdaRow)
169  return def;
170 
171  return gdaRow->getString(gdaColumn, def);
172 }
173 
174 Common::UString GDAFile::getString(size_t row, const Common::UString &columnName,
175  const Common::UString &def) const {
176  size_t gdaColumn;
177  const GFF4Struct *gdaRow = getRowColumn(row, columnName, gdaColumn);
178  if (!gdaRow)
179  return def;
180 
181  return gdaRow->getString(gdaColumn, def);
182 }
183 
184 int32 GDAFile::getInt(size_t row, uint32 columnHash, int32 def) const {
185  size_t gdaColumn;
186  const GFF4Struct *gdaRow = getRowColumn(row, columnHash, gdaColumn);
187  if (!gdaRow)
188  return def;
189 
190  return gdaRow->getSint(gdaColumn, def);
191 }
192 
193 int32 GDAFile::getInt(size_t row, const Common::UString &columnName, int32 def) const {
194  size_t gdaColumn;
195  const GFF4Struct *gdaRow = getRowColumn(row, columnName, gdaColumn);
196  if (!gdaRow)
197  return def;
198 
199  return gdaRow->getSint(gdaColumn, def);
200 }
201 
202 float GDAFile::getFloat(size_t row, uint32 columnHash, float def) const {
203  size_t gdaColumn;
204  const GFF4Struct *gdaRow = getRowColumn(row, columnHash, gdaColumn);
205  if (!gdaRow)
206  return def;
207 
208  return gdaRow->getDouble(gdaColumn, def);
209 }
210 
211 float GDAFile::getFloat(size_t row, const Common::UString &columnName, float def) const {
212  size_t gdaColumn;
213  const GFF4Struct *gdaRow = getRowColumn(row, columnName, gdaColumn);
214  if (!gdaRow)
215  return def;
216 
217  return gdaRow->getDouble(gdaColumn, def);
218 }
219 
220 GDAFile::Type GDAFile::identifyType(const Columns &columns, const Row &rows, size_t column) const {
221  if (!columns || (column >= columns->size()) || !(*columns)[column])
222  return kTypeEmpty;
223 
224  if ((*columns)[column]->hasField(kGFF4G2DAColumnType)) {
225  const Type type = (Type) (*columns)[column]->getUint(kGFF4G2DAColumnType, -1);
226 
227  switch (type) {
228  case kTypeEmpty:
229  case kTypeString:
230  case kTypeInt:
231  case kTypeFloat:
232  case kTypeBool:
233  case kTypeResource:
234  break;
235 
236  default:
237  throw Common::Exception("Invalid GDA column type %d", (int) type);
238  }
239 
240  return type;
241  }
242 
243  if (!rows || rows->empty() || !(*rows)[0])
244  return kTypeEmpty;
245 
246  GFF4Struct::FieldType fieldType = (*rows)[0]->getFieldType(kGFF4G2DAColumn1 + column);
247 
248  switch (fieldType) {
251  return kTypeString;
252 
261  return kTypeInt;
262 
265  return kTypeFloat;
266 
267  default:
268  break;
269  }
270 
271  return kTypeEmpty;
272 }
273 
275  try {
276  _gff4s.push_back(new GFF4File(gda, kG2DAID));
277 
278  const uint32 version = _gff4s.back()->getTypeVersion();
279  if ((version != kVersion01) && (version != kVersion02))
280  throw Common::Exception("Unsupported GDA file version %s", Common::debugTag(version).c_str());
281 
282  const GFF4Struct &top = _gff4s.back()->getTopLevel();
283 
285  _rows.push_back(&top.getList(kGFF4G2DARowList));
286 
287  _rowStarts.push_back(_rowCount);
288  _rowCount += _rows.back()->size();
289 
290  _headers.resize(_columns->size());
291  for (size_t i = 0; i < _columns->size(); i++) {
292  if (!(*_columns)[i])
293  continue;
294 
295  _headers[i].hash = (uint32) (*_columns)[i]->getUint(kGFF4G2DAColumnHash);
296  _headers[i].type = identifyType(_columns, _rows.back(), i);
297  _headers[i].field = (uint32) kGFF4G2DAColumn1 + i;
298  }
299 
300  } catch (Common::Exception &e) {
301  e.add("Failed reading GDA file");
302  throw;
303  }
304 }
305 
307  try {
308  _gff4s.push_back(new GFF4File(gda, kG2DAID));
309 
310  const uint32 version = _gff4s.back()->getTypeVersion();
311  if ((version != kVersion01) && (version != kVersion02))
312  throw Common::Exception("Unsupported GDA file version %s", Common::debugTag(version).c_str());
313 
314  const GFF4Struct &top = _gff4s.back()->getTopLevel();
315 
316  _rows.push_back(&top.getList(kGFF4G2DARowList));
317 
318  _rowStarts.push_back(_rowCount);
319  _rowCount += _rows.back()->size();
320 
321  Columns columns = &top.getList(kGFF4G2DAColumnList);
322  if (columns->size() != _columns->size())
323  throw Common::Exception("Column counts don't match (%u vs. %u)",
324  (uint)columns->size(), (uint)_columns->size());
325 
326  for (size_t i = 0; i < columns->size(); i++) {
327  const uint32 hash1 = (uint32) (* columns)[i]->getUint(kGFF4G2DAColumnHash);
328  const uint32 hash2 = (uint32) (*_columns)[i]->getUint(kGFF4G2DAColumnHash);
329 
330  const Type type1 = identifyType( columns, _rows.back(), i);
331  const Type type2 = identifyType(_columns, _rows[0] , i);
332 
333  if ((hash1 != hash2) || (type1 != type2))
334  throw Common::Exception("Columns don't match (%u: %u+%d vs. %u+%d)", (uint) i,
335  hash1, (int)type1, hash2, (int)type2);
336  }
337 
338  } catch (Common::Exception &e) {
339  e.add("Failed adding GDA file");
340  throw;
341  }
342 }
343 
344 } // End of namespace Aurora
#define MKTAG(a0, a1, a2, a3)
A wrapper macro used around four character constants, like &#39;DATA&#39;, to ensure portability.
Definition: endianness.h:140
Signed 8bit integer.
Definition: gff4file.h:210
void add(const char *s,...) GCC_PRINTF(2
Definition: error.cpp:58
Handling BioWare&#39;s GDAs (2DAs, two-dimensional array, within V4.0 GFFs).
int32 getInt(size_t row, uint32 columnHash, int32 def=0) const
Definition: gdafile.cpp:184
Common::UString getString(uint32 field, Common::Encoding encoding, const Common::UString &def="") const
Return a field string, read from the given encoding.
Definition: gff4file.cpp:949
A class holding an UTF-8 string.
Definition: ustring.h:48
static const uint32 kVersion02
Definition: gdafile.cpp:41
float getFloat(size_t row, uint32 columnHash, float def=0.0f) const
Definition: gdafile.cpp:202
int64 getSint(uint32 field, int64 def=0) const
Definition: gff4file.cpp:909
static const uint32 kVersion01
Definition: gdafile.cpp:40
Signed 64bit integer.
Definition: gff4file.h:216
RowStarts _rowStarts
Definition: gdafile.h:155
UTF-16 LE (little endian).
Definition: encoding.h:44
const GFF4List * Row
Definition: gdafile.h:138
Utility hash functions.
size_t findRow(uint32 id) const
Find a row by its ID value.
Definition: gdafile.cpp:95
A GFF (generic file format) V4.0/V4.1 file, found in Dragon Age: Origins, Dragon Age 2 and Sonic Chro...
Definition: gff4file.h:93
Utility templates and functions for working with strings and streams.
ColumnNameMap _columnNameMap
Definition: gdafile.h:158
Exception that provides a stack of explanations.
Definition: error.h:36
void load(Common::SeekableReadStream *gda)
Definition: gdafile.cpp:274
Signed 32bit integer.
Definition: gff4file.h:214
GFF4s _gff4s
Definition: gdafile.h:146
Handling version V4.0/V4.1 of BioWare&#39;s GFFs (generic file format).
Unsigned 32bit integer.
Definition: gff4file.h:213
Basic exceptions to throw.
void add(Common::SeekableReadStream *gda)
Add another GDA with the same column structure to the bottom of this GDA.
Definition: gdafile.cpp:306
Unsigned 64bit integer.
Definition: gff4file.h:215
ASCII string, found in Sonic.
Definition: gff4file.h:227
double getDouble(uint32 field, double def=0.0) const
Definition: gff4file.cpp:925
const GFF4Struct * getRowColumn(size_t row, uint32 hash, size_t &column) const
Definition: gdafile.cpp:149
static uint32 hashStringCRC32(const UString &string)
Definition: hash.h:193
static const size_t kInvalidColumn
Definition: gdafile.h:64
FieldType
The type of a GFF4 field.
Definition: gff4file.h:207
Headers _headers
Definition: gdafile.h:148
StackException Exception
Definition: error.h:59
const GFF4List * Columns
Definition: gdafile.h:137
Basic reading stream interfaces.
size_t getRowCount() const
Return the number of rows in the array.
Definition: gdafile.cpp:61
size_t getColumnCount() const
Return the number of columns in the array.
Definition: gdafile.cpp:57
GDAFile(Common::SeekableReadStream *gda)
Take over this stream and read a GDA file out of it.
Definition: gdafile.cpp:48
const GFF4Struct * getRow(size_t row) const
Get a row as a GFF4 struct.
Definition: gdafile.cpp:73
size_t findColumn(const Common::UString &name) const
Find a column by its name.
Definition: gdafile.cpp:118
Common::UString getString(size_t row, uint32 columnHash, const Common::UString &def="") const
Definition: gdafile.cpp:165
UString toLower() const
Return a lowercased copy of the string.
Definition: ustring.cpp:481
ColumnHashMap _columnHashMap
Definition: gdafile.h:157
size_t _rowCount
Definition: gdafile.h:153
uint32_t uint32
Definition: types.h:204
UString debugTag(uint32 tag, bool trim)
Create an elaborate string from an integer tag, for debugging purposes.
Definition: strutil.cpp:117
Columns _columns
Definition: gdafile.h:150
64bit IEEE float (double).
Definition: gff4file.h:218
static const size_t kInvalidRow
Definition: gdafile.h:65
Unsigned 16bit integer.
Definition: gff4file.h:211
bool hasRow(size_t row) const
Does this row exist in the GDA?
Definition: gdafile.cpp:69
std::vector< Header > Headers
Definition: gdafile.h:84
const Headers & getHeaders() const
Get the column headers.
Definition: gdafile.cpp:65
const GFF4List & getList(uint32 field) const
Definition: gff4file.cpp:1394
Unsigned 8bit integer.
Definition: gff4file.h:209
Signed 16bit integer.
Definition: gff4file.h:212
Type identifyType(const Columns &columns, const Row &rows, size_t column) const
Definition: gdafile.cpp:220
Interface for a seekable & readable data stream.
Definition: readstream.h:265
static const uint32 kG2DAID
Definition: gdafile.cpp:39
unsigned int uint
Definition: types.h:211
int32_t int32
Definition: types.h:203