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
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`
111 protected
112 // returns `true` for duplicate record id
115 public
134 // won't work for lists
141 public
160 property recrefIndex: Integer read getRecRefIndex; // search for this record in header; -1: not found
161 // for lists
164 property items[const aname: AnsiString]: TDynRecord read getListItem; default; // alas, FPC 3+ lost property overloading feature
165 // userdata
168 //
173 // "value" header record contains TList fields, with name equal to record type
175 private
182 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
184 {$ENDIF}
188 mHeaderRec: TDynRecord; // for "value" records this is header record with data, for "type" records this is header type record
190 // for userdata
194 private
209 protected
212 function addRecordByType (const atypename: AnsiString; rc: TDynRecord): Boolean; // `true`: duplicate record id
217 public
235 // find field with `TriggerType` type
238 // number of records of the given instance
241 //procedure setUserField (const fldname: AnsiString; v: LongInt);
242 //procedure setUserField (const fldname: AnsiString; v: AnsiString);
243 //procedure setUserField (const fldname: AnsiString; v: Boolean);
248 public
253 //property fields: TDynFieldList read mFields;
264 // userdata
267 // userfields
272 private
281 private
290 public
297 // return empty string if not found
300 public
309 public
314 private
322 public
333 // creates new header record
336 // creates new header record
339 public
346 {$IF DEFINED(D2D_DYNREC_PROFILER)}
348 {$ENDIF}
351 implementation
353 uses
354 SysUtils, e_log
358 // ////////////////////////////////////////////////////////////////////////// //
362 // ////////////////////////////////////////////////////////////////////////// //
364 begin
365 //result := TListEnumerator.Create(mRVal);
366 if (mRVal <> nil) then result := mRVal.GetEnumerator else result := TDynRecList.TEnumerator.Create(nil, 0);
370 // ////////////////////////////////////////////////////////////////////////// //
372 begin
380 begin
388 begin
396 begin
420 else
424 begin
461 begin
468 begin
505 var
507 begin
518 begin
550 begin
551 //FIXME: check type
558 begin
581 begin
605 else
609 begin
620 varString:
622 begin
624 end
625 else
626 begin
630 varBoolean:
642 else
647 varByte,
648 varWord,
649 varShortInt,
650 varSmallint,
651 varInteger:
653 varInt64:
656 else
658 varLongWord:
659 if (val > LongWord($7FFFFFFF)) then raise Exception.Create('cannot convert longword variant to field value')
669 // won't work for lists
671 begin
689 begin
699 var
701 begin
703 try
705 finally
712 var
718 begin
720 begin
725 end
726 else
727 begin
733 try
740 finally
752 // default value should be parsed
754 begin
757 begin
759 raise Exception.Create(Format('field ''%s'' in record ''%s'' of record type ''%s'' is not set', [mName, mOwner.mId, mOwner.mName]));
769 // default value should be parsed
771 begin
784 begin
790 begin
791 if (mRVal <> nil) and (idx >= 0) and (idx < mRVal.count) then result := mRVal[idx] else result := nil;
796 var
798 begin
804 begin
807 begin
815 begin
836 begin
845 TEBS.TBitSet: begin result += ' bitset '; if mBitSetUnique then result += 'unique '; result += mEBSTypeName; end;
850 begin
851 if (mType = TType.TPoint) then begin if (mAsT) then result += ' as txy' else result += ' as xy'; end
852 else if (mType = TType.TSize) then begin if (mAsT) then result += ' as twh' else result += ' as wh'; end;
860 begin
864 TType.TChar: if (mMaxDim > 0) then result += formatstrf('Char%d;', [mMaxDim]) else result += 'Char;';
888 var
907 begin
939 begin
941 if (lmaxdim < 1) then raise Exception.Create(Format('invalid field ''%s'' array size', [fldname]));
943 end;
946 begin
948 begin
949 if (fldofs >= 0) then raise Exception.Create(Format('duplicate field ''%s'' offset', [fldname]));
952 continue;
956 begin
963 continue;
964 end;
967 begin
969 if (Length(fldrecname) <> 0) then raise Exception.Create(Format('field ''%s'' already typed as ''%s''', [fldname, fldrecname]));
971 continue;
975 begin
977 if (Length(fldrecname) <> 0) then raise Exception.Create(Format('field ''%s'' already typed as ''%s''', [fldname, fldrecname]));
980 continue;
984 begin
985 if hasdefStr or hasdefInt or hasdefId then raise Exception.Create(Format('field ''%s'' has duplicate default', [fldname]));
988 begin
993 begin
998 begin
1003 begin
1010 else
1013 continue;
1017 begin
1019 continue;
1023 begin
1025 continue;
1028 if (pr.tokType <> pr.TTId) then raise Exception.Create(Format('field ''%s'' has something unexpected in definition', [fldname]));
1030 if (Length(fldrecname) <> 0) then raise Exception.Create(Format('field ''%s'' already typed as ''%s''', [fldname, fldrecname]));
1037 // create field
1052 else raise Exception.Create(Format('field ''%s'' has invalid type ''%s''', [fldname, fldtype]));
1054 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]));
1056 begin
1057 if (lmaxdim < 1) then raise Exception.Create(Format('field ''%s'' of type ''%s'' cannot be array', [fldname, fldtype]));
1058 if (Length(fldrecname) > 0) then raise Exception.Create(Format('field ''%s'' of type ''%s'' cannot have another type', [fldname, fldtype]));
1065 begin
1083 end;
1087 begin
1094 var
1100 begin
1104 begin
1106 begin
1107 // this must be triggerdata
1109 begin
1110 raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
1112 // write triggerdata
1114 if (buf = nil) then raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
1115 try
1118 begin
1123 finally
1127 exit;
1129 // record reference
1137 else raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
1139 // find record number
1141 begin
1143 if (f < 0) then raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' not found in record list', [mEBSTypeName, mName]));
1145 if (f > maxv) then raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' has too big index', [mEBSTypeName, mName]));
1146 end
1147 else
1148 begin
1155 else raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
1157 exit;
1166 begin
1168 begin
1170 end
1171 else
1172 begin
1175 exit;
1178 begin
1179 if (mMaxDim = 0) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1181 begin
1182 if (Length(mSVal) <> 1) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1184 end
1185 else
1186 begin
1187 if (Length(mSVal) > mMaxDim) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1192 exit;
1196 begin
1197 // triggerdata array was processed earlier
1198 if (mMaxDim >= 0) then Exception.Create(Format('byte array in field ''%s'' cannot be written', [mName]));
1200 exit;
1204 begin
1205 if (mMaxDim >= 0) then raise Exception.Create(Format('short array in field ''%s'' cannot be written', [mName]));
1207 exit;
1211 begin
1212 if (mMaxDim >= 0) then raise Exception.Create(Format('int array in field ''%s'' cannot be written', [mName]));
1214 exit;
1217 begin
1221 begin
1222 if (mMaxDim >= 0) then raise Exception.Create(Format('pos/size array in field ''%s'' cannot be written', [mName]));
1225 exit;
1228 begin
1229 if (mMaxDim >= 0) then raise Exception.Create(Format('pos/size array in field ''%s'' cannot be written', [mName]));
1232 exit;
1235 begin
1237 exit;
1240 begin
1242 exit;
1250 var
1254 begin
1260 begin
1262 begin
1264 end
1266 begin
1268 end
1269 else
1270 begin
1274 exit;
1277 begin
1278 //def := mOwner.mOwner;
1279 //es := def.findEBSType(mEBSTypeName);
1282 if (es = nil) or (not es.mIsEnum) then raise Exception.Create(Format('record enum type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1284 begin
1286 begin
1289 exit;
1292 raise Exception.Create(Format('value %d in record enum type ''%s'' for field ''%s'' not found', [mIVal, mEBSTypeName, mName]));
1295 begin
1296 //def := mOwner.mOwner;
1297 //es := def.findEBSType(mEBSTypeName);
1300 if (es = nil) or es.mIsEnum then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1301 // none?
1303 begin
1305 begin
1307 begin
1310 exit;
1313 raise Exception.Create(Format('value %d in record bitset type ''%s'' for field ''%s'' not found', [0, mEBSTypeName, mName]));
1315 // not none
1319 begin
1321 begin
1324 begin
1326 begin
1330 break;
1333 if not found then raise Exception.Create(Format('value %d in record bitset type ''%s'' for field ''%s'' not found', [mask, mEBSTypeName, mName]));
1338 exit;
1345 begin
1347 exit;
1350 begin
1351 if (mMaxDim = 0) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1354 exit;
1362 begin
1364 exit;
1367 begin
1370 exit;
1374 begin
1376 exit;
1379 begin
1381 exit;
1384 begin
1386 exit;
1395 var
1402 begin
1406 begin
1407 // this must be triggerdata
1409 begin
1412 // find trigger definition
1414 if (tfld = nil) then raise Exception.Create(Format('triggerdata value for field ''%s'' in record ''%s'' without TriggerType field', [mName, rec.mName]));
1416 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]));
1419 try
1423 finally
1427 exit;
1428 end
1429 else
1430 begin
1431 // not a trigger data
1439 else raise Exception.Create(Format('invalid non-numeric type ''%s'' for field ''%s'' of record ''%s''', [getTypeName(mType), mName, mEBSTypeName]));
1445 exit;
1449 begin
1458 else raise Exception.Create(Format('invalid non-numeric type ''%s'' for field ''%s'' of record ''%s''', [getTypeName(mType), mName, mEBSTypeName]));
1462 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]));
1464 // build enum/bitfield values
1466 begin
1468 if (Length(mSVal) = 0) then raise Exception.Create(Format('record enum type ''%s'' for field ''%s'' has invalid value %d', [mEBSTypeName, mName, mIVal]));
1469 end
1470 else
1471 begin
1472 // special for 'none'
1474 begin
1476 if (Length(mSVal) = 0) then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' has invalid value %d', [mEBSTypeName, mName, mIVal]));
1477 end
1478 else
1479 begin
1483 begin
1485 begin
1487 if (Length(s) = 0) then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' has invalid value %d', [mEBSTypeName, mName, mask]));
1495 //writeln('ebs <', es.mName, '>: ', mSVal);
1497 exit;
1504 begin
1510 exit;
1513 begin
1515 begin
1517 end
1518 else
1519 begin
1522 try
1527 begin
1532 finally
1537 exit;
1546 begin
1548 exit;
1551 begin
1555 exit;
1558 begin
1562 exit;
1565 begin
1567 exit;
1570 begin
1572 exit;
1583 begin
1585 if (mIVal < min) or (mIVal > max) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
1589 var
1595 begin
1596 // if this field should contain struct, convert type and parse struct
1600 begin
1601 // ugly hack. sorry.
1603 begin
1606 begin
1607 // '{}'
1610 end
1611 else
1612 begin
1614 // find trigger definition
1616 if (tfld = nil) then raise Exception.Create(Format('triggerdata value for field ''%s'' in record ''%s'' without ''type'' field', [mName, rec.mName]));
1618 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]));
1621 //writeln(rc.definition);
1622 try
1626 finally
1632 exit;
1634 // other record types
1636 begin
1638 begin
1640 end
1641 else
1642 begin
1644 if (rec = nil) then raise Exception.Create(Format('record ''%s'' (%s) value for field ''%s'' not found', [pr.tokStr, mEBSTypeName, mName]));
1650 exit;
1651 end
1653 begin
1654 //rec := mOwner.mOwner.findRecType(mEBSTypeName); // find in mapdef
1657 if (rec = nil) then raise Exception.Create(Format('record type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1664 begin
1665 //raise Exception.Create(Format('record type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1666 e_LogWritefln('duplicate record with id ''%s'' for field ''%s'' in record ''%s''', [rc.mId, mName, mOwner.mName]);
1669 exit;
1674 begin
1675 //es := mOwner.mOwner.findEBSType(mEBSTypeName); // find in mapdef
1678 if (es = nil) or (not es.mIsEnum) then raise Exception.Create(Format('record enum type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1680 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]));
1683 //writeln('ENUM ', mEBSName, '; element <', mSVal, '> with value ', mIVal);
1686 exit;
1689 begin
1690 //es := mOwner.mOwner.findEBSType(mEBSTypeName); // find in mapdef
1693 if (es = nil) or es.mIsEnum then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1696 begin
1698 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]));
1702 if mBitSetUnique then raise Exception.Create(Format('record bitset of type ''%s'' for field ''%s'' expects only one value', [tk, mEBSTypeName, mName]));
1703 //pr.expectDelim('|');
1708 exit;
1715 begin
1721 exit;
1724 begin
1725 if (mMaxDim = 0) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1728 begin
1729 // single char
1730 if (Length(mSVal) <> 1) then raise Exception.Create(Format('invalid string size for field ''%s''', [mName]));
1733 end
1734 else
1735 begin
1736 // string
1737 if (Length(mSVal) > mMaxDim) then raise Exception.Create(Format('invalid string size for field ''%s''', [mName]));
1741 exit;
1744 begin
1747 exit;
1750 begin
1753 exit;
1756 begin
1759 exit;
1762 begin
1765 exit;
1768 begin
1771 exit;
1774 begin
1777 exit;
1780 begin
1784 exit;
1788 begin
1792 begin
1793 if (mIVal < 0) or (mIVal > 32767) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
1797 begin
1798 if (mIVal2 < 0) or (mIVal2 > 32767) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
1803 exit;
1806 begin
1808 exit;
1811 begin
1813 exit;
1821 // ////////////////////////////////////////////////////////////////////////// //
1823 begin
1824 if (pr = nil) then raise Exception.Create('cannot create record type without type definition');
1829 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1831 {$ENDIF}
1843 begin
1847 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1849 {$ENDIF}
1859 begin
1863 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1866 {$ENDIF}
1876 begin
1879 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1881 {$ENDIF}
1885 function TDynRecord.addFieldChecked (fld: TDynField): Boolean; inline; // `true`: duplicate name
1886 begin
1889 {$IF not DEFINED(XDYNREC_USE_FIELDHASH)}
1891 {$ENDIF}
1893 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1895 {$ENDIF}
1900 begin
1901 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1903 {$ELSE}
1906 begin
1911 {$ENDIF}
1916 begin
1922 var
1924 begin
1931 begin
1937 begin
1943 begin
1949 var
1951 begin
1959 begin
1965 begin
1971 var
1974 begin
1982 begin
1997 var
2000 begin
2003 // find record data
2006 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]));
2007 // find by id
2009 begin
2012 // alas
2016 function TDynRecord.findRecordNumByType (const atypename: AnsiString; rc: TDynRecord): Integer;
2017 var
2020 begin
2022 // find record data
2025 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]));
2026 // find by ref
2028 begin
2030 begin
2034 // alas
2039 var
2041 begin
2042 // find record data
2045 begin
2046 // first record
2051 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]));
2052 // append
2054 begin
2063 var
2065 begin
2071 begin
2079 var
2082 begin
2084 begin
2095 // number of records of the given instance
2097 var
2099 begin
2107 var
2109 begin
2116 var
2118 begin
2121 begin
2127 end
2128 else
2129 begin
2135 {
2136 procedure TDynRecord.setUserField (const fldname: AnsiString; v: LongInt);
2137 var
2138 fld: TDynField;
2139 begin
2140 if (Length(fldname) = 0) then exit;
2141 fld := field[fldname];
2142 if (fld <> nil) then
2143 begin
2144 if (fld.mType <> fld.TType.TInt) or (fld.mEBS <> fld.TEBS.TNone) then
2145 begin
2146 raise Exception.Create(Format('invalid user field ''%s'' type', [fld.name]));
2147 end;
2148 end
2149 else
2150 begin
2151 fld := TDynField.Create(fldname, fld.TType.TInt);
2152 fld.mOwner := self;
2153 fld.mIVal := v;
2154 fld.mInternal := true;
2155 fld.mDefined := true;
2156 addField(fld);
2157 end;
2158 end;
2161 procedure TDynRecord.setUserField (const fldname: AnsiString; v: AnsiString);
2162 var
2163 fld: TDynField;
2164 begin
2165 if (Length(fldname) = 0) then exit;
2166 fld := field[fldname];
2167 if (fld <> nil) then
2168 begin
2169 if (fld.mType <> fld.TType.TString) or (fld.mEBS <> fld.TEBS.TNone) then
2170 begin
2171 raise Exception.Create(Format('invalid user field ''%s'' type', [fld.name]));
2172 end;
2173 end
2174 else
2175 begin
2176 fld := TDynField.Create(fldname, fld.TType.TString);
2177 fld.mOwner := self;
2178 fld.mSVal := v;
2179 fld.mInternal := true;
2180 fld.mDefined := true;
2181 addField(fld);
2182 end;
2183 end;
2186 procedure TDynRecord.setUserField (const fldname: AnsiString; v: Boolean);
2187 var
2188 fld: TDynField;
2189 begin
2190 if (Length(fldname) = 0) then exit;
2191 fld := field[fldname];
2192 if (fld <> nil) then
2193 begin
2194 if (fld.mType <> fld.TType.TBool) or (fld.mEBS <> fld.TEBS.TNone) then
2195 begin
2196 raise Exception.Create(Format('invalid user field ''%s'' type', [fld.name]));
2197 end;
2198 end
2199 else
2200 begin
2201 fld := TDynField.Create(fldname, fld.TType.TBool);
2202 fld.mOwner := self;
2203 fld.mIVal := Integer(v);
2204 fld.mInternal := true;
2205 fld.mDefined := true;
2206 addField(fld);
2207 end;
2208 end;
2209 }
2213 var
2216 begin
2218 begin
2221 begin
2223 begin
2227 if isForTrig[tdn] then raise Exception.Create(Format('duplicate trigdata ''%s'' trigtype ''%s''', [mName, tdn]));
2231 end
2232 else
2233 begin
2239 end
2240 else
2241 begin
2246 begin
2249 begin
2250 if (mSize > 0) then raise Exception.Create(Format('duplicate `size` in record ''%s''', [mName]));
2252 if (mSize < 1) then raise Exception.Create(Format('invalid record ''%s'' size: %d', [mName, mSize]));
2254 continue;
2257 begin
2258 if (mBinBlock >= 0) then raise Exception.Create(Format('duplicate `binblock` in record ''%s''', [mName]));
2260 if (mBinBlock < 1) then raise Exception.Create(Format('invalid record ''%s'' binblock: %d', [mName, mBinBlock]));
2261 continue;
2267 // load fields
2269 begin
2271 //if hasByName(fld.name) then begin fld.Free(); raise Exception.Create(Format('duplicate field ''%s''', [fld.name])); end;
2272 // append
2275 begin
2279 // done with field
2286 var
2288 begin
2290 begin
2293 end
2294 else
2295 begin
2296 // record
2300 begin
2310 var
2312 begin
2314 begin
2315 // trigger data
2318 begin
2321 begin
2326 end
2327 else
2328 begin
2331 end
2332 else
2333 begin
2334 // record
2341 begin
2351 var
2363 var
2366 begin
2367 //writeln('*** rec: ', rec.mName, '.', rec.mId, ' (', rec.mFields.count, ')');
2369 begin
2371 begin
2373 continue;
2379 begin
2380 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);
2381 //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]));
2383 //writeln(' ', rec.mName, '.', rec.mId, ':', fld.mName, ' -> ', rt.mName, '.', rt.mId, ' (', fld.mEBSTypeName, '.', fld.mRecRefId, ')');
2389 begin
2390 //writeln(' ', fld.mName);
2395 begin
2398 try
2400 begin
2401 // parse map file as sequence of blocks
2405 // parse blocks
2407 begin
2413 if (bsize < 0) or (bsize > $1fffffff) then raise Exception.Create(Format('block of type %d has invalid size %d', [btype, bsize]));
2414 if loaded[btype] then raise Exception.Create(Format('block of type %d already loaded', [btype]));
2416 // find record type for this block
2419 if (rect = nil) then raise Exception.Create(Format('block of type %d has no corresponding record', [btype]));
2420 //writeln('found type ''', rec.mName, ''' for block type ', btype);
2421 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]));
2422 // header?
2424 begin
2425 if (bsize <> mSize) then raise Exception.Create(Format('header block of type %d has invalid number of records', [btype]));
2430 end
2431 else
2432 begin
2433 // create list for this type
2438 begin
2442 begin
2449 //writeln('parsed ''', rec.mId, '''...');
2455 //st.position := st.position+bsize;
2457 // link fields
2459 begin
2463 exit;
2466 // read fields
2468 if (mSize < 1) then raise Exception.Create(Format('cannot read record of type ''%s'' with unknown size', [mName]));
2472 begin
2475 if (fld.mBinOfs >= st.size) then raise Exception.Create(Format('record of type ''%s'' has invalid field ''%s''', [fld.mName]));
2477 //writeln('parsing ''', mName, '.', fld.mName, '''...');
2480 finally
2487 procedure TDynRecord.writeBinTo (st: TStream; trigbufsz: Integer=-1; onlyFields: Boolean=false);
2488 var
2494 //f, c: Integer;
2497 begin
2499 begin
2500 if (mBinBlock < 1) then raise Exception.Create('cannot write binary record without block number');
2503 end
2504 else
2505 begin
2508 try
2513 // write normal fields
2515 begin
2516 // record list?
2520 if (fld.mBinOfs >= bufsz) then raise Exception.Create('binary value offset is outside of the buffer');
2522 //writeln('writing field <', fld.mName, '>');
2526 // write block with normal fields
2528 begin
2529 //writeln('writing header...');
2530 // signature and version
2541 // write other blocks, if any
2543 begin
2544 // calculate blkmax
2547 begin
2548 // record list?
2550 begin
2558 // write blocks
2560 begin
2564 begin
2565 // record list?
2567 begin
2576 // flush block
2578 begin
2589 // write end marker
2594 finally
2602 var
2605 begin
2607 begin
2614 try
2616 begin
2617 // record list?
2619 begin
2622 begin
2624 begin
2630 continue;
2637 finally
2645 {$IF DEFINED(D2D_DYNREC_PROFILER)}
2646 var
2657 begin
2659 writeln('record cloning: ', profCloneRec div 1000, '.', profCloneRec mod 1000, ' milliseconds');
2660 writeln('findRecType : ', profFindRecType div 1000, '.', profFindRecType mod 1000, ' milliseconds');
2661 writeln('field[] : ', profFieldSearching div 1000, '.', profFieldSearching mod 1000, ' milliseconds');
2662 writeln('list dup check: ', profListDupChecking div 1000, '.', profListDupChecking mod 1000, ' milliseconds');
2663 writeln('addRecByType : ', profAddRecByType div 1000, '.', profAddRecByType mod 1000, ' milliseconds');
2664 writeln('field valparse: ', profFieldValParsing div 1000, '.', profFieldValParsing mod 1000, ' milliseconds');
2665 writeln('fix defaults : ', profFixDefaults div 1000, '.', profFixDefaults mod 1000, ' milliseconds');
2666 writeln('recvalparse : ', profRecValParse div 1000, '.', profRecValParse mod 1000, ' milliseconds');
2668 {$ENDIF}
2672 var
2676 {$IF DEFINED(D2D_DYNREC_PROFILER)}
2678 {$ENDIF}
2679 begin
2680 if (mOwner = nil) then raise Exception.Create(Format('can''t parse record ''%s'' value without owner', [mName]));
2684 // not a header?
2686 begin
2687 // id?
2689 end
2690 else
2691 begin
2695 //writeln('parsing record <', mName, '>');
2698 begin
2700 //writeln('<', mName, '.', pr.tokStr, '>');
2702 // records
2704 begin
2705 // add records with this type (if any)
2710 begin
2715 try
2718 (*
2719 if (Length(rec.mId) > 0) then
2720 begin
2721 {$IF DEFINED(D2D_DYNREC_PROFILER)}stt := curTimeMicro();{$ENDIF}
2722 fld := field[pr.tokStr];
2723 {$IF DEFINED(D2D_DYNREC_PROFILER)}profFieldSearching := curTimeMicro()-stt;{$ENDIF}
2724 (*
2725 if (fld <> nil) and (fld.mRVal <> nil) then
2726 begin
2727 {$IF DEFINED(D2D_DYNREC_PROFILER)}stt := curTimeMicro();{$ENDIF}
2728 //idtmp := trc.mName+':'+rec.mId;
2729 //if ids.put(idtmp, 1) then raise Exception.Create(Format('duplicate thing ''%s'' in record ''%s''', [fld.mName, mName]));
2730 if fld.mRHash.has(rec.mId) then raise Exception.Create(Format('duplicate thing ''%s'' in record ''%s''', [fld.mName, mName]));
2731 {$IF DEFINED(D2D_DYNREC_PROFILER)}profListDupChecking := curTimeMicro()-stt;{$ENDIF}
2732 end;
2733 end;
2734 *)
2739 finally
2742 continue;
2746 // fields
2751 begin
2752 if fld.defined then raise Exception.Create(Format('duplicate field ''%s'' in record ''%s''', [fld.mName, mName]));
2753 if fld.internal then raise Exception.Create(Format('internal field ''%s'' in record ''%s''', [fld.mName, mName]));
2758 continue;
2761 // something is wrong
2765 // fix field defaults
2769 //writeln('done parsing record <', mName, '>');
2770 //{$IF DEFINED(D2D_DYNREC_PROFILER)}writeln('stall: ', curTimeMicro()-stall);{$ENDIF}
2775 // ////////////////////////////////////////////////////////////////////////// //
2777 begin
2784 begin
2791 begin
2802 begin
2805 begin
2814 begin
2820 var
2822 begin
2829 var
2831 begin
2835 // fields
2838 begin
2842 begin
2846 end
2847 else
2848 begin
2853 // max field
2860 var
2862 begin
2864 // fields
2866 begin
2873 var
2875 begin
2877 begin
2885 var
2891 begin
2900 begin
2903 begin
2904 if StrEqu(mIds[f], idname) then raise Exception.Create(Format('duplicate field ''%s'' in enum/bitset ''%s''', [idname, mName]));
2906 if StrEqu(mMaxName, idname) then raise Exception.Create(Format('duplicate field ''%s'' in enum/bitset ''%s''', [idname, mName]));
2910 // has value?
2912 begin
2914 begin
2915 if (Length(mMaxName) > 0) then raise Exception.Create(Format('duplicate max field ''%s'' in enum/bitset ''%s''', [idname, mName]));
2918 end
2919 else
2920 begin
2926 // append it?
2928 begin
2929 // fix maxvalue
2931 begin
2938 // next cv
2940 begin
2949 // add max field
2951 begin
2960 // ////////////////////////////////////////////////////////////////////////// //
2962 begin
2971 var
2974 begin
2989 begin
2996 var
2998 begin
3000 begin
3008 var
3010 begin
3012 begin
3020 var
3022 begin
3024 begin
3032 var
3037 // setup header links and type links
3039 var
3041 begin
3044 begin
3049 begin
3051 if (fld.mEBSType = nil) then raise Exception.Create(Format('field ''%s'' of type ''%s'' has no correcponding record definition', [fld.mName, fld.mEBSTypeName]));
3055 begin
3057 if (fld.mEBSType = nil) then raise Exception.Create(Format('field ''%s'' of type ''%s'' has no correcponding enum/bitset', [fld.mName, fld.mEBSTypeName]));
3058 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]));
3064 // setup default values
3066 var
3068 begin
3072 begin
3075 begin
3080 begin
3083 begin
3089 //writeln(eb.definition); writeln;
3090 continue;
3094 begin
3097 begin
3099 begin
3106 //writeln(dr.definition); writeln;
3107 continue;
3111 //writeln(dr.definition); writeln;
3112 if (findRecType(rec.name) <> nil) then begin rec.Free(); raise Exception.Create(Format('duplicate record ''%s''', [rec.name])); end;
3113 if (hdr <> nil) and StrEqu(rec.name, hdr.name) then begin rec.Free(); raise Exception.Create(Format('duplicate record ''%s''', [rec.name])); end;
3116 begin
3117 if (hdr <> nil) then begin rec.Free(); raise Exception.Create(Format('duplicate header record ''%s'' (previous is ''%s'')', [rec.name, hdr.name])); end;
3119 end
3120 else
3121 begin
3126 // put header record to top
3132 // setup header links and type links
3136 // setup default values
3142 // ////////////////////////////////////////////////////////////////////////// //
3144 var
3146 begin
3148 try
3155 finally
3162 var
3164 begin
3166 try
3172 finally
3179 var
3185 begin
3187 result += '// ////////////////////////////////////////////////////////////////////////// //'#10;
3190 result += #10#10'// ////////////////////////////////////////////////////////////////////////// //'#10;
3193 begin
3198 result += #10#10'// ////////////////////////////////////////////////////////////////////////// //'#10;
3204 begin
3208 begin
3214 begin
3226 var
3228 begin
3230 result += '// ////////////////////////////////////////////////////////////////////////// //'#10;
3236 function TDynMapDef.getTrigTypeCount (): Integer; inline; begin result := trigTypes.count; end;
3237 function TDynMapDef.getTrigTypeAt (idx: Integer): TDynRecord; inline; begin if (idx >= 0) and (idx < trigTypes.count) then result := trigTypes[idx] else result := nil; end;