1 (* Copyright (C) DooM 2D:Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE a_modes.inc}
20 interface
22 uses
23 Classes,
27 // ////////////////////////////////////////////////////////////////////////// //
28 type
38 // this is base type for all scalars (and arrays)
40 public
41 type
42 TType = (TBool, TChar, TByte, TUByte, TShort, TUShort, TInt, TUInt, TString, TPoint, TSize, TList, TTrigData);
43 // TPoint: pair of Integers
44 // TSize: pair of UShorts
45 // TList: actually, array of records
46 // TTrigData: array of mMaxDim bytes, but internally a record (mRecRef)
47 // arrays of chars are pascal shortstrings (with counter in the first byte)
49 private
50 type
53 private
74 mAsMonsterId: Boolean; // special hack for triggers: monster record number+1 in binary (so 0 means "none")
75 // default value
82 mEBSType: TObject; // either TDynRecord or TDynEBS; nil means "simple type"; nil for `TTrigData` too
84 // for binary parser
87 // for userdata
91 private
96 procedure parseDefaultValue (); // parse `mDefUnparsed` to `mDefSVal`, `mDefIVal`, `mDefIVal2`, `mDefRecRef`
108 protected
109 // returns `true` for duplicate record id
112 public
113 {
114 type
115 TListEnumerator = record
116 private
117 mList: TDynRecList;
118 mCurIdx: Integer;
119 public
120 constructor Create (alist: TDynRecList);
121 function MoveNext (): Boolean; inline;
122 function getCurrent (): TDynRecord; inline;
123 property Current: TDynRecord read getCurrent;
124 end;
125 }
127 public
145 // won't work for lists
152 public
171 property recrefIndex: Integer read getRecRefIndex; // search for this record in header; -1: not found
172 // for lists
175 property items[const aname: AnsiString]: TDynRecord read getListItem; default; // alas, FPC 3+ lost property overloading feature
176 // userdata
182 // "value" header record contains TList fields, with name equal to record type
184 private
191 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
193 {$ENDIF}
197 mHeaderRec: TDynRecord; // for "value" records this is header record with data, for "type" records this is header type record
199 // for userdata
203 private
218 protected
221 function addRecordByType (const atypename: AnsiString; rc: TDynRecord): Boolean; // `true`: duplicate record id
226 public
244 // find field with `TriggerType` type
247 // number of records of the given instance
254 public
259 //property fields: TDynFieldList read mFields;
270 // userdata
276 private
285 private
294 public
301 // return empty string if not found
304 public
313 public
318 private
326 public
337 // creates new header record
340 // creates new header record
343 public
350 {$IF DEFINED(D2D_DYNREC_PROFILER)}
352 {$ENDIF}
355 implementation
357 uses
358 SysUtils, e_log
362 // ////////////////////////////////////////////////////////////////////////// //
366 // ////////////////////////////////////////////////////////////////////////// //
367 {
368 constructor TDynField.TListEnumerator.Create (alist: TDynRecList);
369 begin
370 mList := alist;
371 mCurIdx := -1;
372 end;
375 function TDynField.TListEnumerator.MoveNext (): Boolean; inline;
376 begin
377 Inc(mCurIdx);
378 result := (mList <> nil) and (mCurIdx < mList.count);
379 end;
382 function TDynField.TListEnumerator.getCurrent (): TDynRecord; inline;
383 begin
384 result := mList[mCurIdx];
385 end;
386 }
390 begin
391 //result := TListEnumerator.Create(mRVal);
392 if (mRVal <> nil) then result := mRVal.GetEnumerator else result := TDynRecList.TEnumerator.Create(nil, 0);
396 // ////////////////////////////////////////////////////////////////////////// //
398 begin
406 begin
414 begin
421 begin
428 begin
465 var
467 begin
478 begin
510 begin
511 //FIXME: check type
517 // won't work for lists
519 begin
537 begin
547 var
549 begin
551 try
553 finally
560 var
566 begin
568 begin
573 end
574 else
575 begin
581 try
588 finally
600 // default value should be parsed
602 begin
605 begin
607 raise Exception.Create(Format('field ''%s'' in record ''%s'' of record type ''%s'' is not set', [mName, mOwner.mId, mOwner.mName]));
617 // default value should be parsed
619 begin
632 begin
638 begin
639 if (mRVal <> nil) and (idx >= 0) and (idx < mRVal.count) then result := mRVal[idx] else result := nil;
644 var
646 begin
652 begin
655 begin
663 begin
684 begin
693 TEBS.TBitSet: begin result += ' bitset '; if mBitSetUnique then result += 'unique '; result += mEBSTypeName; end;
698 begin
699 if (mType = TType.TPoint) then begin if (mAsT) then result += ' as txy' else result += ' as xy'; end
700 else if (mType = TType.TSize) then begin if (mAsT) then result += ' as twh' else result += ' as wh'; end;
708 begin
712 TType.TChar: if (mMaxDim > 0) then result += formatstrf('Char%d;', [mMaxDim]) else result += 'Char;';
736 var
754 begin
785 begin
787 if (lmaxdim < 1) then raise Exception.Create(Format('invalid field ''%s'' array size', [fldname]));
789 end;
792 begin
794 begin
795 if (fldofs >= 0) then raise Exception.Create(Format('duplicate field ''%s'' offset', [fldname]));
798 continue;
802 begin
809 continue;
810 end;
813 begin
815 if (Length(fldrecname) <> 0) then raise Exception.Create(Format('field ''%s'' already typed as ''%s''', [fldname, fldrecname]));
817 continue;
821 begin
823 if (Length(fldrecname) <> 0) then raise Exception.Create(Format('field ''%s'' already typed as ''%s''', [fldname, fldrecname]));
826 continue;
830 begin
831 if hasdefStr or hasdefInt or hasdefId then raise Exception.Create(Format('field ''%s'' has duplicate default', [fldname]));
834 begin
839 begin
844 begin
848 else
851 continue;
855 begin
857 continue;
861 begin
863 continue;
866 if (pr.tokType <> pr.TTId) then raise Exception.Create(Format('field ''%s'' has something unexpected in definition', [fldname]));
868 if (Length(fldrecname) <> 0) then raise Exception.Create(Format('field ''%s'' already typed as ''%s''', [fldname, fldrecname]));
875 // create field
890 else raise Exception.Create(Format('field ''%s'' has invalid type ''%s''', [fldname, fldtype]));
892 if (lmaxdim > 0) and (mType <> TType.TChar) and (mType <> TType.TTrigData) then raise Exception.Create(Format('field ''%s'' of type ''%s'' cannot be array', [fldname, fldtype]));
894 begin
895 if (lmaxdim < 1) then raise Exception.Create(Format('field ''%s'' of type ''%s'' cannot be array', [fldname, fldtype]));
896 if (Length(fldrecname) > 0) then raise Exception.Create(Format('field ''%s'' of type ''%s'' cannot have another type', [fldname, fldtype]));
916 end;
920 begin
927 var
933 begin
937 begin
939 begin
940 // this must be triggerdata
942 begin
943 raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
945 // write triggerdata
947 if (buf = nil) then raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
948 try
951 begin
956 finally
960 exit;
962 // record reference
970 else raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
972 // find record number
974 begin
976 if (f < 0) then raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' not found in record list', [mEBSTypeName, mName]));
978 if (f > maxv) then raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' has too big index', [mEBSTypeName, mName]));
979 end
980 else
981 begin
988 else raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
990 exit;
999 begin
1001 begin
1003 end
1004 else
1005 begin
1008 exit;
1011 begin
1012 if (mMaxDim = 0) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1014 begin
1015 if (Length(mSVal) <> 1) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1017 end
1018 else
1019 begin
1020 if (Length(mSVal) > mMaxDim) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1025 exit;
1029 begin
1030 // triggerdata array was processed earlier
1031 if (mMaxDim >= 0) then Exception.Create(Format('byte array in field ''%s'' cannot be written', [mName]));
1033 exit;
1037 begin
1038 if (mMaxDim >= 0) then raise Exception.Create(Format('short array in field ''%s'' cannot be written', [mName]));
1040 exit;
1044 begin
1045 if (mMaxDim >= 0) then raise Exception.Create(Format('int array in field ''%s'' cannot be written', [mName]));
1047 exit;
1050 begin
1054 begin
1055 if (mMaxDim >= 0) then raise Exception.Create(Format('pos/size array in field ''%s'' cannot be written', [mName]));
1058 exit;
1061 begin
1062 if (mMaxDim >= 0) then raise Exception.Create(Format('pos/size array in field ''%s'' cannot be written', [mName]));
1065 exit;
1068 begin
1070 exit;
1073 begin
1075 exit;
1083 var
1087 begin
1093 begin
1095 begin
1097 end
1099 begin
1101 end
1102 else
1103 begin
1107 exit;
1110 begin
1111 //def := mOwner.mOwner;
1112 //es := def.findEBSType(mEBSTypeName);
1115 if (es = nil) or (not es.mIsEnum) then raise Exception.Create(Format('record enum type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1117 begin
1119 begin
1122 exit;
1125 raise Exception.Create(Format('value %d in record enum type ''%s'' for field ''%s'' not found', [mIVal, mEBSTypeName, mName]));
1128 begin
1129 //def := mOwner.mOwner;
1130 //es := def.findEBSType(mEBSTypeName);
1133 if (es = nil) or es.mIsEnum then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1134 // none?
1136 begin
1138 begin
1140 begin
1143 exit;
1146 raise Exception.Create(Format('value %d in record bitset type ''%s'' for field ''%s'' not found', [0, mEBSTypeName, mName]));
1148 // not none
1152 begin
1154 begin
1157 begin
1159 begin
1163 break;
1166 if not found then raise Exception.Create(Format('value %d in record bitset type ''%s'' for field ''%s'' not found', [mask, mEBSTypeName, mName]));
1171 exit;
1178 begin
1180 exit;
1183 begin
1184 if (mMaxDim = 0) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1187 exit;
1195 begin
1197 exit;
1200 begin
1203 exit;
1207 begin
1209 exit;
1212 begin
1214 exit;
1217 begin
1219 exit;
1228 var
1235 begin
1239 begin
1240 // this must be triggerdata
1242 begin
1245 // find trigger definition
1247 if (tfld = nil) then raise Exception.Create(Format('triggerdata value for field ''%s'' in record ''%s'' without TriggerType field', [mName, rec.mName]));
1249 if (rc = nil) then raise Exception.Create(Format('triggerdata definition for field ''%s'' in record ''%s'' with type ''%s'' not found', [mName, rec.mName, tfld.mSVal]));
1252 try
1256 finally
1260 exit;
1261 end
1262 else
1263 begin
1264 // not a trigger data
1272 else raise Exception.Create(Format('invalid non-numeric type ''%s'' for field ''%s'' of record ''%s''', [getTypeName(mType), mName, mEBSTypeName]));
1278 exit;
1282 begin
1291 else raise Exception.Create(Format('invalid non-numeric type ''%s'' for field ''%s'' of record ''%s''', [getTypeName(mType), mName, mEBSTypeName]));
1295 if (es = nil) or (es.mIsEnum <> (mEBS = TEBS.TEnum)) then raise Exception.Create(Format('record enum type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1297 // build enum/bitfield values
1299 begin
1301 if (Length(mSVal) = 0) then raise Exception.Create(Format('record enum type ''%s'' for field ''%s'' has invalid value %d', [mEBSTypeName, mName, mIVal]));
1302 end
1303 else
1304 begin
1305 // special for 'none'
1307 begin
1309 if (Length(mSVal) = 0) then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' has invalid value %d', [mEBSTypeName, mName, mIVal]));
1310 end
1311 else
1312 begin
1316 begin
1318 begin
1320 if (Length(s) = 0) then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' has invalid value %d', [mEBSTypeName, mName, mask]));
1328 //writeln('ebs <', es.mName, '>: ', mSVal);
1330 exit;
1337 begin
1343 exit;
1346 begin
1348 begin
1350 end
1351 else
1352 begin
1355 try
1360 begin
1365 finally
1370 exit;
1379 begin
1381 exit;
1384 begin
1388 exit;
1391 begin
1395 exit;
1398 begin
1400 exit;
1403 begin
1405 exit;
1416 begin
1418 if (mIVal < min) or (mIVal > max) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
1422 var
1428 begin
1429 // if this field should contain struct, convert type and parse struct
1433 begin
1434 // ugly hack. sorry.
1436 begin
1439 begin
1440 // '{}'
1443 end
1444 else
1445 begin
1447 // find trigger definition
1449 if (tfld = nil) then raise Exception.Create(Format('triggerdata value for field ''%s'' in record ''%s'' without ''type'' field', [mName, rec.mName]));
1451 if (rc = nil) then raise Exception.Create(Format('triggerdata definition for field ''%s'' in record ''%s'' with type ''%s'' not found', [mName, rec.mName, tfld.mSVal]));
1454 //writeln(rc.definition);
1455 try
1459 finally
1465 exit;
1467 // other record types
1469 begin
1471 begin
1473 end
1474 else
1475 begin
1477 if (rec = nil) then raise Exception.Create(Format('record ''%s'' (%s) value for field ''%s'' not found', [pr.tokStr, mEBSTypeName, mName]));
1483 exit;
1484 end
1486 begin
1487 //rec := mOwner.mOwner.findRecType(mEBSTypeName); // find in mapdef
1490 if (rec = nil) then raise Exception.Create(Format('record type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1497 begin
1498 //raise Exception.Create(Format('record type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1499 e_LogWritefln('duplicate record with id ''%s'' for field ''%s'' in record ''%s''', [rc.mId, mName, mOwner.mName]);
1502 exit;
1507 begin
1508 //es := mOwner.mOwner.findEBSType(mEBSTypeName); // find in mapdef
1511 if (es = nil) or (not es.mIsEnum) then raise Exception.Create(Format('record enum type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1513 if not es.has[tk] then raise Exception.Create(Format('record enum value ''%s'' of type ''%s'' for field ''%s'' not found', [tk, mEBSTypeName, mName]));
1516 //writeln('ENUM ', mEBSName, '; element <', mSVal, '> with value ', mIVal);
1519 exit;
1522 begin
1523 //es := mOwner.mOwner.findEBSType(mEBSTypeName); // find in mapdef
1526 if (es = nil) or es.mIsEnum then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1529 begin
1531 if not es.has[tk] then raise Exception.Create(Format('record bitset value ''%s'' of type ''%s'' for field ''%s'' not found', [tk, mEBSTypeName, mName]));
1535 if mBitSetUnique then raise Exception.Create(Format('record bitset of type ''%s'' for field ''%s'' expects only one value', [tk, mEBSTypeName, mName]));
1536 //pr.expectDelim('|');
1541 exit;
1548 begin
1554 exit;
1557 begin
1558 if (mMaxDim = 0) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1561 begin
1562 // single char
1563 if (Length(mSVal) <> 1) then raise Exception.Create(Format('invalid string size for field ''%s''', [mName]));
1566 end
1567 else
1568 begin
1569 // string
1570 if (Length(mSVal) > mMaxDim) then raise Exception.Create(Format('invalid string size for field ''%s''', [mName]));
1574 exit;
1577 begin
1580 exit;
1583 begin
1586 exit;
1589 begin
1592 exit;
1595 begin
1598 exit;
1601 begin
1604 exit;
1607 begin
1610 exit;
1613 begin
1617 exit;
1621 begin
1625 begin
1626 if (mIVal < 0) or (mIVal > 32767) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
1630 begin
1631 if (mIVal2 < 0) or (mIVal2 > 32767) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
1636 exit;
1639 begin
1641 exit;
1644 begin
1646 exit;
1654 // ////////////////////////////////////////////////////////////////////////// //
1656 begin
1657 if (pr = nil) then raise Exception.Create('cannot create record type without type definition');
1662 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1664 {$ENDIF}
1676 begin
1680 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1682 {$ENDIF}
1692 begin
1696 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1699 {$ENDIF}
1709 begin
1712 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1714 {$ENDIF}
1718 function TDynRecord.addFieldChecked (fld: TDynField): Boolean; inline; // `true`: duplicate name
1719 begin
1722 {$IF not DEFINED(XDYNREC_USE_FIELDHASH)}
1724 {$ENDIF}
1726 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1728 {$ENDIF}
1733 begin
1734 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1736 {$ELSE}
1739 begin
1744 {$ENDIF}
1749 begin
1755 var
1757 begin
1764 begin
1770 begin
1776 begin
1782 var
1784 begin
1792 begin
1798 begin
1804 var
1807 begin
1815 begin
1830 var
1833 begin
1836 // find record data
1839 if (fld.mType <> fld.TType.TList) then raise Exception.Create(Format('cannot get record of type ''%s'' due to name conflict with ordinary field', [atypename]));
1840 // find by id
1842 begin
1845 // alas
1849 function TDynRecord.findRecordNumByType (const atypename: AnsiString; rc: TDynRecord): Integer;
1850 var
1853 begin
1855 // find record data
1858 if (fld.mType <> fld.TType.TList) then raise Exception.Create(Format('cannot get record of type ''%s'' due to name conflict with ordinary field', [atypename]));
1859 // find by ref
1861 begin
1863 begin
1867 // alas
1872 var
1874 begin
1875 // find record data
1878 begin
1879 // first record
1884 if (fld.mType <> fld.TType.TList) then raise Exception.Create(Format('cannot append record of type ''%s'' due to name conflict with ordinary field', [atypename]));
1885 // append
1887 begin
1896 var
1898 begin
1904 begin
1912 var
1915 begin
1917 begin
1928 // number of records of the given instance
1930 var
1932 begin
1940 var
1942 begin
1946 begin
1948 begin
1951 end
1952 else
1953 begin
1965 var
1967 begin
1971 begin
1973 begin
1976 end
1977 else
1978 begin
1990 var
1992 begin
1996 begin
1998 begin
2001 end
2002 else
2003 begin
2015 var
2018 begin
2020 begin
2023 begin
2025 begin
2029 if isForTrig[tdn] then raise Exception.Create(Format('duplicate trigdata ''%s'' trigtype ''%s''', [mName, tdn]));
2033 end
2034 else
2035 begin
2041 end
2042 else
2043 begin
2048 begin
2051 begin
2052 if (mSize > 0) then raise Exception.Create(Format('duplicate `size` in record ''%s''', [mName]));
2054 if (mSize < 1) then raise Exception.Create(Format('invalid record ''%s'' size: %d', [mName, mSize]));
2056 continue;
2059 begin
2060 if (mBinBlock >= 0) then raise Exception.Create(Format('duplicate `binblock` in record ''%s''', [mName]));
2062 if (mBinBlock < 1) then raise Exception.Create(Format('invalid record ''%s'' binblock: %d', [mName, mBinBlock]));
2063 continue;
2069 // load fields
2071 begin
2073 //if hasByName(fld.name) then begin fld.Free(); raise Exception.Create(Format('duplicate field ''%s''', [fld.name])); end;
2074 // append
2077 begin
2081 // done with field
2088 var
2090 begin
2092 begin
2095 end
2096 else
2097 begin
2098 // record
2102 begin
2112 var
2114 begin
2116 begin
2117 // trigger data
2120 begin
2123 begin
2128 end
2129 else
2130 begin
2133 end
2134 else
2135 begin
2136 // record
2143 begin
2153 var
2165 var
2168 begin
2169 //writeln('*** rec: ', rec.mName, '.', rec.mId, ' (', rec.mFields.count, ')');
2171 begin
2173 begin
2175 continue;
2181 begin
2182 e_LogWritefln('record of type ''%s'' with id ''%s'' links to inexistant record of type ''%s'' with id ''%s''', [rec.mName, rec.mId, fld.mEBSTypeName, fld.mRecRefId], MSG_WARNING);
2183 //raise Exception.Create(Format('record of type ''%s'' with id ''%s'' links to inexistant record of type ''%s'' with id ''%s''', [rec.mName, rec.mId, fld.mEBSTypeName, fld.mRecRefId]));
2185 //writeln(' ', rec.mName, '.', rec.mId, ':', fld.mName, ' -> ', rt.mName, '.', rt.mId, ' (', fld.mEBSTypeName, '.', fld.mRecRefId, ')');
2191 begin
2192 //writeln(' ', fld.mName);
2197 begin
2200 try
2202 begin
2203 // parse map file as sequence of blocks
2207 // parse blocks
2209 begin
2215 if (bsize < 0) or (bsize > $1fffffff) then raise Exception.Create(Format('block of type %d has invalid size %d', [btype, bsize]));
2216 if loaded[btype] then raise Exception.Create(Format('block of type %d already loaded', [btype]));
2218 // find record type for this block
2221 if (rect = nil) then raise Exception.Create(Format('block of type %d has no corresponding record', [btype]));
2222 //writeln('found type ''', rec.mName, ''' for block type ', btype);
2223 if (rec.mSize = 0) or ((bsize mod rec.mSize) <> 0) then raise Exception.Create(Format('block of type %d has invalid number of records', [btype]));
2224 // header?
2226 begin
2227 if (bsize <> mSize) then raise Exception.Create(Format('header block of type %d has invalid number of records', [btype]));
2232 end
2233 else
2234 begin
2235 // create list for this type
2240 begin
2244 begin
2251 //writeln('parsed ''', rec.mId, '''...');
2257 //st.position := st.position+bsize;
2259 // link fields
2261 begin
2265 exit;
2268 // read fields
2270 if (mSize < 1) then raise Exception.Create(Format('cannot read record of type ''%s'' with unknown size', [mName]));
2274 begin
2277 if (fld.mBinOfs >= st.size) then raise Exception.Create(Format('record of type ''%s'' has invalid field ''%s''', [fld.mName]));
2279 //writeln('parsing ''', mName, '.', fld.mName, '''...');
2282 finally
2289 procedure TDynRecord.writeBinTo (st: TStream; trigbufsz: Integer=-1; onlyFields: Boolean=false);
2290 var
2296 //f, c: Integer;
2299 begin
2301 begin
2302 if (mBinBlock < 1) then raise Exception.Create('cannot write binary record without block number');
2305 end
2306 else
2307 begin
2310 try
2315 // write normal fields
2317 begin
2318 // record list?
2322 if (fld.mBinOfs >= bufsz) then raise Exception.Create('binary value offset is outside of the buffer');
2324 //writeln('writing field <', fld.mName, '>');
2328 // write block with normal fields
2330 begin
2331 //writeln('writing header...');
2332 // signature and version
2343 // write other blocks, if any
2345 begin
2346 // calculate blkmax
2349 begin
2350 // record list?
2352 begin
2360 // write blocks
2362 begin
2366 begin
2367 // record list?
2369 begin
2378 // flush block
2380 begin
2391 // write end marker
2396 finally
2404 var
2407 begin
2409 begin
2416 try
2418 begin
2419 // record list?
2421 begin
2424 begin
2426 begin
2432 continue;
2439 finally
2447 {$IF DEFINED(D2D_DYNREC_PROFILER)}
2448 var
2459 begin
2461 writeln('record cloning: ', profCloneRec div 1000, '.', profCloneRec mod 1000, ' milliseconds');
2462 writeln('findRecType : ', profFindRecType div 1000, '.', profFindRecType mod 1000, ' milliseconds');
2463 writeln('field[] : ', profFieldSearching div 1000, '.', profFieldSearching mod 1000, ' milliseconds');
2464 writeln('list dup check: ', profListDupChecking div 1000, '.', profListDupChecking mod 1000, ' milliseconds');
2465 writeln('addRecByType : ', profAddRecByType div 1000, '.', profAddRecByType mod 1000, ' milliseconds');
2466 writeln('field valparse: ', profFieldValParsing div 1000, '.', profFieldValParsing mod 1000, ' milliseconds');
2467 writeln('fix defaults : ', profFixDefaults div 1000, '.', profFixDefaults mod 1000, ' milliseconds');
2468 writeln('recvalparse : ', profRecValParse div 1000, '.', profRecValParse mod 1000, ' milliseconds');
2470 {$ENDIF}
2474 var
2478 {$IF DEFINED(D2D_DYNREC_PROFILER)}
2480 {$ENDIF}
2481 begin
2482 if (mOwner = nil) then raise Exception.Create(Format('can''t parse record ''%s'' value without owner', [mName]));
2486 // not a header?
2488 begin
2489 // id?
2491 end
2492 else
2493 begin
2497 //writeln('parsing record <', mName, '>');
2500 begin
2502 //writeln('<', mName, '.', pr.tokStr, '>');
2504 // records
2506 begin
2507 // add records with this type (if any)
2512 begin
2517 try
2520 (*
2521 if (Length(rec.mId) > 0) then
2522 begin
2523 {$IF DEFINED(D2D_DYNREC_PROFILER)}stt := curTimeMicro();{$ENDIF}
2524 fld := field[pr.tokStr];
2525 {$IF DEFINED(D2D_DYNREC_PROFILER)}profFieldSearching := curTimeMicro()-stt;{$ENDIF}
2526 (*
2527 if (fld <> nil) and (fld.mRVal <> nil) then
2528 begin
2529 {$IF DEFINED(D2D_DYNREC_PROFILER)}stt := curTimeMicro();{$ENDIF}
2530 //idtmp := trc.mName+':'+rec.mId;
2531 //if ids.put(idtmp, 1) then raise Exception.Create(Format('duplicate thing ''%s'' in record ''%s''', [fld.mName, mName]));
2532 if fld.mRHash.has(rec.mId) then raise Exception.Create(Format('duplicate thing ''%s'' in record ''%s''', [fld.mName, mName]));
2533 {$IF DEFINED(D2D_DYNREC_PROFILER)}profListDupChecking := curTimeMicro()-stt;{$ENDIF}
2534 end;
2535 end;
2536 *)
2541 finally
2544 continue;
2548 // fields
2553 begin
2554 if fld.defined then raise Exception.Create(Format('duplicate field ''%s'' in record ''%s''', [fld.mName, mName]));
2555 if fld.internal then raise Exception.Create(Format('internal field ''%s'' in record ''%s''', [fld.mName, mName]));
2560 continue;
2563 // something is wrong
2567 // fix field defaults
2571 //writeln('done parsing record <', mName, '>');
2572 //{$IF DEFINED(D2D_DYNREC_PROFILER)}writeln('stall: ', curTimeMicro()-stall);{$ENDIF}
2577 // ////////////////////////////////////////////////////////////////////////// //
2579 begin
2586 begin
2593 begin
2604 begin
2607 begin
2616 begin
2622 var
2624 begin
2631 var
2633 begin
2637 // fields
2640 begin
2644 begin
2648 end
2649 else
2650 begin
2655 // max field
2662 var
2664 begin
2666 // fields
2668 begin
2675 var
2677 begin
2679 begin
2687 var
2693 begin
2702 begin
2705 begin
2706 if StrEqu(mIds[f], idname) then raise Exception.Create(Format('duplicate field ''%s'' in enum/bitset ''%s''', [idname, mName]));
2708 if StrEqu(mMaxName, idname) then raise Exception.Create(Format('duplicate field ''%s'' in enum/bitset ''%s''', [idname, mName]));
2712 // has value?
2714 begin
2716 begin
2717 if (Length(mMaxName) > 0) then raise Exception.Create(Format('duplicate max field ''%s'' in enum/bitset ''%s''', [idname, mName]));
2720 end
2721 else
2722 begin
2728 // append it?
2730 begin
2731 // fix maxvalue
2733 begin
2740 // next cv
2742 begin
2751 // add max field
2753 begin
2762 // ////////////////////////////////////////////////////////////////////////// //
2764 begin
2773 var
2776 begin
2791 begin
2798 var
2800 begin
2802 begin
2810 var
2812 begin
2814 begin
2822 var
2824 begin
2826 begin
2834 var
2839 // setup header links and type links
2841 var
2843 begin
2846 begin
2851 begin
2853 if (fld.mEBSType = nil) then raise Exception.Create(Format('field ''%s'' of type ''%s'' has no correcponding record definition', [fld.mName, fld.mEBSTypeName]));
2857 begin
2859 if (fld.mEBSType = nil) then raise Exception.Create(Format('field ''%s'' of type ''%s'' has no correcponding enum/bitset', [fld.mName, fld.mEBSTypeName]));
2860 if ((fld.mEBS = TDynField.TEBS.TEnum) <> (fld.mEBSType as TDynEBS).mIsEnum) then raise Exception.Create(Format('field ''%s'' of type ''%s'' enum/bitset type conflict', [fld.mName, fld.mEBSTypeName]));
2866 // setup default values
2868 var
2870 begin
2874 begin
2877 begin
2882 begin
2885 begin
2891 //writeln(eb.definition); writeln;
2892 continue;
2896 begin
2899 begin
2901 begin
2908 //writeln(dr.definition); writeln;
2909 continue;
2913 //writeln(dr.definition); writeln;
2914 if (findRecType(rec.name) <> nil) then begin rec.Free(); raise Exception.Create(Format('duplicate record ''%s''', [rec.name])); end;
2915 if (hdr <> nil) and StrEqu(rec.name, hdr.name) then begin rec.Free(); raise Exception.Create(Format('duplicate record ''%s''', [rec.name])); end;
2918 begin
2919 if (hdr <> nil) then begin rec.Free(); raise Exception.Create(Format('duplicate header record ''%s'' (previous is ''%s'')', [rec.name, hdr.name])); end;
2921 end
2922 else
2923 begin
2928 // put header record to top
2934 // setup header links and type links
2938 // setup default values
2944 // ////////////////////////////////////////////////////////////////////////// //
2946 var
2948 begin
2950 try
2957 finally
2964 var
2966 begin
2968 try
2974 finally
2981 var
2987 begin
2989 result += '// ////////////////////////////////////////////////////////////////////////// //'#10;
2992 result += #10#10'// ////////////////////////////////////////////////////////////////////////// //'#10;
2995 begin
3000 result += #10#10'// ////////////////////////////////////////////////////////////////////////// //'#10;
3006 begin
3010 begin
3016 begin
3028 var
3030 begin
3032 result += '// ////////////////////////////////////////////////////////////////////////// //'#10;
3038 function TDynMapDef.getTrigTypeCount (): Integer; inline; begin result := trigTypes.count; end;
3039 function TDynMapDef.getTrigTypeAt (idx: Integer): TDynRecord; inline; begin if (idx >= 0) and (idx < trigTypes.count) then result := trigTypes[idx] else result := nil; end;