xoreos  0.0.5
2dafile.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 BioWare's own specs released for Neverwinter Nights modding
26  * (<https://github.com/xoreos/xoreos-docs/tree/master/specs/bioware>)
27  */
28 
29 #include <cassert>
30 
31 #include "src/common/util.h"
32 #include "src/common/error.h"
33 #include "src/common/scopedptr.h"
34 #include "src/common/strutil.h"
35 #include "src/common/encoding.h"
36 #include "src/common/readstream.h"
37 #include "src/common/writefile.h"
39 
40 #include "src/aurora/types.h"
41 #include "src/aurora/2dafile.h"
42 #include "src/aurora/gdafile.h"
43 #include "src/aurora/gdaheaders.h"
44 #include "src/aurora/gff4file.h"
45 
46 static const uint32 k2DAID = MKTAG('2', 'D', 'A', ' ');
47 static const uint32 k2DAIDTab = MKTAG('2', 'D', 'A', '\t');
48 static const uint32 kVersion2a = MKTAG('V', '2', '.', '0');
49 static const uint32 kVersion2b = MKTAG('V', '2', '.', 'b');
50 
51 namespace Aurora {
52 
53 TwoDARow::TwoDARow(TwoDAFile &parent) : _parent(&parent) {
54 }
55 
57 }
58 
59 const Common::UString &TwoDARow::getString(size_t column) const {
60  const Common::UString &cell = getCell(column);
61  if (cell.empty() || (cell == "****"))
62  return _parent->_defaultString;
63 
64  return cell;
65 }
66 
68  const Common::UString &cell = getCell(_parent->headerToColumn(column));
69  if (cell.empty() || (cell == "****"))
70  return _parent->_defaultString;
71 
72  return cell;
73 }
74 
75 int32 TwoDARow::getInt(size_t column) const {
76  const Common::UString &cell = getCell(column);
77  if (cell.empty() || (cell == "****"))
78  return _parent->_defaultInt;
79 
80  return _parent->parseInt(cell);
81 }
82 
83 int32 TwoDARow::getInt(const Common::UString &column) const {
84  const Common::UString &cell = getCell(_parent->headerToColumn(column));
85  if (cell.empty() || (cell == "****"))
86  return _parent->_defaultInt;
87 
88  return _parent->parseInt(cell);
89 }
90 
91 float TwoDARow::getFloat(size_t column) const {
92  const Common::UString &cell = getCell(column);
93  if (cell.empty() || (cell == "****"))
94  return _parent->_defaultFloat;
95 
96  return _parent->parseFloat(cell);
97 }
98 
99 float TwoDARow::getFloat(const Common::UString &column) const {
100  const Common::UString &cell = getCell(_parent->headerToColumn(column));
101  if (cell.empty() || (cell == "****"))
102  return _parent->_defaultFloat;
103 
104  return _parent->parseFloat(cell);
105 }
106 
107 bool TwoDARow::empty(size_t column) const {
108  const Common::UString &cell = getCell(column);
109  if (cell.empty() || (cell == "****"))
110  return true;
111 
112  return false;
113 }
114 
115 bool TwoDARow::empty(const Common::UString &column) const {
116  return empty(_parent->headerToColumn(column));
117 }
118 
119 static const Common::UString kEmpty;
120 const Common::UString &TwoDARow::getCell(size_t n) const {
121  if (n >= _data.size())
122  return kEmpty;
123 
124  return _data[n];
125 }
126 
127 
129  _defaultInt(0), _defaultFloat(0.0f), _emptyRow(*this) {
130 
131  load(twoda);
132 }
133 
135  _defaultInt(0), _defaultFloat(0.0f), _emptyRow(*this) {
136 
137  load(gda);
138 }
139 
141 }
142 
144  readHeader(twoda);
145 
146  if ((_id != k2DAID) && (_id != k2DAIDTab))
147  throw Common::Exception("Not a 2DA file (%s)", Common::debugTag(_id).c_str());
148 
149  if ((_version != kVersion2a) && (_version != kVersion2b))
150  throw Common::Exception("Unsupported 2DA file version %s", Common::debugTag(_version).c_str());
151 
152  // Ignore the rest of the line; it's garbage
154 
155  try {
156 
157  if (_version == kVersion2a)
158  read2a(twoda); // ASCII
159  else if (_version == kVersion2b)
160  read2b(twoda); // Binary
161 
162  // Create the map to quickly translate headers to column indices
163  createHeaderMap();
164 
165  } catch (Common::Exception &e) {
166  e.add("Failed reading 2DA file");
167  throw;
168  }
169 
170 }
171 
174 
175  // Spaces and tabs act to separate cells
176  tokenize.addSeparator(' ');
177  tokenize.addSeparator('\t');
178  // We can quote spaces and tabs with "
179  tokenize.addQuote('\"');
180  // \n ends a whole row
181  tokenize.addChunkEnd('\n');
182  // We're ignoring \r
183  tokenize.addIgnore('\r');
184 
185  readDefault2a(twoda, tokenize);
186  readHeaders2a(twoda, tokenize);
187  readRows2a(twoda, tokenize);
188 }
189 
191  readHeaders2b(twoda);
192  skipRowNames2b(twoda);
193  readRows2b(twoda);
194 }
195 
197  Common::StreamTokenizer &tokenize) {
198 
199  /* ASCII 2DA files can have default values that are returned for cells
200  * that don't exist. They are specified in the second line, optionally
201  * preceded by "Default:".
202  */
203 
204  std::vector<Common::UString> defaultRow;
205  tokenize.getTokens(twoda, defaultRow, 2);
206 
207  if (defaultRow[0].equalsIgnoreCase("Default:"))
208  _defaultString = defaultRow[1];
209 
212 
213  tokenize.nextChunk(twoda);
214 }
215 
217  Common::StreamTokenizer &tokenize) {
218 
219  /* Read the column headers of an ASCII 2DA file. */
220 
221  while (!twoda.eos() && (tokenize.getTokens(twoda, _headers) == 0))
222  tokenize.nextChunk(twoda);
223 
224  tokenize.nextChunk(twoda);
225 }
226 
228  Common::StreamTokenizer &tokenize) {
229 
230  /* And now read the individual cells in the rows. */
231 
232  const size_t columnCount = _headers.size();
233 
234  while (!twoda.eos()) {
235  Common::ScopedPtr<TwoDARow> row(new TwoDARow(*this));
236 
237  /* Skip the first token, which is the row index, possibly indented.
238  * The row index is implicit in the data and its use in the 2DA
239  * file is only meant as a guideline for people editing the file by
240  * hand. It might even be completely incorrect. */
241  tokenize.findFirstToken(twoda);
242  tokenize.skipToken(twoda);
243 
244  // Read all the cells in the row
245  size_t count = tokenize.getTokens(twoda, row->_data, columnCount, columnCount, "****");
246 
247  // And move to the next line
248  tokenize.nextChunk(twoda);
249 
250  // Ignore empty lines
251  if (count == 0)
252  continue;
253 
254  _rows.push_back(row.release());
255  }
256 }
257 
259  /* Read the column headers of a binary 2DA file. */
260 
262 
263  // Individual column headers a separated by either a tab or a NUL
264  tokenize.addSeparator('\t');
265  tokenize.addSeparator('\0');
266 
267  Common::UString header = tokenize.getToken(twoda);
268  while (!header.empty()) {
269  _headers.push_back(header);
270 
271  header = tokenize.getToken(twoda);
272  }
273 }
274 
276  /* Next up are the row names / indices. Like for the ASCII 2DA files,
277  * the actual row indices are implicit in the data, so we're just
278  * ignoring them. The only information we care about is how many rows
279  * there are.
280  */
281 
282  const uint32 rowCount = twoda.readUint32LE();
283  _rows.resize(rowCount, 0);
284 
286 
287  // Individual row indices a separated by either a tab or a NUL
288  tokenize.addSeparator('\t');
289  tokenize.addSeparator('\0');
290 
291  tokenize.skipToken(twoda, rowCount);
292 }
293 
295  /* And now read the cells. In binary 2DA files, each cell only
296  * stores a single 16-bit number, the offset into the data segment
297  * where the data for this cell can be found. Moreover, a single
298  * data offset can be used by several cells, deduplicating the
299  * cell data.
300  */
301 
302  const size_t columnCount = _headers.size();
303  const size_t rowCount = _rows.size();
304  const size_t cellCount = columnCount * rowCount;
305 
306  Common::ScopedArray<uint32> offsets(new uint32[cellCount]);
307 
309 
310  tokenize.addSeparator('\0');
311 
312  for (size_t i = 0; i < cellCount; i++)
313  offsets[i] = twoda.readUint16LE();
314 
315  twoda.skip(2); // Size of the data segment in bytes
316 
317  const size_t dataOffset = twoda.pos();
318 
319  for (size_t i = 0; i < rowCount; i++) {
320  _rows[i] = new TwoDARow(*this);
321 
322  _rows[i]->_data.resize(columnCount);
323 
324  for (size_t j = 0; j < columnCount; j++) {
325  const size_t offset = dataOffset + offsets[i * columnCount + j];
326 
327  twoda.seek(offset);
328 
329  _rows[i]->_data[j] = tokenize.getToken(twoda);
330  if (_rows[i]->_data[j].empty())
331  _rows[i]->_data[j] = "****";
332  }
333  }
334 }
335 
337  for (size_t i = 0; i < _headers.size(); i++)
338  _headerMap.insert(std::make_pair(_headers[i], i));
339 }
340 
341 void TwoDAFile::load(const GDAFile &gda) {
342  try {
343 
344  const GDAFile::Headers &headers = gda.getHeaders();
345  assert(headers.size() == gda.getColumnCount());
346 
347  _headers.resize(gda.getColumnCount());
348  for (size_t i = 0; i < gda.getColumnCount(); i++) {
349  const char *headerString = findGDAHeader(headers[i].hash);
350 
351  _headers[i] = headerString ? headerString : Common::UString::format("[%u]", headers[i].hash);
352  }
353 
354  _rows.resize(gda.getRowCount(), 0);
355  for (size_t i = 0; i < gda.getRowCount(); i++) {
356  const GFF4Struct *row = gda.getRow(i);
357 
358  _rows[i] = new TwoDARow(*this);
359  _rows[i]->_data.resize(gda.getColumnCount());
360 
361  for (size_t j = 0; j < gda.getColumnCount(); j++) {
362  if (row) {
363  switch (headers[j].type) {
366  _rows[i]->_data[j] = row->getString(headers[j].field);
367  break;
368 
369  case GDAFile::kTypeInt:
370  _rows[i]->_data[j] = Common::UString::format("%d", (int) row->getSint(headers[j].field));
371  break;
372 
373  case GDAFile::kTypeFloat:
374  _rows[i]->_data[j] = Common::UString::format("%f", row->getDouble(headers[j].field));
375  break;
376 
377  case GDAFile::kTypeBool:
378  _rows[i]->_data[j] = Common::UString::format("%u", (uint) row->getUint(headers[j].field));
379  break;
380 
381  default:
382  break;
383  }
384  }
385 
386  if (_rows[i]->_data[j].empty())
387  _rows[i]->_data[j] = "****";
388 
389  }
390  }
391 
392  } catch (Common::Exception &e) {
393  e.add("Failed reading GDA file");
394  throw;
395  }
396 
397  createHeaderMap();
398 }
399 
400 size_t TwoDAFile::getRowCount() const {
401  return _rows.size();
402 }
403 
405  return _headers.size();
406 }
407 
408 const std::vector<Common::UString> &TwoDAFile::getHeaders() const {
409  return _headers;
410 }
411 
412 size_t TwoDAFile::headerToColumn(const Common::UString &header) const {
413  HeaderMap::const_iterator column = _headerMap.find(header);
414  if (column == _headerMap.end())
415  // No such header
416  return kFieldIDInvalid;
417 
418  return column->second;
419 }
420 
421 const TwoDARow &TwoDAFile::getRow(size_t row) const {
422  if ((row >= _rows.size()) || !_rows[row])
423  // No such row
424  return _emptyRow;
425 
426  return *_rows[row];
427 }
428 
429 const TwoDARow &TwoDAFile::getRow(const Common::UString &header, const Common::UString &value) const {
430  size_t columnIndex = headerToColumn(header);
431  if (columnIndex == kFieldIDInvalid)
432  return _emptyRow;
433 
434  for (std::vector<TwoDARow *>::const_iterator row = _rows.begin(); row != _rows.end(); ++row) {
435  if ((*row)->getString(columnIndex).equalsIgnoreCase(value))
436  return **row;
437  }
438 
439  // No such row
440  return _emptyRow;
441 }
442 
444  // Write header
445 
446  out.writeString("2DA V2.0\n");
447  if (!_defaultString.empty())
449  out.writeByte('\n');
450 
451  // Calculate column lengths
452 
453  std::vector<size_t> colLength;
454  colLength.resize(_headers.size() + 1, 0);
455 
456  const Common::UString maxRow = Common::UString::format("%d", (int)_rows.size() - 1);
457  colLength[0] = maxRow.size();
458 
459  for (size_t i = 0; i < _headers.size(); i++)
460  colLength[i + 1] = _headers[i].size();
461 
462  for (size_t i = 0; i < _rows.size(); i++) {
463  for (size_t j = 0; j < _rows[i]->_data.size(); j++) {
464  const bool needQuote = _rows[i]->_data[j].contains(' ');
465  const size_t length = needQuote ? _rows[i]->_data[j].size() + 2 : _rows[i]->_data[j].size();
466 
467  colLength[j + 1] = MAX<size_t>(colLength[j + 1], length);
468  }
469  }
470 
471  // Write column headers
472 
473  out.writeString(Common::UString::format("%-*s", (int)colLength[0], ""));
474 
475  for (size_t i = 0; i < _headers.size(); i++)
476  out.writeString(Common::UString::format(" %-*s", (int)colLength[i + 1], _headers[i].c_str()));
477 
478  out.writeByte('\n');
479 
480  // Write array
481 
482  for (size_t i = 0; i < _rows.size(); i++) {
483  out.writeString(Common::UString::format("%*u", (int)colLength[0], (uint)i));
484 
485  for (size_t j = 0; j < _rows[i]->_data.size(); j++) {
486  const bool needQuote = _rows[i]->_data[j].contains(' ');
487 
488  Common::UString cellString;
489  if (needQuote)
490  cellString = Common::UString::format("\"%s\"", _rows[i]->_data[j].c_str());
491  else
492  cellString = _rows[i]->_data[j];
493 
494  out.writeString(Common::UString::format(" %-*s", (int)colLength[j + 1], cellString.c_str()));
495 
496  }
497 
498  out.writeByte('\n');
499  }
500 
501  out.flush();
502 }
503 
504 bool TwoDAFile::writeASCII(const Common::UString &fileName) const {
505  Common::WriteFile file;
506  if (!file.open(fileName))
507  return false;
508 
509  writeASCII(file);
510  file.close();
511 
512  return true;
513 }
514 
516  const size_t columnCount = _headers.size();
517  const size_t rowCount = _rows.size();
518  const size_t cellCount = columnCount * rowCount;
519 
520  out.writeString("2DA V2.b\n");
521 
522  // Write the column headers
523 
524  for (std::vector<Common::UString>::const_iterator h = _headers.begin(); h != _headers.end(); ++h) {
525  out.writeString(*h);
526  out.writeByte('\t');
527  }
528  out.writeByte('\0');
529 
530  // Write the row indices
531 
532  out.writeUint32LE((uint32) rowCount);
533  for (size_t i = 0; i < rowCount; i++) {
535  out.writeByte('\t');
536  }
537 
538  /* Deduplicate cell data strings. Binary 2DA files don't store the
539  * data for each cell directly: instead, each cell contains an offset
540  * into a data array. This way, cells with the same data only need to
541  * to store this data once.
542  *
543  * The original binary 2DA files in KotOR/KotOR2 make extensive use
544  * of that, and we should do this as well.
545  *
546  * Basically, this involves going through each cell, and looking up
547  * if we already saved this particular piece of data. If not, save
548  * it, otherwise only remember the offset. There's no need to be
549  * particularly smart about it, so we're just doing it the naive
550  * O(n^2) way.
551  */
552 
553  std::vector<Common::UString> data;
554  std::vector<size_t> offsets;
555 
556  data.reserve(cellCount);
557  offsets.reserve(cellCount);
558 
559  size_t dataSize = 0;
560 
561  std::vector<size_t> cells;
562  cells.reserve(cellCount);
563 
564  for (size_t i = 0; i < rowCount; i++) {
565  assert(_rows[i]);
566 
567  for (size_t j = 0; j < columnCount; j++) {
568  const Common::UString cell = _rows[i]->getString(j);
569 
570  // Do we already know about this cell data string?
571  size_t foundCell = SIZE_MAX;
572  for (size_t k = 0; k < data.size(); k++) {
573  if (data[k] == cell) {
574  foundCell = k;
575  break;
576  }
577  }
578 
579  // If not, add it to the cell data array
580  if (foundCell == SIZE_MAX) {
581  foundCell = data.size();
582 
583  data.push_back(cell);
584  offsets.push_back(dataSize);
585 
586  dataSize += data.back().size() + 1;
587 
588  if (dataSize > 65535)
589  throw Common::Exception("TwoDAFile::writeBinary(): Cell data size overflow");
590  }
591 
592  // Remember the offset to the cell data array
593  cells.push_back(offsets[foundCell]);
594  }
595  }
596 
597  // Write cell data offsets
598  for (std::vector<size_t>::const_iterator c = cells.begin(); c != cells.end(); ++c)
599  out.writeUint16LE((uint16) *c);
600 
601  // Size of the all cell data strings
602  out.writeUint16LE((uint16) dataSize);
603 
604  // Write cell data strings
605  for (std::vector<Common::UString>::const_iterator d = data.begin(); d != data.end(); ++d) {
606  out.writeString(*d);
607  out.writeByte('\0');
608  }
609 }
610 
611 bool TwoDAFile::writeBinary(const Common::UString &fileName) const {
612  Common::WriteFile file;
613  if (!file.open(fileName))
614  return false;
615 
616  writeBinary(file);
617  file.close();
618 
619  return true;
620 }
621 
623  // Write column headers
624 
625  for (size_t i = 0; i < _headers.size(); i++) {
626  const bool needQuote = _headers[i].contains(',');
627  if (needQuote)
628  out.writeByte('"');
629 
630  out.writeString(_headers[i]);
631 
632  if (needQuote)
633  out.writeByte('"');
634 
635  if (i < (_headers.size() - 1))
636  out.writeByte(',');
637  }
638 
639  out.writeByte('\n');
640 
641  // Write array
642 
643  for (size_t i = 0; i < _rows.size(); i++) {
644  for (size_t j = 0; j < _rows[i]->_data.size(); j++) {
645  const bool needQuote = _rows[i]->_data[j].contains(',');
646 
647  if (needQuote)
648  out.writeByte('"');
649 
650  if (_rows[i]->_data[j] != "****")
651  out.writeString(_rows[i]->_data[j]);
652 
653  if (needQuote)
654  out.writeByte('"');
655 
656  if (j < (_rows[i]->_data.size() - 1))
657  out.writeByte(',');
658  }
659 
660  out.writeByte('\n');
661  }
662 
663  out.flush();
664 }
665 
666 bool TwoDAFile::writeCSV(const Common::UString &fileName) const {
667  Common::WriteFile file;
668  if (!file.open(fileName))
669  return false;
670 
671  writeCSV(file);
672  file.close();
673 
674  return true;
675 }
676 
678  if (str.empty())
679  return 0;
680 
681  int32 v = 0;
682 
683  try {
684  Common::parseString(str, v);
685  } catch (...) {
686  }
687 
688  return v;
689 }
690 
692  if (str.empty())
693  return 0;
694 
695  float v = 0.0f;
696 
697  try {
698  Common::parseString(str, v);
699  } catch (...) {
700  }
701 
702  return v;
703 }
704 
705 } // End of namespace Aurora
Class to hold the two-dimensional array of a 2DA file.
Definition: 2dafile.h:124
void createHeaderMap()
Definition: 2dafile.cpp:336
static const uint32 k2DAID
Definition: 2dafile.cpp:46
#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
uint16 readUint16LE()
Read an unsigned 16-bit word stored in little endian (LSB first) order from the stream and return it...
Definition: readstream.h:122
TwoDAFile(Common::SeekableReadStream &twoda)
Definition: 2dafile.cpp:128
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).
void readHeaders2b(Common::SeekableReadStream &twoda)
Definition: 2dafile.cpp:258
uint32 readUint32LE()
Read an unsigned 32-bit word stored in little endian (LSB first) order from the stream and return it...
Definition: readstream.h:133
void load(Common::SeekableReadStream &twoda)
Definition: 2dafile.cpp:143
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
const Common::UString & getString(size_t column) const
Return the contents of a cell as a string.
Definition: 2dafile.cpp:59
virtual void flush()
Commit any buffered data to the underlying channel or storage medium; unbuffered streams can use the ...
Definition: writestream.cpp:69
A class holding an UTF-8 string.
Definition: ustring.h:48
virtual size_t seek(ptrdiff_t offset, Origin whence=kOriginBegin)=0
Sets the stream position indicator for the stream.
void writeString(const UString &str)
Write the given string to the stream, encoded as UTF-8.
Definition: writestream.cpp:94
PointerType release()
Returns the plain pointer value and releases ScopedPtr.
Definition: scopedptr.h:103
int64 getSint(uint32 field, int64 def=0) const
Definition: gff4file.cpp:909
void skipRowNames2b(Common::SeekableReadStream &twoda)
Definition: 2dafile.cpp:275
UString composeString(T value)
Convert any POD integer, float/double or bool type into a string.
Definition: strutil.cpp:276
virtual bool eos() const =0
Returns true if a read failed because the stream has been reached.
UString getToken(SeekableReadStream &stream)
Parse a token out of the stream.
Tokenizes a stream.
static const uint32 k2DAIDTab
Definition: 2dafile.cpp:47
std::vector< Common::UString > _headers
Definition: 2dafile.h:172
void addChunkEnd(uint32 c)
Add a character marking the end of a chunk.
Common::UString _defaultString
The default string to return should a cell not exist.
Definition: 2dafile.h:168
HeaderMap _headerMap
Definition: 2dafile.h:173
const char * findGDAHeader(uint32 hash)
size_t getRowCount() const
Return the number of rows in the array.
Definition: 2dafile.cpp:400
Resolve a GDA column header hash back to its string.
Utility templates and functions for working with strings and streams.
Exception that provides a stack of explanations.
Definition: error.h:36
static void readHeader(Common::ReadStream &stream, uint32 &id, uint32 &version, bool &utf16le)
Read the header out of a stream.
Definition: aurorafile.cpp:53
A simple scoped smart pointer template.
static const uint32 kVersion2b
Definition: 2dafile.cpp:49
Handling version V4.0/V4.1 of BioWare&#39;s GFFs (generic file format).
Basic exceptions to throw.
UString readStringLine(SeekableReadStream &stream, Encoding encoding)
Read a line with the given encoding out of a stream.
Definition: encoding.cpp:310
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
size_t getColumnCount() const
Return the number of columns in the array.
Definition: 2dafile.cpp:404
void writeCSV(Common::WriteStream &out) const
Write the 2DA data into a CSV stream.
Definition: 2dafile.cpp:622
bool open(const UString &fileName)
Try to open the file with the given fileName.
Definition: writefile.cpp:50
int32 _defaultInt
The default int to return should a cell not exist.
Definition: 2dafile.h:169
static UString format(const char *s,...) GCC_PRINTF(1
Print formatted data into an UString object, similar to sprintf().
Definition: ustring.cpp:718
void readRows2b(Common::SeekableReadStream &twoda)
Definition: 2dafile.cpp:294
double getDouble(uint32 field, double def=0.0) const
Definition: gff4file.cpp:925
uint16_t uint16
Definition: types.h:202
Utility templates and functions.
Handling BioWare&#39;s 2DAs (two-dimensional array).
std::vector< Common::UString > _data
Definition: 2dafile.h:86
virtual size_t skip(ptrdiff_t offset)
Skip the specified number of bytes, adding that offset to the current position in the stream...
Definition: readstream.h:317
void writeASCII(Common::WriteStream &out) const
Write the 2DA data into an V2.0 ASCII 2DA.
Definition: 2dafile.cpp:443
static const uint32 kVersion2a
Definition: 2dafile.cpp:48
Utility functions for working with differing string encodings.
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
void writeUint16LE(uint16 value)
Definition: writestream.h:98
void read2a(Common::SeekableReadStream &twoda)
Definition: 2dafile.cpp:172
uint32 _id
The file&#39;s ID.
Definition: aurorafile.h:77
StackException Exception
Definition: error.h:59
void addIgnore(uint32 c)
Add a character to ignore.
void writeBinary(Common::WriteStream &out) const
Write the 2DA data into an V2.b binary 2DA.
Definition: 2dafile.cpp:515
A scoped plain pointer, allowing pointer-y access and normal deletion.
Definition: scopedptr.h:120
uint32 _version
The file&#39;s version.
Definition: aurorafile.h:78
void writeByte(byte value)
Definition: writestream.h:88
int32 getInt(size_t column) const
Return the contents of a cell as an int.
Definition: 2dafile.cpp:75
Basic reading stream interfaces.
virtual size_t pos() const =0
Obtains the current value of the stream position indicator of the stream.
Generic interface for a writable data stream.
Definition: writestream.h:64
size_t getRowCount() const
Return the number of rows in the array.
Definition: gdafile.cpp:61
void readDefault2a(Common::SeekableReadStream &twoda, Common::StreamTokenizer &tokenize)
Definition: 2dafile.cpp:196
static float parseFloat(const Common::UString &str)
Definition: 2dafile.cpp:691
size_t getColumnCount() const
Return the number of columns in the array.
Definition: gdafile.cpp:57
const GFF4Struct * getRow(size_t row) const
Get a row as a GFF4 struct.
Definition: gdafile.cpp:73
void findFirstToken(SeekableReadStream &stream)
Find the first token character, skipping past separators.
Basic type definitions to handle files used in BioWare&#39;s Aurora engine.
const std::vector< Common::UString > & getHeaders() const
Return the columns&#39; headers.
Definition: 2dafile.cpp:408
Plain, unextended ASCII (7bit clean).
Definition: encoding.h:40
bool empty(size_t column) const
Check if the cell is empty.
Definition: 2dafile.cpp:107
void close()
Close the file, if open.
Definition: writefile.cpp:69
size_t getTokens(SeekableReadStream &stream, std::vector< UString > &list, size_t min=0, size_t max=SIZE_MAX, const UString &def="")
Parse tokens out of the stream.
const TwoDARow & getRow(size_t row) const
Get a row.
Definition: 2dafile.cpp:421
size_t size() const
Return the size of the string, in characters.
Definition: ustring.cpp:241
TwoDARow _emptyRow
Definition: 2dafile.h:175
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
void readRows2a(Common::SeekableReadStream &twoda, Common::StreamTokenizer &tokenize)
Definition: 2dafile.cpp:227
Parse tokens out of a stream.
static const uint32 kFieldIDInvalid
Definition: types.h:443
void nextChunk(SeekableReadStream &stream)
Skip past end of chunk characters.
Ignore all repeated separators.
Implementing the stream writing interfaces for files.
void readHeaders2a(Common::SeekableReadStream &twoda, Common::StreamTokenizer &tokenize)
Definition: 2dafile.cpp:216
#define SIZE_MAX
Definition: types.h:172
Common::PtrVector< TwoDARow > _rows
Definition: 2dafile.h:176
std::vector< Header > Headers
Definition: gdafile.h:84
float _defaultFloat
The default float to return should a cell not exist.
Definition: 2dafile.h:170
const Common::UString & getCell(size_t n) const
Definition: 2dafile.cpp:120
const Headers & getHeaders() const
Get the column headers.
Definition: gdafile.cpp:65
static int32 parseInt(const Common::UString &str)
Definition: 2dafile.cpp:677
void addSeparator(uint32 c)
Add a character on where to split tokens.
TwoDAFile * _parent
The parent 2DA.
Definition: 2dafile.h:84
void skipToken(SeekableReadStream &stream, size_t n=1)
Skip a number of tokens.
A row within a 2DA file.
Definition: 2dafile.h:61
uint64 getUint(uint32 field, uint64 def=0) const
Definition: gff4file.cpp:897
friend class TwoDARow
Definition: 2dafile.h:201
A simple streaming file writing class.
Definition: writefile.h:40
void read2b(Common::SeekableReadStream &twoda)
Definition: 2dafile.cpp:190
void addQuote(uint32 c)
Add a character able to enclose (quote) separators and chunk ends.
Interface for a seekable & readable data stream.
Definition: readstream.h:265
void writeUint32LE(uint32 value)
Definition: writestream.h:104
void parseString(const UString &str, T &value, bool allowEmpty)
Parse a string into any POD integer, float/double or bool type.
Definition: strutil.cpp:215
size_t headerToColumn(const Common::UString &header) const
Translate a column header to a column index.
Definition: 2dafile.cpp:412
TwoDARow(TwoDAFile &parent)
Definition: 2dafile.cpp:53
Class to hold the GFF&#39;d two-dimensional array of a GDA file.
Definition: gdafile.h:62
unsigned int uint
Definition: types.h:211
float getFloat(size_t column) const
Return the contents of a cell as a float.
Definition: 2dafile.cpp:91
int32_t int32
Definition: types.h:203
static const Common::UString kEmpty
Definition: 2dafile.cpp:119