xoreos  0.0.5
foxpro.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 #include <cstring>
26 #include <cstdio>
27 
28 #include "src/common/foxpro.h"
29 #include "src/common/error.h"
30 #include "src/common/scopedptr.h"
31 #include "src/common/encoding.h"
33 #include "src/common/writestream.h"
34 #include "src/common/datetime.h"
35 
36 namespace Common {
37 
38 FoxPro::FoxPro() : _hasIndex(false), _hasMemo(false), _memoBlockSize(512) {
39  updateUpdate();
40 }
41 
43 }
44 
46  SeekableReadStream *fpt) {
47 
48  assert(dbf);
49 
50  // Read header
51  uint32 recordSize, recordCount, firstRecordPos;
52  loadHeader(*dbf, recordSize, recordCount, firstRecordPos);
53  if (_hasIndex && !cdx)
54  throw Exception("Index needed");
55  if (_hasMemo && !fpt)
56  throw Exception("Memo needed");
57 
58  // Read fields
59  loadFields(*dbf, recordSize);
60  if (_hasMemo && !fpt)
61  throw Exception("Memo needed");
62 
63  // Read records
64  dbf->seek(firstRecordPos);
65  loadRecords(*dbf, recordSize, recordCount);
66 
67  // Read memos
68  if (fpt)
69  loadMemos(*fpt);
70 
71  // TODO: Read the compound index (CDX) file
72 }
73 
74 void FoxPro::loadHeader(SeekableReadStream &dbf, uint32 &recordSize, uint32 &recordCount,
75  uint32 &firstRecordPos) {
76 
77  byte version = dbf.readByte();
78  if (version != 0xF5)
79  throw Exception("Unknown database version 0x%02X", version);
80 
81  _lastUpdateYear = dbf.readByte() + 2000;
82  _lastUpdateMonth = dbf.readByte();
83  _lastUpdateDay = dbf.readByte();
84 
85  recordCount = dbf.readUint32LE();
86  firstRecordPos = dbf.readUint16LE();
87  recordSize = dbf.readUint16LE();
88 
89  dbf.skip(16); // Reserved
90 
91  byte flags = dbf.readByte();
92 
93  _hasIndex = flags & 0x01;
94  _hasMemo = (flags & 0x02) != 0;
95 
96  if (flags & 0x04)
97  throw Exception("DBC unsupported");
98  if (flags & 0xF8)
99  throw Exception("Unknown flags 0x%02X", flags);
100 
101  dbf.skip(1); // Codepage marker
102  dbf.skip(2); // Reserved
103 }
104 
106  // Read all field descriptions, 0x0D is the end marker
107  uint32 fieldsLength = 0;
108  while (!dbf.eos() && (dbf.readByte() != 0x0D)) {
109  Field field;
110 
112 
113  field.name = readStringFixed(dbf, kEncodingASCII, 11);
114 
115  field.type = (Type) dbf.readByte();
116  field.offset = dbf.readUint32LE();
117  field.size = dbf.readByte();
118  field.decimals = dbf.readByte();
119 
120  field.flags = dbf.readByte();
121 
122  field.autoIncNext = dbf.readUint32LE();
123  field.autoIncStep = dbf.readByte();
124 
125  dbf.skip(8); // Reserved
126 
127  if (field.offset != (fieldsLength + 1))
128  throw Exception("Field offset makes no sense (%d != %d)",
129  field.offset, fieldsLength + 1);
130 
131  if (field.type == kTypeMemo)
132  _hasMemo = true;
133 
134  fieldsLength += field.size;
135 
136  _fields.push_back(field);
137  }
138 
139  if (recordSize != (fieldsLength + 1))
140  throw Exception("Length of all fields does not equal the record size");
141 }
142 
143 void FoxPro::loadRecords(SeekableReadStream &dbf, uint32 recordSize, uint32 recordCount) {
144  _pool.push_back(new byte[recordSize * recordCount]);
145  byte *recordData = _pool.back();
146 
147  if (dbf.read(recordData, recordSize * recordCount) != (recordSize * recordCount))
148  throw Exception(kReadError);
149 
150  if (dbf.readByte() != 0x1A)
151  throw Exception("Record end marker missing");
152 
153  uint32 fieldCount = _fields.size();
154 
155  // Create the records array
156  _records.resize(recordCount);
157  for (uint32 i = 0; i < recordCount; i++) {
158  Record &record = _records[i];
159  byte *data = recordData + i * recordSize;
160 
161  char status = *data++;
162  if ((status != ' ') && (status != '*'))
163  throw Exception("Unknown record status '%c'", status);
164 
165  record.deleted = status == '*';
166 
167  record.fields.resize(fieldCount);
168  for (uint32 j = 0; j < fieldCount; j++) {
169  record.fields[j] = data;
170  data += _fields[j].size;
171  }
172  }
173 }
174 
176  fpt.skip(4); // Next free block
177  fpt.skip(2); // Unused
178 
180  if (_memoBlockSize < 33)
181  _memoBlockSize *= 1024;
182 
183  fpt.skip(504); // Unused
184 
185  while (!fpt.eos()) {
186  _memos.push_back(new byte[_memoBlockSize]);
187  byte *data = _memos.back();
188 
189  fpt.read(data, _memoBlockSize);
190  }
191 }
192 
193 void FoxPro::save(WriteStream *dbf, WriteStream *cdx, WriteStream *fpt) const {
194  assert(dbf);
195 
196  if (_hasIndex && !cdx)
197  throw Exception("Index needed");
198  if (_hasMemo && !fpt)
199  throw Exception("Memo needed");
200 
201  if (_records.empty() || _fields.empty())
202  throw Exception("No records / fields");
203 
204  saveHeader(*dbf);
205  saveFields(*dbf);
206  saveRecords(*dbf);
207  dbf->flush();
208 
209  if (fpt) {
210  saveMemos(*fpt);
211  fpt->flush();
212  }
213 
214  // TODO: Write the compound index (CDX) file
215 
216  if (cdx)
217  cdx->flush();
218 }
219 
220 void FoxPro::saveHeader(WriteStream &dbf) const {
221  dbf.writeByte(0xF5); // Version
222 
223  dbf.writeByte(_lastUpdateYear - 2000);
226 
227  dbf.writeUint32LE(_records.size());
228 
229  // Header + fields + field end marker
230  uint16 firstRecordPos = 32 + _fields.size() * 32 + 1;
231  dbf.writeUint16LE(firstRecordPos);
232 
233  uint16 recordSize = _fields.back().offset + _fields.back().size;
234  dbf.writeUint16LE(recordSize);
235 
236  dbf.writeUint32LE(0x00000000); // Reserved
237  dbf.writeUint32LE(0x00000000); // Reserved
238  dbf.writeUint32LE(0x00000000); // Reserved
239  dbf.writeUint32LE(0x00000000); // Reserved
240 
241  uint8 flags = (_hasIndex ? 0x01 : 0x00) | (_hasMemo ? 0x02 : 0x00);
242  dbf.writeByte(flags);
243 
244  dbf.writeByte(0x00); // Codepage marker
245 
246  dbf.writeUint16LE(0x0000); // Reserved
247 }
248 
249 void FoxPro::saveFields(WriteStream &dbf) const {
250  for (size_t i = 0; i < _fields.size(); i++) {
251  const Field &field = _fields[i];
252 
253  const size_t nameLength = std::strlen(field.name.c_str());
254 
255  dbf.write(field.name.c_str(), MIN<size_t>(10, nameLength));
256  dbf.writeByte(0x00);
257 
258  if (nameLength < 10)
259  for (size_t j = 0; j < (10 - nameLength); j++)
260  dbf.writeByte(0x00);
261 
262  dbf.writeByte((byte) ((char) field.type));
263 
264  dbf.writeUint32LE(field.offset);
265 
266  dbf.writeByte(field.size);
267  dbf.writeByte(field.decimals);
268  dbf.writeByte(field.flags);
269 
270  dbf.writeUint32LE(field.autoIncNext);
271  dbf.writeByte (field.autoIncStep);
272 
273  dbf.writeUint32LE(0x00000000); // Reserved
274  dbf.writeUint32LE(0x00000000); // Reserved
275  }
276 
277  dbf.writeByte(0x0D); // Field end marker
278 }
279 
281  // Write the records
282  for (size_t i = 0; i < _records.size(); i++) {
283  const Record &record = _records[i];
284 
285  dbf.writeByte(record.deleted ? '*' : ' ');
286 
287  for (size_t j = 0; j < _fields.size(); j++)
288  dbf.write(record.fields[j], _fields[j].size);
289  }
290 
291  dbf.writeByte(0x1A); // Records end marker
292 }
293 
294 void FoxPro::saveMemos(WriteStream &fpt) const {
295  fpt.writeUint32BE(_memos.size() + 1); // Next free block
296  fpt.writeUint16BE(0x0000); // Reserved
298 
299  // Reserved
300  for (int i = 0; i < 126; i++)
301  fpt.writeUint32BE(0x00000000);
302 
303  for (size_t i = 0; i < _memos.size(); i++)
304  fpt.write(_memos[i], _memoBlockSize);
305 }
306 
307 void FoxPro::getLastUpdate(uint16 &lastUpdateYear, uint8 &lastUpdateMonth, uint8 &lastUpdateDay) const {
308  lastUpdateYear = _lastUpdateYear;
309  lastUpdateMonth = _lastUpdateMonth;
310  lastUpdateDay = _lastUpdateDay;
311 }
312 
313 bool FoxPro::hasIndex() const {
314  return _hasIndex;
315 }
316 
317 bool FoxPro::hasMemo() const {
318  return _hasMemo;
319 }
320 
321 size_t FoxPro::getFieldCount() const {
322  return _fields.size();
323 }
324 
325 size_t FoxPro::getRecordCount() const {
326  return _records.size();
327 }
328 
329 const std::vector<FoxPro::Field> &FoxPro::getFields() const {
330  return _fields;
331 }
332 
333 const std::vector<FoxPro::Record> &FoxPro::getRecords() const {
334  return _records;
335 }
336 
337 UString FoxPro::getString(const Record &record, size_t field) const {
338  assert(field < _fields.size());
339 
340  const Field &f = _fields[field];
341  if (f.type != kTypeString)
342  throw Exception("Field is not of string type ('%c')", f.type);
343 
344  MemoryReadStream stream(record.fields[field], f.size);
345 
346  UString str = readStringFixed(stream, kEncodingLatin9, f.size);
347 
348  // xBase fields are padded with spaces...
349  str.trimRight();
350 
351  return str;
352 }
353 
354 int32 FoxPro::getInt(const Record &record, size_t field) const {
355  assert(field < _fields.size());
356 
357  const Field &f = _fields[field];
358 
359  int32 i = 0;
360  if (f.type == kTypeNumber) {
361 
362  if (!getInt(record.fields[field], f.size, i))
363  i = 0;
364 
365  } else if (f.type == kTypeInteger) {
366 
367  if (f.size != 4)
368  throw Exception("Integer field size != 4 (%d)", f.size);
369 
370  i = READ_LE_UINT32(record.fields[field]);
371 
372  } else
373  throw Exception("Field is not of int type ('%c')", f.type);
374 
375  return i;
376 }
377 
378 bool FoxPro::getBool(const Record &record, size_t field) const {
379  assert(field < _fields.size());
380 
381  const Field &f = _fields[field];
382  if (f.type != kTypeBool)
383  throw Exception("Field is not of bool type ('%c')", f.type);
384 
385  if (f.size != 1)
386  throw Exception("Bool field size != 1 (%d)", f.size);
387 
388  char c = (char) record.fields[field][0];
389 
390  if ((c == 't') || (c == 'T') || (c == 'y') || (c == 'Y') || (c == '1'))
391  return true;
392 
393  return false;
394 }
395 
396 double FoxPro::getDouble(const Record &record, size_t field) const {
397  assert(field < _fields.size());
398 
399  const Field &f = _fields[field];
400 
401  char n[32];
402  double d = 0.0;
403  if (f.type == kTypeNumber) {
404 
405  if (f.size > 31)
406  throw Exception("Numerical field size > 31 (%d)", f.size);
407 
408  strncpy(n, reinterpret_cast<const char *>(record.fields[field]), f.size);
409  n[f.size] = '\0';
410 
411  if (std::sscanf(n, "%lf", &d) != 1)
412  d = 0.0;
413 
414  } else if (f.type == kTypeFloat) {
415 
416  if (f.size != 4)
417  throw Exception("Float field size != 4 (%d)", f.size);
418 
419  d = convertIEEEFloat(READ_LE_UINT32(record.fields[field]));
420 
421  } else if (f.type == kTypeDouble) {
422 
423  if (f.size != 8)
424  throw Exception("Double field size != 8 (%d)", f.size);
425 
426  d = convertIEEEDouble(READ_LE_UINT64(record.fields[field]));
427 
428  } else
429  throw Exception("Field is not of double type ('%c')", f.type);
430 
431  return d;
432 }
433 
434 void FoxPro::getDate(const Record &record, size_t field, uint16 &year, uint8 &month, uint8 &day) {
435  assert(field < _fields.size());
436 
437  const Field &f = _fields[field];
438  if (f.type != kTypeDate)
439  throw Exception("Field is not of date type ('%c')", f.type);
440 
441  if (f.size != 8)
442  throw Exception("Date field size != 8 (%d)", f.size);
443 
444  // Date fields are not 0-terminated, so create a 0-terminated copy for reading
445 
446  char tmp[9];
447  memcpy(tmp, record.fields[field], MIN<size_t>(f.size, sizeof(tmp)));
448  tmp[sizeof(tmp) - 1] = '\0';
449 
450  uint fieldYear, fieldMonth, fieldDay;
451  if (std::sscanf(tmp, "%4u%2u%2u", &fieldYear, &fieldMonth, &fieldDay) != 3)
452  throw Exception("Failed reading the date");
453 
454  year = fieldYear;
455  month = fieldMonth;
456  day = fieldDay;
457 }
458 
459 SeekableReadStream *FoxPro::getMemo(const Record &record, size_t field) const {
460  assert(field < _fields.size());
461 
462  const Field &f = _fields[field];
463  if (f.type != kTypeMemo)
464  throw Exception("Field is not of memo type ('%c')", f.type);
465 
466  int32 i = 0;
467  if (!getInt(record.fields[field], f.size, i) || (i < 1))
468  return 0;
469 
470  size_t block = ((uint32) i) - 1;
471 
472  if (block >= _memos.size())
473  throw Exception("Memo block #%u >= memo block count %u", (uint)block, (uint)_memos.size());
474 
475  size_t type = READ_BE_UINT32(_memos[block] + 0);
476  size_t size = READ_BE_UINT32(_memos[block] + 4);
477 
478  if ((type != 0x00) && (type != 0x01) && (type != 0x02))
479  throw Exception("Memo type unknown (%u)", (uint)type);
480 
481  bool first = true;
482 
483  size_t dataSize = size;
484 
485  // Read the data
486  ScopedArray<byte> data(new byte[size]);
487  byte *dataPtr = data.get();
488 
489  while (size > 0) {
490  if (block >= _memos.size())
491  throw Exception("Memo block #%u >= memo block count %u", (uint)block, (uint)_memos.size());
492 
493  size_t n = MIN<size_t>(size, _memoBlockSize - (first ? 8 : 0));
494 
495  std::memcpy(dataPtr, _memos[block] + (first ? 8 : 0), n);
496 
497  dataPtr += n;
498  size -= n;
499  block += 1;
500 
501  first = false;
502  }
503 
504  return new MemoryReadStream(data.release(), dataSize, true);
505 }
506 
507 void FoxPro::deleteRecord(size_t record) {
508  assert(record < _records.size());
509 
510  _records[record].deleted = true;
511 
512  updateUpdate();
513 
514  // TODO: Deleting a record should also mark any memo fields in that
515  // record as free. They should be reused when adding a memo
516  // field of equals or less size.
517 }
518 
519 void FoxPro::checkName(const UString &name) {
520  for (UString::iterator c = name.begin(); c != name.end(); ++c)
521  if (!UString::isASCII(*c))
522  throw Common::Exception("FoxPro field names need to be in unextended ASCII");
523 }
524 
525 size_t FoxPro::addFieldString(const UString &name, uint8 size) {
526  checkName(name);
527 
528  size_t offset = 1;
529  if (!_fields.empty())
530  offset = _fields.back().offset + _fields.back().size;
531 
532  _fields.push_back(Field());
533 
534  Field &field = _fields.back();
535 
536  field.name = name;
537  field.type = kTypeString;
538  field.offset = offset;
539  field.size = size;
540  field.decimals = 0;
541  field.flags = 0;
542  field.autoIncNext = 0;
543  field.autoIncStep = 0;
544 
545  addField(field.size);
546  updateUpdate();
547 
548  return _fields.size() - 1;
549 }
550 
551 size_t FoxPro::addFieldNumber(const UString &name, uint8 size, uint8 decimals) {
552  checkName(name);
553 
554  size_t offset = 1;
555  if (!_fields.empty())
556  offset = _fields.back().offset + _fields.back().size;
557 
558  _fields.push_back(Field());
559 
560  Field &field = _fields.back();
561 
562  field.name = name;
563  field.type = kTypeNumber;
564  field.offset = offset;
565  field.size = size;
566  field.decimals = decimals;
567  field.flags = 0;
568  field.autoIncNext = 0;
569  field.autoIncStep = 0;
570 
571  addField(field.size);
572  updateUpdate();
573 
574  return _fields.size() - 1;
575 }
576 
577 size_t FoxPro::addFieldInt(const UString &name) {
578  checkName(name);
579 
580  size_t offset = 1;
581  if (!_fields.empty())
582  offset = _fields.back().offset + _fields.back().size;
583 
584  _fields.push_back(Field());
585 
586  Field &field = _fields.back();
587 
588  field.name = name;
589  field.type = kTypeInteger;
590  field.offset = offset;
591  field.size = 4;
592  field.decimals = 0;
593  field.flags = 0;
594  field.autoIncNext = 0;
595  field.autoIncStep = 0;
596 
597  addField(field.size);
598  updateUpdate();
599 
600  return _fields.size() - 1;
601 }
602 
603 size_t FoxPro::addFieldBool(const UString &name) {
604  checkName(name);
605 
606  size_t offset = 1;
607  if (!_fields.empty())
608  offset = _fields.back().offset + _fields.back().size;
609 
610  _fields.push_back(Field());
611 
612  Field &field = _fields.back();
613 
614  field.name = name;
615  field.type = kTypeBool;
616  field.offset = offset;
617  field.size = 1;
618  field.decimals = 0;
619  field.flags = 0;
620  field.autoIncNext = 0;
621  field.autoIncStep = 0;
622 
623  addField(field.size);
624  updateUpdate();
625 
626  return _fields.size() - 1;
627 }
628 
629 size_t FoxPro::addFieldDate(const UString &name) {
630  checkName(name);
631 
632  size_t offset = 1;
633  if (!_fields.empty())
634  offset = _fields.back().offset + _fields.back().size;
635 
636  _fields.push_back(Field());
637 
638  Field &field = _fields.back();
639 
640  field.name = name;
641  field.type = kTypeDate;
642  field.offset = offset;
643  field.size = 8;
644  field.decimals = 0;
645  field.flags = 0;
646  field.autoIncNext = 0;
647  field.autoIncStep = 0;
648 
649  addField(field.size);
650  updateUpdate();
651 
652  return _fields.size() - 1;
653 }
654 
655 size_t FoxPro::addFieldMemo(const UString &name) {
656  checkName(name);
657 
658  size_t offset = 1;
659  if (!_fields.empty())
660  offset = _fields.back().offset + _fields.back().size;
661 
662  _fields.push_back(Field());
663 
664  Field &field = _fields.back();
665 
666  field.name = name;
667  field.type = kTypeMemo;
668  field.offset = offset;
669  field.size = 10;
670  field.decimals = 0;
671  field.flags = 0;
672  field.autoIncNext = 0;
673  field.autoIncStep = 0;
674 
675  addField(field.size);
676  updateUpdate();
677 
678  _hasMemo = true;
679 
680  return _fields.size() - 1;
681 }
682 
684  if (_records.empty())
685  return;
686 
687  size_t dataSize = size * _records.size();
688 
689  _pool.push_back(new byte[dataSize]);
690 
691  byte *data = _pool.back();
692 
693  for (std::vector<Record>::iterator it = _records.begin(); it != _records.end(); ++it) {
694  it->fields.push_back(data);
695  data += size;
696  }
697 }
698 
700  _records.push_back(Record());
701  Record &record = _records.back();
702 
703  record.deleted = false;
704 
705  if (!_fields.empty()) {
706  size_t dataSize = _fields.back().offset + _fields.back().size;
707 
708  _pool.push_back(new byte[dataSize]);
709 
710  byte *data = _pool.back();
711 
712  record.fields.resize(_fields.size());
713  for (size_t i = 0; i < _fields.size(); i++) {
714  record.fields[i] = data;
715  data += _fields[i].size;
716  }
717  }
718 
719  updateUpdate();
720 
721  return _records.size() - 1;
722 }
723 
724 void FoxPro::setString(size_t record, size_t field, const UString &value) {
725  assert((record < _records.size()) && (field < _fields.size()));
726 
727  Record &r = _records[record];
728  Field &f = _fields[field];
729 
730  if (f.type != kTypeString)
731  throw Exception("Field is not of string type ('%c')", f.type);
732 
733  char *data = reinterpret_cast<char *>(r.fields[field]);
734  char *dataEnd = reinterpret_cast<char *>(r.fields[field]) + f.size;
735 
736  strncpy(data, value.c_str(), f.size);
737 
738  data += std::strlen(value.c_str());
739 
740  while (data < dataEnd)
741  *dataEnd++ = 0x20;
742 
743  updateUpdate();
744 }
745 
746 void FoxPro::setInt(size_t record, size_t field, int32 value) {
747  assert((record < _records.size()) && (field < _fields.size()));
748 
749  Record &r = _records[record];
750  Field &f = _fields[field];
751 
752  char *data = reinterpret_cast<char *>(r.fields[field]);
753 
754  if (f.type == kTypeNumber) {
755 
756  if (f.decimals != 0)
757  snprintf(data, f.size, "%*d", f.size, value);
758  else
759  snprintf(data, f.size, "%*.*f", f.size, f.decimals, (double) value);
760 
761  } else if (f.type == kTypeInteger) {
762 
763  if (f.size != 4)
764  throw Exception("Integer field size != 4 (%d)", f.size);
765 
766  WRITE_LE_UINT32(data, value);
767 
768  } else
769  throw Exception("Field is not of int type ('%c')", f.type);
770 
771  updateUpdate();
772 }
773 
774 void FoxPro::setBool(size_t record, size_t field, bool value) {
775  assert((record < _records.size()) && (field < _fields.size()));
776 
777  Record &r = _records[record];
778  Field &f = _fields[field];
779 
780  if (f.type != kTypeBool)
781  throw Exception("Field is not of bool type ('%c')", f.type);
782 
783  if (f.size != 1)
784  throw Exception("Bool field size != 1 (%d)", f.size);
785 
786  char *data = reinterpret_cast<char *>(r.fields[field]);
787 
788  data[0] = value ? 'T' : 'F';
789 
790  updateUpdate();
791 }
792 
793 void FoxPro::setDouble(size_t record, size_t field, double value) {
794  assert((record < _records.size()) && (field < _fields.size()));
795 
796  Record &r = _records[record];
797  Field &f = _fields[field];
798 
799  char *data = reinterpret_cast<char *>(r.fields[field]);
800 
801  if (f.type == kTypeNumber) {
802 
803  if (f.decimals != 0)
804  snprintf(data, f.size, "%*d", f.size, (int32) value);
805  else
806  snprintf(data, f.size, "%*.*f", f.size, f.decimals, value);
807 
808  } else if (f.type == kTypeFloat) {
809 
810  if (f.size != 4)
811  throw Exception("Float field size != 4 (%d)", f.size);
812 
813  WRITE_LE_UINT32(data, convertIEEEFloat((float) value));
814 
815  } else if (f.type == kTypeDouble) {
816 
817  if (f.size != 8)
818  throw Exception("Double field size != 8 (%d)", f.size);
819 
820  WRITE_LE_UINT64(data, convertIEEEDouble(value));
821 
822  } else
823  throw Exception("Field is not of double type ('%c')", f.type);
824 
825  updateUpdate();
826 }
827 
828 void FoxPro::setDate(size_t record, size_t field, uint16 year, uint8 month, uint8 day) {
829  assert((record < _records.size()) && (field < _fields.size()));
830 
831  Record &r = _records[record];
832  Field &f = _fields[field];
833 
834  if (f.type != kTypeDate)
835  throw Exception("Field is not of date type ('%c')", f.type);
836  if (f.size != 8)
837  throw Exception("Date field size != 8 (%d)", f.size);
838 
839  // Date fields are not 0-terminated, so copy the date after creation
840 
841  char tmp[12];
842  snprintf(tmp, sizeof(tmp), "%04u%02u%02u", (uint) year, (uint) month, (uint) day);
843 
844  memcpy(r.fields[field], tmp, f.size);
845 
846  updateUpdate();
847 }
848 
849 void FoxPro::setMemo(size_t record, size_t field, SeekableReadStream *value) {
850  assert((record < _records.size()) && (field < _fields.size()));
851 
852  Record &r = _records[record];
853  Field &f = _fields[field];
854 
855  if (f.type != kTypeMemo)
856  throw Exception("Field is not of memo type ('%c')", f.type);
857 
858  char *data = reinterpret_cast<char *>(r.fields[field]);
859 
860  if (!value) {
861  std::memset(data, 0x20, f.size);
862  updateUpdate();
863  return;
864  }
865 
866  value->seek(0);
867 
868  size_t size = value->size();
869 
870  size_t block = _memos.size();
871  _memos.push_back(new byte[_memoBlockSize]);
872 
873  size_t startBlock = block + 1;
874 
875  WRITE_BE_UINT32(_memos[block] , 1);
876  WRITE_BE_UINT32(_memos[block] + 4, size);
877 
878  bool first = true;
879  while (size > 0) {
880  size_t n = MIN<size_t>(size, _memoBlockSize - (first ? 8 : 0));
881 
882  if (value->read(_memos[block] + (first ? 8 : 0), n) != n)
883  throw Exception(kReadError);
884 
885  size -= n;
886  block += 1;
887 
888  if (size > 0)
889  _memos.push_back(new byte[_memoBlockSize]);
890 
891  first = false;
892  }
893 
894  if (f.decimals != 0)
895  snprintf(data, f.size, "%*u", f.size, (uint) startBlock);
896  else
897  snprintf(data, f.size, "%*.*f", f.size, f.decimals, (double) startBlock);
898 
899  updateUpdate();
900 }
901 
902 bool FoxPro::getInt(const byte *data, size_t size, int32 &i) {
903  char n[32];
904 
905  if (size > 31)
906  throw Exception("Numerical field size > 31 (%u)", (uint)size);
907 
908  strncpy(n, reinterpret_cast<const char *>(data), size);
909  n[size] = '\0';
910 
911  return std::sscanf(n, "%d", &i) == 1;
912 }
913 
916 }
917 
918 } // End of namespace Common
int32 getInt(const Record &record, size_t field) const
Definition: foxpro.cpp:354
static void checkName(const UString &name)
Definition: foxpro.cpp:519
bool getBool(const Record &record, size_t field) const
Definition: foxpro.cpp:378
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
bool deleted
Has this record been deleted?
Definition: foxpro.h:76
void loadRecords(SeekableReadStream &dbf, uint32 recordSize, uint32 recordCount)
Definition: foxpro.cpp:143
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
Definition: 2dafile.h:39
bool _hasIndex
Definition: foxpro.h:134
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.
PointerType release()
Returns the plain pointer value and releases ScopedPtr.
Definition: scopedptr.h:103
const std::vector< Record > & getRecords() const
Definition: foxpro.cpp:333
A date/time object, storing a specific point in time.
Definition: datetime.h:36
size_t addFieldInt(const UString &name)
Definition: foxpro.cpp:577
uint8_t uint8
Definition: types.h:200
virtual bool eos() const =0
Returns true if a read failed because the stream has been reached.
A field description.
Definition: foxpro.h:60
void deleteRecord(size_t record)
Definition: foxpro.cpp:507
Implementing the reading stream interfaces for plain memory blocks.
size_t getRecordCount() const
Definition: foxpro.cpp:325
iterator begin() const
Definition: ustring.cpp:253
size_t addRecord()
Definition: foxpro.cpp:699
void saveMemos(WriteStream &fpt) const
Definition: foxpro.cpp:294
double convertIEEEDouble(uint64 data)
Convert a uint64 holding the bit pattern of a 64-bit IEEE 754 double precision floating point value i...
Definition: util.cpp:143
Exception that provides a stack of explanations.
Definition: error.h:36
void saveHeader(WriteStream &dbf) const
Definition: foxpro.cpp:220
A simple scoped smart pointer template.
Utility functions for manipulating date and time.
void updateUpdate()
Definition: foxpro.cpp:914
Basic exceptions to throw.
utf8::iterator< std::string::const_iterator > iterator
Definition: ustring.h:50
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
void writeUint16BE(uint16 value)
Definition: writestream.h:116
uint8 _lastUpdateDay
Definition: foxpro.h:132
ISO-8859-15 (Latin-9).
Definition: encoding.h:47
uint16_t uint16
Definition: types.h:202
void saveFields(WriteStream &dbf) const
Definition: foxpro.cpp:249
size_t addFieldDate(const UString &name)
Definition: foxpro.cpp:629
uint16 readUint16BE()
Read an unsigned 16-bit word stored in big endian (MSB first) order from the stream and return it...
Definition: readstream.h:155
void trimRight()
Definition: ustring.cpp:410
size_t addFieldBool(const UString &name)
Definition: foxpro.cpp:603
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
Basic writing stream interfaces.
size_t addFieldNumber(const UString &name, uint8 size, uint8 decimals)
Definition: foxpro.cpp:551
bool hasMemo() const
Definition: foxpro.cpp:317
void setDate(size_t record, size_t field, uint16 year, uint8 month, uint8 day)
Definition: foxpro.cpp:828
size_t addFieldMemo(const UString &name)
Definition: foxpro.cpp:655
virtual size_t read(void *dataPtr, size_t dataSize)=0
Read data from the stream.
Simple memory based &#39;stream&#39;, which implements the ReadStream interface for a plain memory block...
Definition: memreadstream.h:66
Utility functions for working with differing string encodings.
void writeUint16LE(uint16 value)
Definition: writestream.h:98
StackException Exception
Definition: error.h:59
void loadFields(SeekableReadStream &dbf, uint32 recordSize)
Definition: foxpro.cpp:105
const Exception kReadError("Read error")
Exception when reading from a stream failed.
Definition: error.h:62
A database in FoxPro format.
virtual size_t write(const void *dataPtr, size_t dataSize)=0
Write data into the stream.
void writeByte(byte value)
Definition: writestream.h:88
virtual size_t size() const =0
Obtains the total size of the stream, measured in bytes.
std::vector< Record > _records
Definition: foxpro.h:138
void save(WriteStream *dbf, WriteStream *cdx=0, WriteStream *fpt=0) const
Definition: foxpro.cpp:193
Generic interface for a writable data stream.
Definition: writestream.h:64
void setMemo(size_t record, size_t field, SeekableReadStream *value=0)
Definition: foxpro.cpp:849
double getDouble(const Record &record, size_t field) const
Definition: foxpro.cpp:396
Seek from the current position of the stream.
Definition: readstream.h:270
void saveRecords(WriteStream &dbf) const
Definition: foxpro.cpp:280
PtrList< byte, DeallocatorArray > _pool
Definition: foxpro.h:140
Plain, unextended ASCII (7bit clean).
Definition: encoding.h:40
void setString(size_t record, size_t field, const UString &value)
Definition: foxpro.cpp:724
PointerType get() const
Returns the plain pointer value.
Definition: scopedptr.h:96
void getDate(uint16 &year, uint8 &month, uint8 &day) const
Return the year, month (1..12) and day (1..31) in the Gregorian calendar.
Definition: datetime.h:77
Type
A field type.
Definition: foxpro.h:44
static bool isASCII(uint32 c)
Is the character an ASCII character?
Definition: ustring.cpp:785
float convertIEEEFloat(uint32 data)
Convert a uint32 holding the bit pattern of a 32-bit IEEE 754 single precision floating point value i...
Definition: util.cpp:116
uint32_t uint32
Definition: types.h:204
uint16 _memoBlockSize
Definition: foxpro.h:142
uint16 _lastUpdateYear
Definition: foxpro.h:130
void loadHeader(SeekableReadStream &dbf, uint32 &recordSize, uint32 &recordCount, uint32 &firstRecordPos)
Definition: foxpro.cpp:74
SeekableReadStream * getMemo(const Record &record, size_t field) const
Definition: foxpro.cpp:459
Coordinated Universal Time (UTC).
Definition: datetime.h:39
const std::vector< Field > & getFields() const
Definition: foxpro.cpp:329
PtrVector< byte, DeallocatorArray > _memos
Definition: foxpro.h:143
UString getString(const Record &record, size_t field) const
Definition: foxpro.cpp:337
void getDate(const Record &record, size_t field, uint16 &year, uint8 &month, uint8 &day)
Definition: foxpro.cpp:434
size_t addFieldString(const UString &name, uint8 size)
Definition: foxpro.cpp:525
std::vector< Field > _fields
Definition: foxpro.h:137
void status(const char *s,...)
Definition: util.cpp:52
size_t getFieldCount() const
Definition: foxpro.cpp:321
A record.
Definition: foxpro.h:75
void getLastUpdate(uint16 &lastUpdateYear, uint8 &lastUpdateMonth, uint8 &lastUpdateDay) const
Definition: foxpro.cpp:307
UString readStringFixed(SeekableReadStream &stream, Encoding encoding, size_t length)
Read length bytes as a string with the given encoding out of a stream.
Definition: encoding.cpp:297
void writeUint32BE(uint32 value)
Definition: writestream.h:122
void load(SeekableReadStream *dbf, SeekableReadStream *cdx=0, SeekableReadStream *fpt=0)
Definition: foxpro.cpp:45
iterator end() const
Definition: ustring.cpp:257
std::vector< byte * > fields
Raw field data.
Definition: foxpro.h:77
bool hasIndex() const
Definition: foxpro.cpp:313
void setDouble(size_t record, size_t field, double value)
Definition: foxpro.cpp:793
uint8 _lastUpdateMonth
Definition: foxpro.h:131
bool _hasMemo
Definition: foxpro.h:135
uint32 autoIncNext
Definition: foxpro.h:70
void setBool(size_t record, size_t field, bool value)
Definition: foxpro.cpp:774
Interface for a seekable & readable data stream.
Definition: readstream.h:265
void writeUint32LE(uint32 value)
Definition: writestream.h:104
void addField(uint8 size)
Definition: foxpro.cpp:683
void setInt(size_t record, size_t field, int32 value)
Definition: foxpro.cpp:746
byte readByte()
Read an unsigned byte from the stream and return it.
Definition: readstream.h:92
uint8 byte
Definition: types.h:209
unsigned int uint
Definition: types.h:211
int32_t int32
Definition: types.h:203
void loadMemos(SeekableReadStream &fpt)
Definition: foxpro.cpp:175