xoreos  0.0.5
talktable_gff.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 TLK description on the Dragon Age toolset wiki
26  * (<http://social.bioware.com/wiki/datoolset/index.php/TLK>).
27  */
28 
29 #include <cassert>
30 
31 #include "src/common/util.h"
32 #include "src/common/error.h"
33 #include "src/common/readstream.h"
34 
36 #include "src/aurora/gff4file.h"
37 
38 static const uint32 kTLKID = MKTAG('T', 'L', 'K', ' ');
39 static const uint32 kVersion02 = MKTAG('V', '0', '.', '2');
40 static const uint32 kVersion04 = MKTAG('V', '0', '.', '4');
41 static const uint32 kVersion05 = MKTAG('V', '0', '.', '5');
42 
43 namespace Aurora {
44 
46  TalkTable(encoding) {
47 
48  load(tlk);
49 }
50 
52 }
53 
54 bool TalkTable_GFF::hasEntry(uint32 strRef) const {
55  return _entries.find(strRef) != _entries.end();
56 }
57 
58 static const Common::UString kEmptyString = "";
60  Entries::iterator e = _entries.find(strRef);
61  if (e == _entries.end())
62  return kEmptyString;
63 
64  readString(*e->second);
65 
66  return e->second->text;
67 }
68 
70  return kEmptyString;
71 }
72 
74  return kFieldIDInvalid;
75 }
76 
78  assert(tlk);
79 
80  try {
81  _gff.reset(new GFF4File(tlk, kTLKID));
82 
83  const GFF4Struct &top = _gff->getTopLevel();
84 
85  if (_gff->getTypeVersion() == kVersion02)
86  load02(top);
87  else if (_gff->getTypeVersion() == kVersion04)
88  load05(top);
89  else if (_gff->getTypeVersion() == kVersion05)
90  load05(top);
91  else
92  throw Common::Exception("Unsupported GFF TLK file version %08X", _gff->getTypeVersion());
93 
94  } catch (Common::Exception &e) {
95  e.add("Unable to load GFF TLK");
96  throw;
97  }
98 }
99 
101  if (!top.hasField(kGFF4TalkStringList))
102  return;
103 
104  const GFF4List &strings = top.getList(kGFF4TalkStringList);
105 
106  for (GFF4List::const_iterator s = strings.begin(); s != strings.end(); ++s) {
107  if (!*s)
108  continue;
109 
110  uint32 strRef = (*s)->getUint(kGFF4TalkStringID, 0xFFFFFFFF);
111  if (strRef == 0xFFFFFFFF)
112  continue;
113 
114  Common::ScopedPtr<Entry> entry(new Entry(*s));
115 
116  std::pair<Entries::iterator, bool> result = _entries.insert(std::make_pair(strRef, entry.get()));
117  if (result.second)
118  entry.release();
119  }
120 }
121 
123  if (!top.hasField(kGFF4HuffTalkStringList) ||
126  return;
127 
128  const GFF4List &strings = top.getList(kGFF4HuffTalkStringList);
129 
130  for (GFF4List::const_iterator s = strings.begin(); s != strings.end(); ++s) {
131  if (!*s)
132  continue;
133 
134  uint32 strRef = (*s)->getUint(kGFF4HuffTalkStringID, 0xFFFFFFFF);
135  if (strRef == 0xFFFFFFFF)
136  continue;
137 
138  Common::ScopedPtr<Entry> entry(new Entry(*s));
139 
140  std::pair<Entries::iterator, bool> result = _entries.insert(std::make_pair(strRef, entry.get()));
141  if (result.second)
142  entry.release();
143  }
144 }
145 
146 void TalkTable_GFF::readString(Entry &entry) const {
147  if (!entry.strct)
148  return;
149 
150  if (_gff->getTypeVersion() == kVersion02)
151  readString02(entry);
152  else if (_gff->getTypeVersion() == kVersion04)
153  readString05(entry, _gff->isBigEndian());
154  else if (_gff->getTypeVersion() == kVersion05)
155  readString05(entry, _gff->isBigEndian());
156 
157  entry.strct = 0;
158 }
159 
160 void TalkTable_GFF::readString02(Entry &entry) const {
162  entry.text = entry.strct->getString(kGFF4TalkString, _encoding);
163  else
164  entry.text = "[???]";
165 }
166 
167 void TalkTable_GFF::readString05(Entry &entry, bool bigEndian) const {
169  huffTree (_gff->getTopLevel().getData(kGFF4HuffTalkStringHuffTree)),
170  bitStream(_gff->getTopLevel().getData(kGFF4HuffTalkStringBitStream));
171 
172  if (!huffTree || !bitStream)
173  return;
174 
175  Common::SeekableSubReadStreamEndian huffTreeEndian(huffTree.get(), 0, huffTree->size(), bigEndian);
176  Common::SeekableSubReadStreamEndian bitStreamEndian(bitStream.get(), 0, bitStream->size(), bigEndian);
177 
178  readString05(huffTreeEndian, bitStreamEndian, entry);
179 }
180 
182  Common::SeekableSubReadStreamEndian &bitStream, Entry &entry) const {
183 
184  /* Read a string encoded in a Huffman'd bitstream.
185  *
186  * The Huffman tree itself is made up of signed 32bit nodes:
187  * - Positive values are internal nodes, encoding a child index
188  * - Negative values are leaf nodes, encoding an UTF-16 character value
189  *
190  * Kudos to Rick (gibbed) (<http://gib.me/>).
191  */
192 
193  std::vector<uint16> utf16Str;
194 
195  const uint32 startOffset = entry.strct->getUint(kGFF4HuffTalkStringBitOffset);
196 
197  uint32 index = startOffset >> 5;
198  uint32 shift = startOffset & 0x1F;
199 
200  do {
201  ptrdiff_t e = (huffTree.size() / 8) - 1;
202 
203  while (e >= 0) {
204  bitStream.seek(index * 4);
205  const ptrdiff_t offset = (bitStream.readUint32() >> shift) & 1;
206 
207  huffTree.seek(((e * 2) + offset) * 4);
208  e = huffTree.readSint32();
209 
210  shift++;
211  index += (shift >> 5);
212 
213  shift %= 32;
214  }
215 
216  utf16Str.push_back(TO_LE_16(0xFFFF - e));
217 
218  } while (utf16Str.back() != 0);
219 
220  const byte *data = reinterpret_cast<const byte *>(&utf16Str[0]);
221  const size_t size = utf16Str.size() * 2;
222 
223  entry.text = Common::readString(data, size, Common::kEncodingUTF16LE);
224 }
225 
226 } // 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
void readString05(Entry &entry, bool bigEndian) const
void add(const char *s,...) GCC_PRINTF(2
Definition: error.cpp:58
This is a wrapper around SeekableSubReadStream, but it adds non-endian read methods whose endianness ...
Definition: readstream.h:383
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
PointerType release()
Returns the plain pointer value and releases ScopedPtr.
Definition: scopedptr.h:103
Base class for BioWare&#39;s talk tables.
Definition: talktable.h:52
uint32 getSoundID(uint32 strRef) const
UTF-16 LE (little endian).
Definition: encoding.h:44
TalkTable_GFF(Common::SeekableReadStream *tlk, Common::Encoding encoding)
Take over this stream and read a GFF&#39;d TLK out of it.
static const uint32 kVersion05
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
static const uint32 kTLKID
Exception that provides a stack of explanations.
Definition: error.h:36
size_t size() const
Obtains the total size of the stream, measured in bytes.
Definition: readstream.cpp:144
std::vector< const GFF4Struct * > GFF4List
Definition: types.h:453
Handling version V4.0/V4.1 of BioWare&#39;s GFFs (generic file format).
Basic exceptions to throw.
static const uint32 kVersion02
size_t seek(ptrdiff_t offset, Origin whence=kOriginBegin)
Sets the stream position indicator for the stream.
Definition: readstream.cpp:148
#define UNUSED(x)
Definition: system.h:170
void load(Common::SeekableReadStream *tlk)
Utility templates and functions.
Common::Encoding _encoding
Definition: talktable.h:70
const GFF4Struct * strct
Definition: talktable_gff.h:78
bool hasField(uint32 field) const
Does this specific field exist?
Definition: gff4file.cpp:573
Encoding
Definition: encoding.h:37
const Common::UString & getString(uint32 strRef) const
StackException Exception
Definition: error.h:59
A scoped plain pointer, allowing pointer-y access and normal deletion.
Definition: scopedptr.h:120
void load02(const GFF4Struct &top)
Basic reading stream interfaces.
static const uint32 kVersion04
Common::ScopedPtr< GFF4File > _gff
Definition: talktable_gff.h:86
PointerType get() const
Returns the plain pointer value.
Definition: scopedptr.h:96
void readString(Entry &entry) const
const Common::UString & getSoundResRef(uint32 strRef) const
uint32_t uint32
Definition: types.h:204
static const uint32 kFieldIDInvalid
Definition: types.h:443
bool hasEntry(uint32 strRef) const
UString readString(SeekableReadStream &stream, Encoding encoding)
Read a string with the given encoding of a stream.
Definition: encoding.cpp:287
const GFF4List & getList(uint32 field) const
Definition: gff4file.cpp:1394
static const Common::UString kEmptyString
Definition: talkman.cpp:129
void load05(const GFF4Struct &top)
uint64 getUint(uint32 field, uint64 def=0) const
Definition: gff4file.cpp:897
void readString02(Entry &entry) const
Interface for a seekable & readable data stream.
Definition: readstream.h:265
Handling BioWare&#39;s GFF&#39;d talk tables.
uint8 byte
Definition: types.h:209