DEADSOFTWARE

mapdef cleanup; renamed some fields; mapdef.txt is RC0 now
[d2df-sdl.git] / src / shared / xdynrec.pas
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}
17 {.$DEFINE XDYNREC_USE_FIELDHASH} // actually, it is SLOWER with this
18 unit xdynrec;
20 interface
22 uses
23 Variants, Classes,
24 xparser, xstreams, utils, hashtable;
27 // ////////////////////////////////////////////////////////////////////////// //
28 type
29 TDynMapDef = class;
30 TDynRecord = class;
31 TDynField = class;
32 TDynEBS = class;
34 TDynFieldList = specialize TSimpleList<TDynField>;
35 TDynRecList = specialize TSimpleList<TDynRecord>;
36 TDynEBSList = specialize TSimpleList<TDynEBS>;
38 // this is base type for all scalars (and arrays)
39 TDynField = class
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 // in binary: arrays of chars are pascal shortstrings (with counter in the first byte)
49 private
50 type
51 TEBS = (TNone, TRec, TEnum, TBitSet);
53 private
54 mOwner: TDynRecord; // owner record
55 mName: AnsiString; // field name
56 mType: TType; // field type
57 mIVal: Integer; // for all integer types
58 mIVal2: Integer; // for point and size
59 mSVal: AnsiString; // string; for byte and char arrays
60 mRVal: TDynRecList; // for list
61 mRHash: THashStrInt; // id -> index in mRVal
62 mRecRef: TDynRecord; // for TEBS.TRec
63 mMaxDim: Integer; // for byte and char arrays; <0: not an array; 0: impossible value
64 mBinOfs: Integer; // offset in binary; <0 - none
65 mSepPosSize: Boolean; // for points and sizes, use separate fields
66 mAsT: Boolean; // for points and sizes, use separate fields, names starts with `t`
67 mDefined: Boolean;
68 mHasDefault: Boolean;
69 mWriteDef: Boolean;
70 mInternal: Boolean;
71 mNegBool: Boolean;
72 mBitSetUnique: Boolean; // bitset can contain only one value
73 mAsMonsterId: Boolean; // special hack for triggers: monster record number+1 in binary (so 0 means "none")
74 // default value
75 mDefUnparsed: AnsiString;
76 mDefSVal: AnsiString; // default string value
77 mDefIVal, mDefIVal2: Integer; // default integer values
78 mDefRecRef: TDynRecord;
79 mEBS: TEBS; // complex type type
80 mEBSTypeName: AnsiString; // name of enum, bitset or record
81 mEBSType: TObject; // either TDynRecord or TDynEBS; nil means "simple type"; nil for `TTrigData` too
83 // for binary parser
84 mRecRefId: AnsiString;
86 // for userdata
87 mTagInt: Integer;
88 mTagPtr: Pointer;
90 // for pasgen
91 mAlias: AnsiString;
93 private
94 procedure cleanup ();
96 procedure parseDef (pr: TTextParser);
98 procedure parseDefaultValue (); // parse `mDefUnparsed` to `mDefSVal`, `mDefIVal`, `mDefIVal2`, `mDefRecRef`
99 procedure fixDefaultValue (); // this will NOT clone `mDefRecRef`
100 function isDefaultValue (): Boolean;
102 function getListCount (): Integer; inline;
103 function getListItem (idx: Integer): TDynRecord; inline; overload;
104 function getListItem (const aname: AnsiString): TDynRecord; inline; overload;
106 function getRecRefIndex (): Integer;
108 procedure setIVal (v: Integer); inline;
110 function getVar (): Variant;
111 procedure setVar (val: Variant);
113 protected
114 // returns `true` for duplicate record id
115 function addListItem (rec: TDynRecord): Boolean; inline;
117 public
118 constructor Create (const aname: AnsiString; atype: TType);
119 constructor Create (pr: TTextParser);
120 constructor Create (const aname: AnsiString; val: Variant);
121 destructor Destroy (); override;
123 class function getTypeName (t: TType): AnsiString;
125 // build "alias name" for pascal code
126 function palias (firstUp: Boolean=false): AnsiString;
128 function definition (): AnsiString;
130 function clone (newOwner: TDynRecord=nil; registerIn: TDynRecord=nil): TDynField;
132 procedure parseValue (pr: TTextParser);
133 procedure parseBinValue (st: TStream);
135 procedure writeTo (wr: TTextWriter);
136 procedure writeBinTo (st: TStream);
138 // won't work for lists
139 function isSimpleEqu (fld: TDynField): Boolean;
141 procedure setValue (const s: AnsiString);
143 function GetEnumerator (): TDynRecList.TEnumerator; inline;
145 public
146 property name: AnsiString read mName;
147 property baseType: TType read mType;
148 property negbool: Boolean read mNegBool;
149 property defined: Boolean read mDefined;
150 property internal: Boolean read mInternal write mInternal;
151 property hasTPrefix: Boolean read mAsT;
152 property separatePasFields: Boolean read mSepPosSize;
153 property binOfs: Integer read mBinOfs;
154 property ival: Integer read mIVal write setIVal;
155 property ival2: Integer read mIVal2;
156 property sval: AnsiString read mSVal;
157 property hasDefault: Boolean read mHasDefault;
158 property defsval: AnsiString read mDefSVal;
159 property ebs: TEBS read mEBS;
160 property ebstype: TObject read mEBSType;
161 property ebstypename: AnsiString read mEBSTypeName; // enum/bitset name
162 property recref: TDynRecord read mRecRef;
163 property recrefIndex: Integer read getRecRefIndex; // search for this record in header; -1: not found
164 // for lists
165 property count: Integer read getListCount;
166 property item[idx: Integer]: TDynRecord read getListItem;
167 property items[const aname: AnsiString]: TDynRecord read getListItem; default; // alas, FPC 3+ lost property overloading feature
168 // userdata
169 property tagInt: Integer read mTagInt write mTagInt;
170 property tagPtr: Pointer read mTagPtr write mTagPtr;
171 //
172 property varvalue: Variant read getVar write setVar;
173 end;
176 // "value" header record contains TList fields, with name equal to record type
177 TDynRecord = class
178 private
179 mOwner: TDynMapDef;
180 mId: AnsiString;
181 mName: AnsiString;
182 mSize: Integer;
183 mFields: TDynFieldList;
184 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
185 mFieldsHash: THashStrInt; // id -> index in mRVal
186 {$ENDIF}
187 mTrigTypes: array of AnsiString; // if this is triggerdata, we'll hold list of triggers here
188 mHeader: Boolean; // true for header record
189 mBinBlock: Integer; // -1: none
190 mHeaderRec: TDynRecord; // for "value" records this is header record with data, for "type" records this is header type record
192 // for userdata
193 mTagInt: Integer;
194 mTagPtr: Pointer;
196 mRec2Free: TDynRecList;
198 private
199 procedure parseDef (pr: TTextParser); // parse definition
201 function findByName (const aname: AnsiString): Integer; inline;
202 function hasByName (const aname: AnsiString): Boolean; inline;
203 function getFieldByName (const aname: AnsiString): TDynField; inline;
204 function getFieldAt (idx: Integer): TDynField; inline;
205 function getCount (): Integer; inline;
207 function getIsTrigData (): Boolean; inline;
208 function getIsForTrig (const aname: AnsiString): Boolean; inline;
210 function getForTrigCount (): Integer; inline;
211 function getForTrigAt (idx: Integer): AnsiString; inline;
213 procedure regrec (rec: TDynRecord);
215 protected
216 function findRecordByTypeId (const atypename, aid: AnsiString): TDynRecord;
217 function findRecordNumByType (const atypename: AnsiString; rc: TDynRecord): Integer;
218 function addRecordByType (const atypename: AnsiString; rc: TDynRecord): Boolean; // `true`: duplicate record id
220 procedure addField (fld: TDynField); inline;
221 function addFieldChecked (fld: TDynField): Boolean; inline; // `true`: duplicate name
223 public
224 constructor Create ();
225 constructor Create (pr: TTextParser); // parse definition
226 destructor Destroy (); override;
228 function definition (): AnsiString;
230 function clone (registerIn: TDynRecord): TDynRecord;
232 function isSimpleEqu (rec: TDynRecord): Boolean;
234 procedure parseValue (pr: TTextParser; beginEaten: Boolean=false);
235 procedure parseBinValue (st: TStream; forceData: Boolean=false);
237 procedure writeTo (wr: TTextWriter; putHeader: Boolean=true);
238 procedure writeBinTo (st: TStream; trigbufsz: Integer=-1; onlyFields: Boolean=false);
240 // find field with `TriggerType` type
241 function trigTypeField (): TDynField;
243 // number of records of the given instance
244 function instanceCount (const typename: AnsiString): Integer;
246 function getUserVar (const aname: AnsiString): Variant;
247 procedure setUserVar (const aname: AnsiString; val: Variant);
249 public
250 property id: AnsiString read mId; // for map parser
251 property name: AnsiString read mName; // record name
252 property size: Integer read mSize; // size in bytes
253 //property fields: TDynFieldList read mFields;
254 property has[const aname: AnsiString]: Boolean read hasByName;
255 property count: Integer read getCount;
256 property field[const aname: AnsiString]: TDynField read getFieldByName; default;
257 property fieldAt[idx: Integer]: TDynField read getFieldAt;
258 property isTrigData: Boolean read getIsTrigData;
259 property isForTrig[const aname: AnsiString]: Boolean read getIsForTrig;
260 property forTrigCount: Integer read getForTrigCount;
261 property forTrigAt[idx: Integer]: AnsiString read getForTrigAt;
262 property headerRec: TDynRecord read mHeaderRec;
263 property isHeader: Boolean read mHeader;
264 // userdata
265 property tagInt: Integer read mTagInt write mTagInt;
266 property tagPtr: Pointer read mTagPtr write mTagPtr;
267 // userfields
268 property user[const aname: AnsiString]: Variant read getUserVar write setUserVar;
269 end;
271 TDynEBS = class
272 private
273 mOwner: TDynMapDef;
274 mIsEnum: Boolean;
275 mName: AnsiString;
276 mIds: array of AnsiString;
277 mVals: array of Integer;
278 mMaxName: AnsiString; // MAX field
279 mMaxVal: Integer; // max value
281 private
282 procedure cleanup ();
284 procedure parseDef (pr: TTextParser); // parse definition
286 function findByName (const aname: AnsiString): Integer; inline;
287 function hasByName (const aname: AnsiString): Boolean; inline;
288 function getFieldByName (const aname: AnsiString): Integer; inline;
290 public
291 constructor Create (pr: TTextParser); // parse definition
292 destructor Destroy (); override;
294 function definition (): AnsiString;
295 function pasdef (): AnsiString;
297 // return empty string if not found
298 function nameByValue (v: Integer): AnsiString;
300 public
301 property name: AnsiString read mName; // record name
302 property isEnum: Boolean read mIsEnum;
303 property has[const aname: AnsiString]: Boolean read hasByName;
304 property field[const aname: AnsiString]: Integer read getFieldByName;
305 end;
308 TDynMapDef = class
309 public
310 recTypes: TDynRecList; // [0] is always header
311 trigTypes: TDynRecList; // trigdata
312 ebsTypes: TDynEBSList; // enums, bitsets
314 private
315 procedure parseDef (pr: TTextParser);
317 function getHeaderRecType (): TDynRecord; inline;
319 function getTrigTypeCount (): Integer; inline;
320 function getTrigTypeAt (idx: Integer): TDynRecord; inline;
322 public
323 constructor Create (pr: TTextParser); // parses data definition
324 destructor Destroy (); override;
326 function findRecType (const aname: AnsiString): TDynRecord;
327 function findTrigFor (const aname: AnsiString): TDynRecord;
328 function findEBSType (const aname: AnsiString): TDynEBS;
330 function pasdefconst (): AnsiString;
332 // creates new header record
333 function parseMap (pr: TTextParser): TDynRecord;
335 // creates new header record
336 function parseBinMap (st: TStream): TDynRecord;
338 public
339 property headerType: TDynRecord read getHeaderRecType;
340 property trigTypeCount: Integer read getTrigTypeCount;
341 property trigType[idx: Integer]: TDynRecord read getTrigTypeAt;
342 end;
345 {$IF DEFINED(D2D_DYNREC_PROFILER)}
346 procedure xdynDumpProfiles ();
347 {$ENDIF}
350 implementation
352 uses
353 SysUtils, e_log
354 {$IF DEFINED(D2D_DYNREC_PROFILER)},xprofiler{$ENDIF};
357 // ////////////////////////////////////////////////////////////////////////// //
358 function StrEqu (const a, b: AnsiString): Boolean; inline; begin result := (a = b); end;
361 // ////////////////////////////////////////////////////////////////////////// //
362 function TDynField.GetEnumerator (): TDynRecList.TEnumerator; inline;
363 begin
364 //result := TListEnumerator.Create(mRVal);
365 if (mRVal <> nil) then result := mRVal.GetEnumerator else result := TDynRecList.TEnumerator.Create(nil, 0);
366 end;
369 // ////////////////////////////////////////////////////////////////////////// //
370 constructor TDynField.Create (const aname: AnsiString; atype: TType);
371 begin
372 mRVal := nil;
373 mRecRef := nil;
374 mRHash := nil;
375 cleanup();
376 mName := aname;
377 mType := atype;
378 if (mType = TType.TList) then
379 begin
380 mRVal := TDynRecList.Create();
381 mRHash := hashNewStrInt();
382 end;
383 end;
386 constructor TDynField.Create (pr: TTextParser);
387 begin
388 cleanup();
389 parseDef(pr);
390 end;
393 constructor TDynField.Create (const aname: AnsiString; val: Variant);
394 procedure setInt32 (v: LongInt);
395 begin
396 case mType of
397 TType.TBool:
398 if (v = 0) then mIVal := 0
399 else if (v = 1) then mIVal := 1
400 else raise Exception.Create('cannot convert shortint variant to field value');
401 TType.TByte:
402 if (v >= -128) and (v <= 127) then mIVal := v
403 else raise Exception.Create('cannot convert shortint variant to field value');
404 TType.TUByte:
405 if (v >= 0) and (v <= 255) then mIVal := v
406 else raise Exception.Create('cannot convert shortint variant to field value');
407 TType.TShort:
408 if (v >= -32768) and (v <= 32767) then mIVal := v
409 else raise Exception.Create('cannot convert shortint variant to field value');
410 TType.TUShort:
411 if (v >= 0) and (v <= 65535) then mIVal := v
412 else raise Exception.Create('cannot convert shortint variant to field value');
413 TType.TInt:
414 mIVal := v;
415 TType.TUInt:
416 mIVal := v;
417 TType.TString:
418 mSVal := formatstrf('%s', [v]);
419 else
420 raise Exception.Create('cannot convert integral variant to field value');
421 end;
422 end;
423 begin
424 mRVal := nil;
425 mRecRef := nil;
426 mRHash := nil;
427 cleanup();
428 mName := aname;
429 case varType(val) of
430 varEmpty: raise Exception.Create('cannot convert empty variant to field value');
431 varNull: raise Exception.Create('cannot convert null variant to field value');
432 varSingle: raise Exception.Create('cannot convert single variant to field value');
433 varDouble: raise Exception.Create('cannot convert double variant to field value');
434 varDecimal: raise Exception.Create('cannot convert decimal variant to field value');
435 varCurrency: raise Exception.Create('cannot convert currency variant to field value');
436 varDate: raise Exception.Create('cannot convert date variant to field value');
437 varOleStr: raise Exception.Create('cannot convert olestr variant to field value');
438 varStrArg: raise Exception.Create('cannot convert stdarg variant to field value');
439 varString: mType := TType.TString;
440 varDispatch: raise Exception.Create('cannot convert dispatch variant to field value');
441 varBoolean: mType := TType.TBool;
442 varVariant: raise Exception.Create('cannot convert variant variant to field value');
443 varUnknown: raise Exception.Create('cannot convert unknown variant to field value');
444 varByte: mType := TType.TUByte;
445 varWord: mType := TType.TUShort;
446 varShortInt: mType := TType.TByte;
447 varSmallint: mType := TType.TShort;
448 varInteger: mType := TType.TInt;
449 varInt64: raise Exception.Create('cannot convert int64 variant to field value');
450 varLongWord: raise Exception.Create('cannot convert longword variant to field value');
451 varQWord: raise Exception.Create('cannot convert uint64 variant to field value');
452 varError: raise Exception.Create('cannot convert error variant to field value');
453 else raise Exception.Create('cannot convert undetermined variant to field value');
454 end;
455 varvalue := val;
456 end;
459 destructor TDynField.Destroy ();
460 begin
461 cleanup();
462 inherited;
463 end;
466 procedure TDynField.cleanup ();
467 begin
468 mName := '';
469 mType := TType.TInt;
470 mIVal := 0;
471 mIVal2 := 0;
472 mSVal := '';
473 mRVal.Free();
474 mRVal := nil;
475 mRHash.Free();
476 mRHash := nil;
477 mRecRef := nil;
478 mMaxDim := -1;
479 mBinOfs := -1;
480 mSepPosSize := false;
481 mAsT := false;
482 mHasDefault := false;
483 mDefined := false;
484 mWriteDef := false;
485 mInternal := true;
486 mDefUnparsed := '';
487 mDefSVal := '';
488 mDefIVal := 0;
489 mDefIVal2 := 0;
490 mDefRecRef := nil;
491 mEBS := TEBS.TNone;
492 mEBSTypeName := '';
493 mEBSType := nil;
494 mBitSetUnique := false;
495 mAsMonsterId := false;
496 mNegBool := false;
497 mRecRefId := '';
498 mTagInt := 0;
499 mTagPtr := nil;
500 mAlias := '';
501 end;
504 function TDynField.clone (newOwner: TDynRecord=nil; registerIn: TDynRecord=nil): TDynField;
505 var
506 rec: TDynRecord;
507 begin
508 result := TDynField.Create(mName, mType);
509 result.mOwner := mOwner;
510 if (newOwner <> nil) then result.mOwner := newOwner else result.mOwner := mOwner;
511 result.mName := mName;
512 result.mType := mType;
513 result.mIVal := mIVal;
514 result.mIVal2 := mIVal2;
515 result.mSVal := mSVal;
516 if (mRVal <> nil) then
517 begin
518 if (result.mRVal = nil) then result.mRVal := TDynRecList.Create(mRVal.count);
519 if (result.mRHash = nil) then result.mRHash := hashNewStrInt();
520 for rec in mRVal do result.addListItem(rec.clone(registerIn));
521 end;
522 result.mRecRef := mRecRef;
523 result.mMaxDim := mMaxDim;
524 result.mBinOfs := mBinOfs;
525 result.mSepPosSize := mSepPosSize;
526 result.mAsT := mAsT;
527 result.mDefined := mDefined;
528 result.mHasDefault := mHasDefault;
529 result.mWriteDef := mWriteDef;
530 result.mInternal := mInternal;
531 result.mNegBool := mNegBool;
532 result.mBitSetUnique := mBitSetUnique;
533 result.mAsMonsterId := mAsMonsterId;
534 result.mDefUnparsed := mDefUnparsed;
535 result.mDefSVal := mDefSVal;
536 result.mDefIVal := mDefIVal;
537 result.mDefIVal2 := mDefIVal2;
538 result.mDefRecRef := mDefRecRef;
539 result.mEBS := mEBS;
540 result.mEBSTypeName := mEBSTypeName;
541 result.mEBSType := mEBSType;
542 result.mRecRefId := mRecRefId;
543 result.mTagInt := mTagInt;
544 result.mTagPtr := mTagPtr;
545 result.mAlias := mAlias;
546 end;
549 function TDynField.palias (firstUp: Boolean=false): AnsiString;
550 var
551 nextUp: Boolean;
552 ch: AnsiChar;
553 begin
554 if (Length(mAlias) > 0) then
555 begin
556 if firstUp then result := UpCase1251(mAlias[1])+Copy(mAlias, 2, Length(mAlias)-1) else result := mAlias;
557 end
558 else
559 begin
560 result := '';
561 nextUp := firstUp;
562 for ch in mName do
563 begin
564 if (ch = '_') then begin nextUp := true; continue; end;
565 if nextUp then result += UpCase1251(ch) else result += ch;
566 nextUp := false;
567 end;
568 end;
569 end;
572 procedure TDynField.setIVal (v: Integer); inline;
573 begin
574 //FIXME: check type
575 mIVal := v;
576 mDefined := true;
577 end;
580 function TDynField.getVar (): Variant;
581 begin
582 if (mEBS = TEBS.TRec) then begin result := LongInt(getRecRefIndex); exit; end;
583 case mType of
584 TType.TBool: result := (mIVal <> 0);
585 TType.TChar: result := mSVal;
586 TType.TByte: result := ShortInt(mIVal);
587 TType.TUByte: result := Byte(mIVal);
588 TType.TShort: result := SmallInt(mIVal);
589 TType.TUShort: result := Word(mIVal);
590 TType.TInt: result := LongInt(mIVal);
591 TType.TUInt: result := LongWord(mIVal);
592 TType.TString: result := mSVal;
593 TType.TPoint: raise Exception.Create('cannot convert point field to variant');
594 TType.TSize: raise Exception.Create('cannot convert size field to variant');
595 TType.TList: raise Exception.Create('cannot convert list field to variant');
596 TType.TTrigData: raise Exception.Create('cannot convert trigdata field to variant');
597 else result := Unassigned; raise Exception.Create('ketmar forgot to handle some field type');
598 end;
599 end;
602 procedure TDynField.setVar (val: Variant);
603 procedure setInt32 (v: LongInt);
604 begin
605 case mType of
606 TType.TBool:
607 if (v = 0) then mIVal := 0
608 else if (v = 1) then mIVal := 1
609 else raise Exception.Create('cannot convert shortint variant to field value');
610 TType.TByte:
611 if (v >= -128) and (v <= 127) then mIVal := v
612 else raise Exception.Create('cannot convert shortint variant to field value');
613 TType.TUByte:
614 if (v >= 0) and (v <= 255) then mIVal := v
615 else raise Exception.Create('cannot convert shortint variant to field value');
616 TType.TShort:
617 if (v >= -32768) and (v <= 32767) then mIVal := v
618 else raise Exception.Create('cannot convert shortint variant to field value');
619 TType.TUShort:
620 if (v >= 0) and (v <= 65535) then mIVal := v
621 else raise Exception.Create('cannot convert shortint variant to field value');
622 TType.TInt:
623 mIVal := v;
624 TType.TUInt:
625 mIVal := v;
626 TType.TString:
627 mSVal := formatstrf('%s', [v]);
628 else
629 raise Exception.Create('cannot convert integral variant to field value');
630 end;
631 end;
632 begin
633 case varType(val) of
634 varEmpty: raise Exception.Create('cannot convert empty variant to field value');
635 varNull: raise Exception.Create('cannot convert null variant to field value');
636 varSingle: raise Exception.Create('cannot convert single variant to field value');
637 varDouble: raise Exception.Create('cannot convert double variant to field value');
638 varDecimal: raise Exception.Create('cannot convert decimal variant to field value');
639 varCurrency: raise Exception.Create('cannot convert currency variant to field value');
640 varDate: raise Exception.Create('cannot convert date variant to field value');
641 varOleStr: raise Exception.Create('cannot convert olestr variant to field value');
642 varStrArg: raise Exception.Create('cannot convert stdarg variant to field value');
643 varString:
644 if (mType = TType.TChar) or (mType = TType.TString) then
645 begin
646 mSVal := val;
647 end
648 else
649 begin
650 raise Exception.Create('cannot convert string variant to field value');
651 end;
652 varDispatch: raise Exception.Create('cannot convert dispatch variant to field value');
653 varBoolean:
654 case mType of
655 TType.TBool,
656 TType.TByte,
657 TType.TUByte,
658 TType.TShort,
659 TType.TUShort,
660 TType.TInt,
661 TType.TUInt:
662 if val then mIVal := 1 else mIVal := 0;
663 TType.TString:
664 if val then mSVal := 'true' else mSVal := 'false';
665 else
666 raise Exception.Create('cannot convert boolean variant to field value');
667 end;
668 varVariant: raise Exception.Create('cannot convert variant variant to field value');
669 varUnknown: raise Exception.Create('cannot convert unknown variant to field value');
670 varByte,
671 varWord,
672 varShortInt,
673 varSmallint,
674 varInteger:
675 setInt32(val);
676 varInt64:
677 if (val < Int64(LongInt($80000000))) or (val > LongInt($7FFFFFFF)) then
678 raise Exception.Create('cannot convert boolean variant to field value')
679 else
680 mIVal := LongInt(val);
681 varLongWord:
682 if (val > LongWord($7FFFFFFF)) then raise Exception.Create('cannot convert longword variant to field value')
683 else setInt32(Integer(val));
684 varQWord: raise Exception.Create('cannot convert uint64 variant to field value');
685 varError: raise Exception.Create('cannot convert error variant to field value');
686 else raise Exception.Create('cannot convert undetermined variant to field value');
687 end;
688 mDefined := true;
689 end;
692 // won't work for lists
693 function TDynField.isSimpleEqu (fld: TDynField): Boolean;
694 begin
695 if (fld = nil) or (mType <> fld.mType) then begin result := false; exit; end;
696 case mType of
697 TType.TBool: result := ((mIVal <> 0) = (fld.mIVal <> 0));
698 TType.TChar: result := (mSVal = fld.mSVal);
699 TType.TByte,
700 TType.TUByte,
701 TType.TShort,
702 TType.TUShort,
703 TType.TInt,
704 TType.TUInt:
705 result := (mIVal = fld.mIVal);
706 TType.TString: result := (mSVal = fld.mSVal);
707 TType.TPoint,
708 TType.TSize:
709 result := ((mIVal = fld.mIVal) and (mIVal2 = fld.mIVal2));
710 TType.TList: result := false;
711 TType.TTrigData:
712 begin
713 if (mRecRef = nil) then begin result := (fld.mRecRef = nil); exit; end;
714 result := mRecRef.isSimpleEqu(fld.mRecRef);
715 end;
716 else raise Exception.Create('ketmar forgot to handle some field type');
717 end;
718 end;
721 procedure TDynField.setValue (const s: AnsiString);
722 var
723 stp: TTextParser;
724 begin
725 stp := TStrTextParser.Create(s+';');
726 try
727 parseValue(stp);
728 finally
729 stp.Free();
730 end;
731 end;
734 procedure TDynField.parseDefaultValue ();
735 var
736 stp: TTextParser = nil;
737 oSVal: AnsiString;
738 oIVal, oIVal2: Integer;
739 oRRef: TDynRecord;
740 oDef: Boolean;
741 begin
742 if not mHasDefault then
743 begin
744 mDefSVal := '';
745 mDefIVal := 0;
746 mDefIVal2 := 0;
747 mDefRecRef := nil;
748 end
749 else
750 begin
751 oSVal := mSVal;
752 oIVal := mIVal;
753 oIVal2 := mIVal2;
754 oRRef := mRecRef;
755 oDef := mDefined;
756 try
757 stp := TStrTextParser.Create(mDefUnparsed+';');
758 parseValue(stp);
759 mDefSVal := mSVal;
760 mDefIVal := mIVal;
761 mDefIVal2 := mIVal2;
762 mDefRecRef := mRecRef;
763 finally
764 mSVal := oSVal;
765 mIVal := oIVal;
766 mIVal2 := oIVal2;
767 mRecRef := oRRef;
768 mDefined := oDef;
769 stp.Free();
770 end;
771 end;
772 end;
775 // default value should be parsed
776 procedure TDynField.fixDefaultValue ();
777 begin
778 if mDefined then exit;
779 if not mHasDefault then
780 begin
781 if mInternal then exit;
782 raise Exception.Create(Format('field ''%s'' in record ''%s'' of record type ''%s'' is not set', [mName, mOwner.mId, mOwner.mName]));
783 end;
784 if (mEBS = TEBS.TRec) then mRecRef := mDefRecRef;
785 mSVal := mDefSVal;
786 mIVal := mDefIVal;
787 mIVal2 := mDefIVal2;
788 mDefined := true;
789 end;
792 // default value should be parsed
793 function TDynField.isDefaultValue (): Boolean;
794 begin
795 if not mHasDefault then begin result := false; exit; end;
796 if (mEBS = TEBS.TRec) then begin result := (mRecRef = mDefRecRef); exit; end;
797 case mType of
798 TType.TChar, TType.TString: result := (mSVal = mDefSVal);
799 TType.TPoint, TType.TSize: result := (mIVal = mDefIVal2) and (mIVal2 = mDefIVal2);
800 TType.TList, TType.TTrigData: result := false; // no default values for those types
801 else result := (mIVal = mDefIVal);
802 end;
803 end;
806 function TDynField.getListCount (): Integer; inline;
807 begin
808 if (mRVal <> nil) then result := mRVal.count else result := 0;
809 end;
812 function TDynField.getListItem (idx: Integer): TDynRecord; inline; overload;
813 begin
814 if (mRVal <> nil) and (idx >= 0) and (idx < mRVal.count) then result := mRVal[idx] else result := nil;
815 end;
818 function TDynField.getListItem (const aname: AnsiString): TDynRecord; inline; overload;
819 var
820 idx: Integer;
821 begin
822 if (mRVal <> nil) and mRHash.get(aname, idx) then result := mRVal[idx] else result := nil;
823 end;
826 function TDynField.addListItem (rec: TDynRecord): Boolean; inline;
827 begin
828 result := false;
829 if (mRVal <> nil) then
830 begin
831 mRVal.append(rec);
832 if (Length(rec.mId) > 0) then result := mRHash.put(rec.mId, mRVal.count-1);
833 end;
834 end;
837 class function TDynField.getTypeName (t: TType): AnsiString;
838 begin
839 case t of
840 TType.TBool: result := 'bool';
841 TType.TChar: result := 'char';
842 TType.TByte: result := 'byte';
843 TType.TUByte: result := 'ubyte';
844 TType.TShort: result := 'short';
845 TType.TUShort: result := 'ushort';
846 TType.TInt: result := 'int';
847 TType.TUInt: result := 'uint';
848 TType.TString: result := 'string';
849 TType.TPoint: result := 'point';
850 TType.TSize: result := 'size';
851 TType.TList: result := 'array';
852 TType.TTrigData: result := 'trigdata';
853 else raise Exception.Create('ketmar forgot to handle some field type');
854 end;
855 end;
858 function TDynField.definition (): AnsiString;
859 begin
860 result := quoteStr(mName)+' type ';
861 result += getTypeName(mType);
862 if (Length(mAlias) > 0) then result += ' alias '+mAlias;
863 if (mMaxDim >= 0) then result += Format('[%d]', [mMaxDim]);
864 if (mBinOfs >= 0) then result += Format(' offset %d', [mBinOfs]);
865 case mEBS of
866 TEBS.TNone: begin end;
867 TEBS.TRec: result += ' '+mEBSTypeName;
868 TEBS.TEnum: result += ' enum '+mEBSTypeName;
869 TEBS.TBitSet: begin result += ' bitset '; if mBitSetUnique then result += 'unique '; result += mEBSTypeName; end;
870 end;
871 if mAsMonsterId then result += ' as monsterid';
872 if mHasDefault and (Length(mDefUnparsed) > 0) then result += ' default '+mDefUnparsed;
873 if mSepPosSize then
874 begin
875 if (mType = TType.TPoint) then begin if (mAsT) then result += ' as txy' else result += ' as xy'; end
876 else if (mType = TType.TSize) then begin if (mAsT) then result += ' as twh' else result += ' as wh'; end;
877 end;
878 if mWriteDef then result += ' writedefault';
879 if mInternal then result += ' internal';
880 end;
883 procedure TDynField.parseDef (pr: TTextParser);
884 var
885 fldname: AnsiString;
886 fldtype: AnsiString;
887 fldofs: Integer;
888 fldrecname: AnsiString;
889 asxy, aswh, ast: Boolean;
890 ainternal: Boolean;
891 writedef: Boolean;
892 defstr: AnsiString;
893 defint, defint2: Integer;
894 hasdefStr: Boolean;
895 hasdefInt: Boolean;
896 hasdefId: Boolean;
897 lmaxdim: Integer;
898 lebs: TDynField.TEBS;
899 unique: Boolean;
900 asmonid: Boolean;
901 defech: AnsiChar;
902 xalias: AnsiString;
903 begin
904 fldname := '';
905 fldtype := '';
906 fldofs := -1;
907 fldrecname := '';
908 asxy := false;
909 aswh := false;
910 ast := false;
911 ainternal := false;
912 writedef := false;
913 defstr := '';
914 defint := 0;
915 defint2 := 0;
916 hasdefStr := false;
917 hasdefInt := false;
918 hasdefId := false;
919 unique := false;
920 asmonid := false;
921 lmaxdim := -1;
922 lebs := TDynField.TEBS.TNone;
923 xalias := '';
925 // field name
926 fldname := pr.expectStrOrId();
928 while (pr.tokType <> pr.TTSemi) do
929 begin
930 if pr.eatId('type') then
931 begin
932 if (Length(fldtype) > 0) then raise Exception.Create(Format('duplicate type definition for field ''%s''', [fldname]));
933 // field type
934 fldtype := pr.expectId();
935 // fixed-size array?
936 if pr.eatDelim('[') then
937 begin
938 lmaxdim := pr.expectInt();
939 // arbitrary limits
940 if (lmaxdim < 1) or (lmaxdim > 32768) then raise Exception.Create(Format('invalid field ''%s'' array size', [fldname]));
941 pr.expectDelim(']');
942 end;
943 continue;
944 end;
946 if pr.eatId('alias') then
947 begin
948 if (Length(xalias) > 0) then raise Exception.Create(Format('duplicate alias definition for field ''%s''', [fldname]));
949 xalias := pr.expectId();
950 continue;
951 end;
953 if pr.eatId('offset') then
954 begin
955 if (fldofs >= 0) then raise Exception.Create(Format('duplicate field ''%s'' offset', [fldname]));
956 fldofs := pr.expectInt();
957 if (fldofs < 0) then raise Exception.Create(Format('invalid field ''%s'' offset', [fldname]));
958 continue;
959 end;
961 if pr.eatId('as') then
962 begin
963 if pr.eatId('xy') then asxy := true
964 else if pr.eatId('wh') then aswh := true
965 else if pr.eatId('txy') then begin asxy := true; ast := true; end
966 else if pr.eatId('twh') then begin aswh := true; ast := true; end
967 else if pr.eatId('monsterid') then begin asmonid := true; end
968 else raise Exception.Create(Format('invalid field ''%s'' as what?', [fldname]));
969 continue;
970 end;
972 if pr.eatId('enum') then
973 begin
974 lebs := TDynField.TEBS.TEnum;
975 if (Length(fldrecname) <> 0) then raise Exception.Create(Format('field ''%s'' already typed as ''%s''', [fldname, fldrecname]));
976 fldrecname := pr.expectId();
977 continue;
978 end;
980 if pr.eatId('bitset') then
981 begin
982 lebs := TDynField.TEBS.TBitSet;
983 if (Length(fldrecname) <> 0) then raise Exception.Create(Format('field ''%s'' already typed as ''%s''', [fldname, fldrecname]));
984 unique := pr.eatId('unique');
985 fldrecname := pr.expectId();
986 continue;
987 end;
989 if pr.eatId('default') then
990 begin
991 if hasdefStr or hasdefInt or hasdefId then raise Exception.Create(Format('field ''%s'' has duplicate default', [fldname]));
992 case pr.tokType of
993 pr.TTStr:
994 begin
995 hasdefStr := true;
996 defstr := pr.expectStr(true); // allow empty strings
997 end;
998 pr.TTId:
999 begin
1000 hasdefId := true;
1001 defstr := pr.expectId();
1002 end;
1003 pr.TTInt:
1004 begin
1005 hasdefInt := true;
1006 defint := pr.expectInt();
1007 end;
1008 pr.TTDelim:
1009 begin
1010 hasdefInt := true;
1011 if pr.eatDelim('[') then defech := ']' else begin pr.expectDelim('('); defech := ')'; end;
1012 defint := pr.expectInt();
1013 defint2 := pr.expectInt();
1014 pr.expectDelim(defech);
1015 end;
1016 else
1017 raise Exception.Create(Format('field ''%s'' has invalid default', [fldname]));
1018 end;
1019 continue;
1020 end;
1022 if pr.eatId('writedefault') then
1023 begin
1024 writedef := true;
1025 continue;
1026 end;
1028 if pr.eatId('internal') then
1029 begin
1030 ainternal := true;
1031 continue;
1032 end;
1034 // record type, no special modifiers
1035 if (pr.tokType <> pr.TTId) then raise Exception.Create(Format('field ''%s'' has something unexpected in definition', [fldname]));
1037 if (Length(fldrecname) <> 0) then raise Exception.Create(Format('field ''%s'' already typed as ''%s''', [fldname, fldrecname]));
1038 fldrecname := pr.expectId();
1039 lebs := TDynField.TEBS.TRec;
1040 end;
1042 pr.expectTT(pr.TTSemi);
1044 // create field
1045 mName := fldname;
1046 if (fldtype = 'bool') then mType := TType.TBool
1047 else if (fldtype = 'negbool') then begin mType := TType.TBool; mNegBool := true; end
1048 else if (fldtype = 'char') then mType := TType.TChar
1049 else if (fldtype = 'byte') then mType := TType.TByte
1050 else if (fldtype = 'ubyte') then mType := TType.TUByte
1051 else if (fldtype = 'short') then mType := TType.TShort
1052 else if (fldtype = 'ushort') then mType := TType.TUShort
1053 else if (fldtype = 'int') then mType := TType.TInt
1054 else if (fldtype = 'uint') then mType := TType.TUInt
1055 else if (fldtype = 'string') then mType := TType.TString
1056 else if (fldtype = 'point') then mType := TType.TPoint
1057 else if (fldtype = 'size') then mType := TType.TSize
1058 else if (fldtype = 'trigdata') then mType := TType.TTrigData
1059 else
1060 begin
1061 // record types defaults to int
1062 if (Length(fldrecname) > 0) then
1063 begin
1064 mType := TType.TInt;
1065 end
1066 else
1067 begin
1068 if (Length(fldtype) = 0) then raise Exception.Create(Format('field ''%s'' has no type', [fldname]))
1069 else raise Exception.Create(Format('field ''%s'' has invalid type ''%s''', [fldname, fldtype]));
1070 end;
1071 end;
1073 // check for valid arrays
1074 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]));
1076 // check for valid trigdata or record type
1077 if (mType = TType.TTrigData) then
1078 begin
1079 // trigdata
1080 if (lmaxdim < 1) then raise Exception.Create(Format('field ''%s'' of type ''%s'' cannot be non-array', [fldname, 'trigdata']));
1081 if (Length(fldrecname) > 0) then raise Exception.Create(Format('field ''%s'' of type ''%s'' cannot have another type', [fldname, 'trigdata']));
1082 lebs := TDynField.TEBS.TRec;
1083 end
1084 else if (Length(fldrecname) > 0) then
1085 begin
1086 // record
1087 if not (mType in [TType.TByte, TType.TUByte, TType.TShort, TType.TUShort, TType.TInt, TType.TUInt]) then
1088 begin
1089 raise Exception.Create(Format('field ''%s'' of record type ''%s'' cannot have type ''%s''', [fldname, fldrecname, fldtype]));
1090 end;
1091 end;
1093 // setup default value
1094 if hasdefStr then self.mDefUnparsed := quoteStr(defstr)
1095 else if hasdefId then self.mDefUnparsed := defstr
1096 else if hasdefInt then
1097 begin
1098 if (mType = TType.TPoint) then self.mDefUnparsed := Format('(%d %d)', [defint, defint2])
1099 else if (mType = TType.TSize) then self.mDefUnparsed := Format('[%d %d]', [defint, defint2])
1100 else self.mDefUnparsed := Format('%d', [defint]);
1101 end;
1103 self.mHasDefault := (hasdefStr or hasdefId or hasdefInt);
1104 self.mEBS := lebs;
1105 self.mEBSTypeName := fldrecname;
1106 self.mBitSetUnique := unique;
1107 self.mAsMonsterId := asmonid;
1108 self.mMaxDim := lmaxdim;
1109 self.mBinOfs := fldofs;
1110 self.mSepPosSize := (asxy or aswh);
1111 self.mAsT := ast;
1112 self.mWriteDef := writedef;
1113 self.mInternal := ainternal;
1114 self.mAlias := xalias;
1115 end;
1118 function TDynField.getRecRefIndex (): Integer;
1119 begin
1120 if (mRecRef = nil) then begin result := -1; exit; end;
1121 result := mOwner.findRecordNumByType(mEBSTypeName, mRecRef);
1122 end;
1125 procedure TDynField.writeBinTo (st: TStream);
1126 var
1127 s: AnsiString;
1128 f: Integer;
1129 maxv: Integer;
1130 buf: PByte;
1131 ws: TStream = nil;
1132 begin
1133 case mEBS of
1134 TEBS.TNone: begin end;
1135 TEBS.TRec:
1136 begin
1137 if (mMaxDim >= 0) then
1138 begin
1139 // this must be triggerdata
1140 if (mType <> TType.TTrigData) then
1141 begin
1142 raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
1143 end;
1144 // write triggerdata
1145 GetMem(buf, mMaxDim);
1146 if (buf = nil) then raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
1147 try
1148 FillChar(buf^, mMaxDim, 0);
1149 if (mRecRef <> nil) then
1150 begin
1151 ws := TSFSMemoryChunkStream.Create(buf, mMaxDim);
1152 mRecRef.writeBinTo(ws, mMaxDim); // as trigdata
1153 end;
1154 st.WriteBuffer(buf^, mMaxDim);
1155 finally
1156 ws.Free();
1157 if (buf <> nil) then FreeMem(buf);
1158 end;
1159 exit;
1160 end;
1161 // record reference
1162 case mType of
1163 TType.TByte: maxv := 127;
1164 TType.TUByte: maxv := 254;
1165 TType.TShort: maxv := 32767;
1166 TType.TUShort: maxv := 65534;
1167 TType.TInt: maxv := $7fffffff;
1168 TType.TUInt: maxv := $7fffffff;
1169 else raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
1170 end;
1171 // find record number
1172 if (mRecRef <> nil) then
1173 begin
1174 f := mOwner.findRecordNumByType(mEBSTypeName, mRecRef);
1175 if (f < 0) then raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' not found in record list', [mEBSTypeName, mName]));
1176 if mAsMonsterId then Inc(f);
1177 if (f > maxv) then raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' has too big index', [mEBSTypeName, mName]));
1178 end
1179 else
1180 begin
1181 if mAsMonsterId then f := 0 else f := -1;
1182 end;
1183 case mType of
1184 TType.TByte, TType.TUByte: writeInt(st, Byte(f));
1185 TType.TShort, TType.TUShort: writeInt(st, SmallInt(f));
1186 TType.TInt, TType.TUInt: writeInt(st, LongWord(f));
1187 else raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
1188 end;
1189 exit;
1190 end;
1191 TEBS.TEnum: begin end;
1192 TEBS.TBitSet: begin end;
1193 else raise Exception.Create('ketmar forgot to handle some EBS type');
1194 end;
1196 case mType of
1197 TType.TBool:
1198 begin
1199 if not mNegBool then
1200 begin
1201 if (mIVal <> 0) then writeInt(st, Byte(1)) else writeInt(st, Byte(0));
1202 end
1203 else
1204 begin
1205 if (mIVal = 0) then writeInt(st, Byte(1)) else writeInt(st, Byte(0));
1206 end;
1207 exit;
1208 end;
1209 TType.TChar:
1210 begin
1211 if (mMaxDim = 0) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1212 if (mMaxDim < 0) then
1213 begin
1214 if (Length(mSVal) <> 1) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1215 writeInt(st, Byte(mSVal[1]));
1216 end
1217 else
1218 begin
1219 if (Length(mSVal) > mMaxDim) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1220 s := utf2win(mSVal);
1221 if (Length(s) > 0) then st.WriteBuffer(PChar(s)^, Length(s));
1222 for f := Length(s) to mMaxDim do writeInt(st, Byte(0));
1223 end;
1224 exit;
1225 end;
1226 TType.TByte,
1227 TType.TUByte:
1228 begin
1229 // triggerdata array was processed earlier
1230 if (mMaxDim >= 0) then Exception.Create(Format('byte array in field ''%s'' cannot be written', [mName]));
1231 writeInt(st, Byte(mIVal));
1232 exit;
1233 end;
1234 TType.TShort,
1235 TType.TUShort:
1236 begin
1237 if (mMaxDim >= 0) then raise Exception.Create(Format('short array in field ''%s'' cannot be written', [mName]));
1238 writeInt(st, Word(mIVal));
1239 exit;
1240 end;
1241 TType.TInt,
1242 TType.TUInt:
1243 begin
1244 if (mMaxDim >= 0) then raise Exception.Create(Format('int array in field ''%s'' cannot be written', [mName]));
1245 writeInt(st, LongWord(mIVal));
1246 exit;
1247 end;
1248 TType.TString:
1249 begin
1250 raise Exception.Create(Format('cannot write string field ''%s''', [mName]));
1251 end;
1252 TType.TPoint:
1253 begin
1254 if (mMaxDim >= 0) then raise Exception.Create(Format('pos/size array in field ''%s'' cannot be written', [mName]));
1255 writeInt(st, LongInt(mIVal));
1256 writeInt(st, LongInt(mIVal2));
1257 exit;
1258 end;
1259 TType.TSize:
1260 begin
1261 if (mMaxDim >= 0) then raise Exception.Create(Format('pos/size array in field ''%s'' cannot be written', [mName]));
1262 writeInt(st, Word(mIVal));
1263 writeInt(st, Word(mIVal2));
1264 exit;
1265 end;
1266 TType.TList:
1267 begin
1268 assert(false);
1269 exit;
1270 end;
1271 TType.TTrigData:
1272 begin
1273 assert(false);
1274 exit;
1275 end;
1276 else raise Exception.Create('ketmar forgot to handle some field type');
1277 end;
1278 end;
1281 procedure TDynField.writeTo (wr: TTextWriter);
1282 var
1283 es: TDynEBS = nil;
1284 f, mask: Integer;
1285 first, found: Boolean;
1286 begin
1287 wr.put(mName);
1288 wr.put(' ');
1289 case mEBS of
1290 TEBS.TNone: begin end;
1291 TEBS.TRec:
1292 begin
1293 if (mRecRef = nil) then
1294 begin
1295 if (mType = TType.TTrigData) then wr.put('{}'#10) else wr.put('null;'#10);
1296 end
1297 else if (Length(mRecRef.mId) = 0) then
1298 begin
1299 mRecRef.writeTo(wr, false); // only data, no header
1300 end
1301 else
1302 begin
1303 wr.put(mRecRef.mId);
1304 wr.put(';'#10);
1305 end;
1306 exit;
1307 end;
1308 TEBS.TEnum:
1309 begin
1310 //def := mOwner.mOwner;
1311 //es := def.findEBSType(mEBSTypeName);
1312 es := nil;
1313 if (mEBSType <> nil) and (mEBSType is TDynEBS) then es := (mEBSType as TDynEBS);
1314 if (es = nil) or (not es.mIsEnum) then raise Exception.Create(Format('record enum type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1315 for f := 0 to High(es.mVals) do
1316 begin
1317 if (es.mVals[f] = mIVal) then
1318 begin
1319 wr.put(es.mIds[f]);
1320 wr.put(';'#10);
1321 exit;
1322 end;
1323 end;
1324 raise Exception.Create(Format('value %d in record enum type ''%s'' for field ''%s'' not found', [mIVal, mEBSTypeName, mName]));
1325 end;
1326 TEBS.TBitSet:
1327 begin
1328 //def := mOwner.mOwner;
1329 //es := def.findEBSType(mEBSTypeName);
1330 es := nil;
1331 if (mEBSType <> nil) and (mEBSType is TDynEBS) then es := (mEBSType as TDynEBS);
1332 if (es = nil) or es.mIsEnum then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1333 // none?
1334 if (mIVal = 0) then
1335 begin
1336 for f := 0 to High(es.mVals) do
1337 begin
1338 if (es.mVals[f] = 0) then
1339 begin
1340 wr.put(es.mIds[f]);
1341 wr.put(';'#10);
1342 exit;
1343 end;
1344 end;
1345 raise Exception.Create(Format('value %d in record bitset type ''%s'' for field ''%s'' not found', [0, mEBSTypeName, mName]));
1346 end;
1347 // not none
1348 mask := 1;
1349 first := true;
1350 while (mask <> 0) do
1351 begin
1352 if ((mIVal and mask) <> 0) then
1353 begin
1354 found := false;
1355 for f := 0 to High(es.mVals) do
1356 begin
1357 if (es.mVals[f] = mask) then
1358 begin
1359 if not first then wr.put('+') else first := false;
1360 wr.put(es.mIds[f]);
1361 found := true;
1362 break;
1363 end;
1364 end;
1365 if not found then raise Exception.Create(Format('value %d in record bitset type ''%s'' for field ''%s'' not found', [mask, mEBSTypeName, mName]));
1366 end;
1367 mask := mask shl 1;
1368 end;
1369 wr.put(';'#10);
1370 exit;
1371 end;
1372 else raise Exception.Create('ketmar forgot to handle some EBS type');
1373 end;
1375 case mType of
1376 TType.TBool:
1377 begin
1378 if (mIVal = 0) then wr.put('false;'#10) else wr.put('true;'#10);
1379 exit;
1380 end;
1381 TType.TChar:
1382 begin
1383 if (mMaxDim = 0) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1384 wr.put(quoteStr(mSVal));
1385 wr.put(';'#10);
1386 exit;
1387 end;
1388 TType.TByte,
1389 TType.TUByte,
1390 TType.TShort,
1391 TType.TUShort,
1392 TType.TInt,
1393 TType.TUInt:
1394 begin
1395 wr.put('%d;'#10, [mIVal]);
1396 exit;
1397 end;
1398 TType.TString:
1399 begin
1400 wr.put(quoteStr(mSVal));
1401 wr.put(';'#10);
1402 exit;
1403 end;
1404 TType.TPoint,
1405 TType.TSize:
1406 begin
1407 wr.put('(%d %d);'#10, [mIVal, mIVal2]);
1408 exit;
1409 end;
1410 TType.TList:
1411 begin
1412 assert(false);
1413 exit;
1414 end;
1415 TType.TTrigData:
1416 begin
1417 assert(false);
1418 exit;
1419 end;
1420 else raise Exception.Create('ketmar forgot to handle some field type');
1421 end;
1422 raise Exception.Create(Format('cannot parse field ''%s'' yet', [mName]));
1423 end;
1426 procedure TDynField.parseBinValue (st: TStream);
1427 var
1428 rec, rc: TDynRecord;
1429 tfld: TDynField;
1430 es: TDynEBS = nil;
1431 tdata: PByte = nil;
1432 f, mask: Integer;
1433 s: AnsiString;
1434 begin
1435 case mEBS of
1436 TEBS.TNone: begin end;
1437 TEBS.TRec:
1438 begin
1439 // this must be triggerdata
1440 if (mType = TType.TTrigData) then
1441 begin
1442 assert(mMaxDim > 0);
1443 rec := mOwner;
1444 // find trigger definition
1445 tfld := rec.trigTypeField();
1446 if (tfld = nil) then raise Exception.Create(Format('triggerdata value for field ''%s'' in record ''%s'' without TriggerType field', [mName, rec.mName]));
1447 rc := mOwner.mOwner.findTrigFor(tfld.mSVal); // find in mapdef
1448 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]));
1449 rc := rc.clone(mOwner.mHeaderRec);
1450 rc.mHeaderRec := mOwner.mHeaderRec;
1451 // on error, it will be freed be memowner
1452 rc.parseBinValue(st, true);
1453 mRecRef := rc;
1454 mDefined := true;
1455 exit;
1456 end
1457 else
1458 begin
1459 // not a trigger data
1460 case mType of
1461 TType.TByte: f := readShortInt(st);
1462 TType.TUByte: f := readByte(st);
1463 TType.TShort: f := readSmallInt(st);
1464 TType.TUShort: f := readWord(st);
1465 TType.TInt: f := readLongInt(st);
1466 TType.TUInt: f := readLongWord(st);
1467 else raise Exception.Create(Format('invalid non-numeric type ''%s'' for field ''%s'' of record ''%s''', [getTypeName(mType), mName, mEBSTypeName]));
1468 end;
1469 if mAsMonsterId then Dec(f);
1470 if (f < 0) then mRecRefId := '' else mRecRefId := Format('%s%d', [mEBSTypeName, f]);
1471 end;
1472 mDefined := true;
1473 exit;
1474 end;
1475 TEBS.TEnum,
1476 TEBS.TBitSet:
1477 begin
1478 assert(mMaxDim < 0);
1479 case mType of
1480 TType.TByte: f := readShortInt(st);
1481 TType.TUByte: f := readByte(st);
1482 TType.TShort: f := readSmallInt(st);
1483 TType.TUShort: f := readWord(st);
1484 TType.TInt: f := readLongInt(st);
1485 TType.TUInt: f := readLongWord(st);
1486 else raise Exception.Create(Format('invalid non-numeric type ''%s'' for field ''%s'' of record ''%s''', [getTypeName(mType), mName, mEBSTypeName]));
1487 end;
1488 es := nil;
1489 if (mEBSType <> nil) and (mEBSType is TDynEBS) then es := (mEBSType as TDynEBS);
1490 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]));
1491 mIVal := f;
1492 // build enum/bitfield values
1493 if (mEBS = TEBS.TEnum) then
1494 begin
1495 mSVal := es.nameByValue(mIVal);
1496 if (Length(mSVal) = 0) then raise Exception.Create(Format('record enum type ''%s'' for field ''%s'' has invalid value %d', [mEBSTypeName, mName, mIVal]));
1497 end
1498 else
1499 begin
1500 // special for 'none'
1501 if (mIVal = 0) then
1502 begin
1503 mSVal := es.nameByValue(mIVal);
1504 if (Length(mSVal) = 0) then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' has invalid value %d', [mEBSTypeName, mName, mIVal]));
1505 end
1506 else
1507 begin
1508 mSVal := '';
1509 mask := 1;
1510 while (mask <> 0) do
1511 begin
1512 if ((mIVal and mask) <> 0) then
1513 begin
1514 s := es.nameByValue(mask);
1515 if (Length(s) = 0) then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' has invalid value %d', [mEBSTypeName, mName, mask]));
1516 if (Length(mSVal) <> 0) then mSVal += '+';
1517 mSVal += s;
1518 end;
1519 mask := mask shl 1;
1520 end;
1521 end;
1522 end;
1523 //writeln('ebs <', es.mName, '>: ', mSVal);
1524 mDefined := true;
1525 exit;
1526 end;
1527 else raise Exception.Create('ketmar forgot to handle some EBS type');
1528 end;
1530 case mType of
1531 TType.TBool:
1532 begin
1533 f := readByte(st);
1534 if (f <> 0) then f := 1;
1535 if mNegBool then f := 1-f;
1536 mIVal := f;
1537 mDefined := true;
1538 exit;
1539 end;
1540 TType.TChar:
1541 begin
1542 if (mMaxDim < 0) then
1543 begin
1544 mIVal := readByte(st);
1545 end
1546 else
1547 begin
1548 mSVal := '';
1549 GetMem(tdata, mMaxDim);
1550 try
1551 st.ReadBuffer(tdata^, mMaxDim);
1552 f := 0;
1553 while (f < mMaxDim) and (tdata[f] <> 0) do Inc(f);
1554 if (f > 0) then
1555 begin
1556 SetLength(mSVal, f);
1557 Move(tdata^, PChar(mSVal)^, f);
1558 mSVal := win2utf(mSVal);
1559 end;
1560 finally
1561 FreeMem(tdata);
1562 end;
1563 end;
1564 mDefined := true;
1565 exit;
1566 end;
1567 TType.TByte: begin mIVal := readShortInt(st); mDefined := true; exit; end;
1568 TType.TUByte: begin mIVal := readByte(st); mDefined := true; exit; end;
1569 TType.TShort: begin mIVal := readSmallInt(st); mDefined := true; exit; end;
1570 TType.TUShort: begin mIVal := readWord(st); mDefined := true; exit; end;
1571 TType.TInt: begin mIVal := readLongInt(st); mDefined := true; exit; end;
1572 TType.TUInt: begin mIVal := readLongWord(st); mDefined := true; exit; end;
1573 TType.TString:
1574 begin
1575 raise Exception.Create('cannot read strings from binaries yet');
1576 exit;
1577 end;
1578 TType.TPoint:
1579 begin
1580 mIVal := readLongInt(st);
1581 mIVal2 := readLongInt(st);
1582 mDefined := true;
1583 exit;
1584 end;
1585 TType.TSize:
1586 begin
1587 mIVal := readWord(st);
1588 mIVal2 := readWord(st);
1589 mDefined := true;
1590 exit;
1591 end;
1592 TType.TList:
1593 begin
1594 assert(false);
1595 exit;
1596 end;
1597 TType.TTrigData:
1598 begin
1599 assert(false);
1600 exit;
1601 end;
1602 else raise Exception.Create('ketmar forgot to handle some field type');
1603 end;
1604 raise Exception.Create(Format('cannot parse field ''%s'' yet', [mName]));
1605 end;
1608 procedure TDynField.parseValue (pr: TTextParser);
1610 procedure parseInt (min, max: Integer);
1611 begin
1612 mIVal := pr.expectInt();
1613 if (mIVal < min) or (mIVal > max) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
1614 mDefined := true;
1615 end;
1617 var
1618 rec, rc: TDynRecord;
1619 es: TDynEBS = nil;
1620 tfld: TDynField;
1621 tk: AnsiString;
1622 edim: AnsiChar;
1623 begin
1624 if (pr.tokType = pr.TTEOF) then raise Exception.Create('field value expected');
1625 if (pr.tokType = pr.TTSemi) then raise Exception.Create('extra semicolon');
1626 // if this field should contain struct, convert type and parse struct
1627 case mEBS of
1628 TEBS.TNone: begin end;
1629 TEBS.TRec:
1630 begin
1631 // ugly hack. sorry.
1632 if (mType = TType.TTrigData) then
1633 begin
1634 pr.expectTT(pr.TTBegin);
1635 if (pr.tokType = pr.TTEnd) then
1636 begin
1637 // '{}'
1638 mRecRef := nil;
1639 pr.expectTT(pr.TTEnd);
1640 end
1641 else
1642 begin
1643 rec := mOwner;
1644 // find trigger definition
1645 tfld := rec.trigTypeField();
1646 if (tfld = nil) then raise Exception.Create(Format('triggerdata value for field ''%s'' in record ''%s'' without ''type'' field', [mName, rec.mName]));
1647 rc := mOwner.mOwner.findTrigFor(tfld.mSVal); // find in mapdef
1648 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]));
1649 rc := rc.clone(mOwner.mHeaderRec);
1650 rc.mHeaderRec := mOwner.mHeaderRec;
1651 //writeln(rc.definition);
1652 // on error, it will be freed be memowner
1653 rc.parseValue(pr, true);
1654 mRecRef := rc;
1655 end;
1656 mDefined := true;
1657 pr.eatTT(pr.TTSemi); // hack: allow (but don't require) semicolon after inline records
1658 exit;
1659 end;
1660 // other record types
1661 if (pr.tokType = pr.TTId) then
1662 begin
1663 if pr.eatId('null') then
1664 begin
1665 mRecRef := nil;
1666 end
1667 else
1668 begin
1669 rec := mOwner.findRecordByTypeId(mEBSTypeName, pr.tokStr);
1670 if (rec = nil) then
1671 begin
1672 //raise Exception.Create(Format('record ''%s'' (%s) value for field ''%s'' not found', [pr.tokStr, mEBSTypeName, mName]));
1673 mRecRefId := pr.tokStr;
1674 end
1675 else
1676 begin
1677 mRecRef := rec;
1678 mRecRefId := '';
1679 end;
1680 pr.expectId();
1681 end;
1682 mDefined := true;
1683 pr.expectTT(pr.TTSemi);
1684 exit;
1685 end
1686 else if (pr.tokType = pr.TTBegin) then
1687 begin
1688 //rec := mOwner.mOwner.findRecType(mEBSTypeName); // find in mapdef
1689 rec := nil;
1690 if (mEBSType <> nil) and (mEBSType is TDynRecord) then rec := (mEBSType as TDynRecord);
1691 if (rec = nil) then raise Exception.Create(Format('record type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1692 rc := rec.clone(mOwner.mHeaderRec);
1693 rc.mHeaderRec := mOwner.mHeaderRec;
1694 rc.parseValue(pr);
1695 mRecRef := rc;
1696 mDefined := true;
1697 if mOwner.addRecordByType(mEBSTypeName, rc) then
1698 begin
1699 //raise Exception.Create(Format('record type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1700 e_LogWritefln('duplicate record with id ''%s'' for field ''%s'' in record ''%s''', [rc.mId, mName, mOwner.mName]);
1701 end;
1702 pr.eatTT(pr.TTSemi); // hack: allow (but don't require) semicolon after inline records
1703 exit;
1704 end;
1705 pr.expectTT(pr.TTBegin);
1706 end;
1707 TEBS.TEnum:
1708 begin
1709 //es := mOwner.mOwner.findEBSType(mEBSTypeName); // find in mapdef
1710 es := nil;
1711 if (mEBSType <> nil) and (mEBSType is TDynEBS) then es := (mEBSType as TDynEBS);
1712 if (es = nil) or (not es.mIsEnum) then raise Exception.Create(Format('record enum type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1713 tk := pr.expectId();
1714 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]));
1715 mIVal := es.field[tk];
1716 mSVal := tk;
1717 //writeln('ENUM ', mEBSName, '; element <', mSVal, '> with value ', mIVal);
1718 mDefined := true;
1719 pr.expectTT(pr.TTSemi);
1720 exit;
1721 end;
1722 TEBS.TBitSet:
1723 begin
1724 //es := mOwner.mOwner.findEBSType(mEBSTypeName); // find in mapdef
1725 es := nil;
1726 if (mEBSType <> nil) and (mEBSType is TDynEBS) then es := (mEBSType as TDynEBS);
1727 if (es = nil) or es.mIsEnum then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1728 mIVal := 0;
1729 while true do
1730 begin
1731 tk := pr.expectId();
1732 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]));
1733 mIVal := mIVal or es.field[tk];
1734 mSVal := tk;
1735 if (pr.tokType <> pr.TTDelim) or ((pr.tokChar <> '|') and (pr.tokChar <> '+')) then break;
1736 if mBitSetUnique then raise Exception.Create(Format('record bitset of type ''%s'' for field ''%s'' expects only one value', [tk, mEBSTypeName, mName]));
1737 //pr.expectDelim('|');
1738 pr.skipToken(); // plus or pipe
1739 end;
1740 mDefined := true;
1741 pr.expectTT(pr.TTSemi);
1742 exit;
1743 end;
1744 else raise Exception.Create('ketmar forgot to handle some EBS type');
1745 end;
1747 case mType of
1748 TType.TBool:
1749 begin
1750 if pr.eatId('true') or pr.eatId('tan') or pr.eatId('yes') then mIVal := 1
1751 else if pr.eatId('false') or pr.eatId('ona') or pr.eatId('no') then mIVal := 0
1752 else raise Exception.Create(Format('invalid bool value for field ''%s''', [mName]));
1753 mDefined := true;
1754 pr.expectTT(pr.TTSemi);
1755 exit;
1756 end;
1757 TType.TChar:
1758 begin
1759 if (mMaxDim = 0) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1760 mSVal := pr.expectStr(true);
1761 if (mMaxDim < 0) then
1762 begin
1763 // single char
1764 if (Length(mSVal) <> 1) then raise Exception.Create(Format('invalid string size for field ''%s''', [mName]));
1765 mIVal := Integer(mSVal[1]);
1766 mSVal := '';
1767 end
1768 else
1769 begin
1770 // string
1771 if (Length(mSVal) > mMaxDim) then raise Exception.Create(Format('invalid string size for field ''%s''', [mName]));
1772 end;
1773 mDefined := true;
1774 pr.expectTT(pr.TTSemi);
1775 exit;
1776 end;
1777 TType.TByte:
1778 begin
1779 parseInt(-128, 127);
1780 pr.expectTT(pr.TTSemi);
1781 exit;
1782 end;
1783 TType.TUByte:
1784 begin
1785 parseInt(0, 255);
1786 pr.expectTT(pr.TTSemi);
1787 exit;
1788 end;
1789 TType.TShort:
1790 begin
1791 parseInt(-32768, 32768);
1792 pr.expectTT(pr.TTSemi);
1793 exit;
1794 end;
1795 TType.TUShort:
1796 begin
1797 parseInt(0, 65535);
1798 pr.expectTT(pr.TTSemi);
1799 exit;
1800 end;
1801 TType.TInt:
1802 begin
1803 parseInt(Integer($80000000), $7fffffff);
1804 pr.expectTT(pr.TTSemi);
1805 exit;
1806 end;
1807 TType.TUInt:
1808 begin
1809 parseInt(0, $7fffffff); //FIXME
1810 pr.expectTT(pr.TTSemi);
1811 exit;
1812 end;
1813 TType.TString:
1814 begin
1815 mSVal := pr.expectStr(true);
1816 mDefined := true;
1817 pr.expectTT(pr.TTSemi);
1818 exit;
1819 end;
1820 TType.TPoint,
1821 TType.TSize:
1822 begin
1823 if pr.eatDelim('[') then edim := ']' else begin pr.expectDelim('('); edim := ')'; end;
1824 mIVal := pr.expectInt();
1825 if (mType = TType.TSize) then
1826 begin
1827 if (mIVal < 0) or (mIVal > 32767) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
1828 end;
1829 mIVal2 := pr.expectInt();
1830 if (mType = TType.TSize) then
1831 begin
1832 if (mIVal2 < 0) or (mIVal2 > 32767) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
1833 end;
1834 mDefined := true;
1835 pr.expectDelim(edim);
1836 pr.expectTT(pr.TTSemi);
1837 exit;
1838 end;
1839 TType.TList:
1840 begin
1841 assert(false);
1842 exit;
1843 end;
1844 TType.TTrigData:
1845 begin
1846 assert(false);
1847 exit;
1848 end;
1849 else raise Exception.Create('ketmar forgot to handle some field type');
1850 end;
1851 raise Exception.Create(Format('cannot parse field ''%s'' yet', [mName]));
1852 end;
1855 // ////////////////////////////////////////////////////////////////////////// //
1856 constructor TDynRecord.Create (pr: TTextParser);
1857 begin
1858 if (pr = nil) then raise Exception.Create('cannot create record type without type definition');
1859 mId := '';
1860 mName := '';
1861 mSize := 0;
1862 mFields := TDynFieldList.Create();
1863 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1864 mFieldsHash := hashNewStrInt();
1865 {$ENDIF}
1866 mTrigTypes := nil;
1867 mHeader := false;
1868 mHeaderRec := nil;
1869 mBinBlock := -1;
1870 mTagInt := 0;
1871 mTagPtr := nil;
1872 parseDef(pr);
1873 end;
1876 constructor TDynRecord.Create ();
1877 begin
1878 mName := '';
1879 mSize := 0;
1880 mFields := TDynFieldList.Create();
1881 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1882 mFieldsHash := hashNewStrInt();
1883 {$ENDIF}
1884 mTrigTypes := nil;
1885 mHeader := false;
1886 mHeaderRec := nil;
1887 mTagInt := 0;
1888 mTagPtr := nil;
1889 mRec2Free := nil;
1890 end;
1893 destructor TDynRecord.Destroy ();
1894 var
1895 fld: TDynField;
1896 rec: TDynRecord;
1897 begin
1898 if (mRec2Free <> nil) then
1899 begin
1900 for rec in mRec2Free do
1901 begin
1902 if (rec <> self) then
1903 begin
1904 //writeln(formatstrf('freeing: 0x%08x; name=%s; id=%s', [Pointer(rec), rec.mName, rec.mId]));
1905 rec.Free();
1906 end;
1907 end;
1908 mRec2Free.Free();
1909 mRec2Free := nil;
1910 end;
1911 mName := '';
1912 for fld in mFields do fld.Free();
1913 mFields.Free();
1914 mFields := nil;
1915 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1916 mFieldsHash.Free();
1917 mFieldsHash := nil;
1918 {$ENDIF}
1919 mTrigTypes := nil;
1920 mHeaderRec := nil;
1921 mTagInt := 0;
1922 mTagPtr := nil;
1923 inherited;
1924 end;
1927 procedure TDynRecord.regrec (rec: TDynRecord);
1928 begin
1929 if (rec <> nil) and (rec <> self) then
1930 begin
1931 if (mRec2Free = nil) then mRec2Free := TDynRecList.Create();
1932 mRec2Free.append(rec);
1933 end;
1934 end;
1937 procedure TDynRecord.addField (fld: TDynField); inline;
1938 begin
1939 if (fld = nil) then raise Exception.Create('cannot append nil field to record');
1940 mFields.append(fld);
1941 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1942 if (Length(fld.mName) > 0) then mFieldsHash.put(fld.mName, mFields.count-1);
1943 {$ENDIF}
1944 end;
1947 function TDynRecord.addFieldChecked (fld: TDynField): Boolean; inline; // `true`: duplicate name
1948 begin
1949 result := false;
1950 if (fld = nil) then raise Exception.Create('cannot append nil field to record');
1951 {$IF not DEFINED(XDYNREC_USE_FIELDHASH)}
1952 if (Length(fld.mName) > 0) then result := hasByName(fld.mName);
1953 {$ENDIF}
1954 mFields.append(fld);
1955 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1956 if (Length(fld.mName) > 0) then result := mFieldsHash.put(fld.mName, mFields.count-1);
1957 {$ENDIF}
1958 end;
1961 function TDynRecord.findByName (const aname: AnsiString): Integer; inline;
1962 begin
1963 {$IF DEFINED(XDYNREC_USE_FIELDHASH)}
1964 if not mFieldsHash.get(aname, result) then result := -1;
1965 {$ELSE}
1966 result := 0;
1967 while (result < mFields.count) do
1968 begin
1969 if StrEqu(aname, mFields[result].mName) then exit;
1970 Inc(result);
1971 end;
1972 result := -1;
1973 {$ENDIF}
1974 end;
1977 function TDynRecord.hasByName (const aname: AnsiString): Boolean; inline;
1978 begin
1979 result := (findByName(aname) >= 0);
1980 end;
1983 function TDynRecord.getFieldByName (const aname: AnsiString): TDynField; inline;
1984 var
1985 f: Integer;
1986 begin
1987 f := findByName(aname);
1988 if (f >= 0) then result := mFields[f] else result := nil;
1989 end;
1992 function TDynRecord.getFieldAt (idx: Integer): TDynField; inline;
1993 begin
1994 if (idx >= 0) and (idx < mFields.count) then result := mFields[idx] else result := nil;
1995 end;
1998 function TDynRecord.getCount (): Integer; inline;
1999 begin
2000 result := mFields.count;
2001 end;
2004 function TDynRecord.getIsTrigData (): Boolean; inline;
2005 begin
2006 result := (Length(mTrigTypes) > 0);
2007 end;
2010 function TDynRecord.getIsForTrig (const aname: AnsiString): Boolean; inline;
2011 var
2012 f: Integer;
2013 begin
2014 result := true;
2015 for f := 0 to High(mTrigTypes) do if StrEqu(mTrigTypes[f], aname) then exit;
2016 result := false;
2017 end;
2020 function TDynRecord.getForTrigCount (): Integer; inline;
2021 begin
2022 result := Length(mTrigTypes);
2023 end;
2026 function TDynRecord.getForTrigAt (idx: Integer): AnsiString; inline;
2027 begin
2028 if (idx >= 0) and (idx < Length(mTrigTypes)) then result := mTrigTypes[idx] else result := '';
2029 end;
2032 function TDynRecord.clone (registerIn: TDynRecord): TDynRecord;
2033 var
2034 fld: TDynField;
2035 f: Integer;
2036 begin
2037 result := TDynRecord.Create();
2038 result.mOwner := mOwner;
2039 result.mId := mId;
2040 result.mName := mName;
2041 result.mSize := mSize;
2042 result.mHeader := mHeader;
2043 result.mBinBlock := mBinBlock;
2044 result.mHeaderRec := mHeaderRec;
2045 result.mTagInt := mTagInt;
2046 result.mTagPtr := mTagPtr;
2047 if (mFields.count > 0) then
2048 begin
2049 result.mFields.capacity := mFields.count;
2050 for fld in mFields do result.addField(fld.clone(result, registerIn));
2051 end;
2052 SetLength(result.mTrigTypes, Length(mTrigTypes));
2053 for f := 0 to High(mTrigTypes) do result.mTrigTypes[f] := mTrigTypes[f];
2054 if (registerIn <> nil) then registerIn.regrec(result);
2055 end;
2058 function TDynRecord.findRecordByTypeId (const atypename, aid: AnsiString): TDynRecord;
2059 var
2060 fld: TDynField;
2061 idx: Integer;
2062 begin
2063 result := nil;
2064 if (Length(aid) = 0) then exit;
2065 // find record data
2066 fld := mHeaderRec.field[atypename];
2067 if (fld = nil) then exit;
2068 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]));
2069 // find by id
2070 if (fld.mRVal <> nil) then
2071 begin
2072 if fld.mRHash.get(aid, idx) then begin result := fld.mRVal[idx]; exit; end;
2073 end;
2074 // alas
2075 end;
2078 function TDynRecord.findRecordNumByType (const atypename: AnsiString; rc: TDynRecord): Integer;
2079 var
2080 fld: TDynField;
2081 idx: Integer;
2082 begin
2083 result := -1;
2084 // find record data
2085 fld := mHeaderRec.field[atypename];
2086 if (fld = nil) then exit;
2087 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]));
2088 // find by ref
2089 if (fld.mRVal <> nil) then
2090 begin
2091 for idx := 0 to fld.mRVal.count-1 do
2092 begin
2093 if (fld.mRVal[idx] = rc) then begin result := idx; exit; end;
2094 end;
2095 end;
2096 // alas
2097 end;
2100 function TDynRecord.addRecordByType (const atypename: AnsiString; rc: TDynRecord): Boolean;
2101 var
2102 fld: TDynField;
2103 begin
2104 // find record data
2105 fld := mHeaderRec.field[atypename];
2106 if (fld = nil) then
2107 begin
2108 // first record
2109 fld := TDynField.Create(atypename, TDynField.TType.TList);
2110 fld.mOwner := mHeaderRec;
2111 mHeaderRec.addField(fld);
2112 end;
2113 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]));
2114 // append
2115 if (fld.mRVal = nil) then
2116 begin
2117 fld.mRVal := TDynRecList.Create();
2118 fld.mRHash := hashNewStrInt();
2119 end;
2120 result := fld.addListItem(rc);
2121 end;
2124 function TDynRecord.isSimpleEqu (rec: TDynRecord): Boolean;
2125 var
2126 f: Integer;
2127 begin
2128 if (rec = nil) then begin result := false; exit; end; // self.mRecRef can't be `nil` here
2129 if (rec = self) then begin result := true; exit; end;
2130 if (mFields.count <> rec.mFields.count) then begin result := false; exit; end;
2131 result := false;
2132 for f := 0 to mFields.count-1 do
2133 begin
2134 if not mFields[f].isSimpleEqu(rec.mFields[f]) then exit;
2135 end;
2136 result := true;
2137 end;
2140 function TDynRecord.trigTypeField (): TDynField;
2141 var
2142 fld: TDynField;
2143 es: TDynEBS = nil;
2144 begin
2145 for fld in mFields do
2146 begin
2147 if (fld.mEBS <> TDynField.TEBS.TEnum) then continue;
2148 if not (fld.mEBSType is TDynEBS) then continue;
2149 es := (fld.mEBSType as TDynEBS);
2150 assert(es <> nil);
2151 if StrEqu(es.mName, 'TriggerType') then begin result := fld; exit; end;
2152 end;
2153 result := nil;
2154 end;
2157 // number of records of the given instance
2158 function TDynRecord.instanceCount (const typename: AnsiString): Integer;
2159 var
2160 fld: TDynField;
2161 begin
2162 result := 0;
2163 fld := field[typename];
2164 if (fld <> nil) and (fld.mType = fld.TType.TList) then result := fld.mRVal.count;
2165 end;
2168 function TDynRecord.getUserVar (const aname: AnsiString): Variant;
2169 var
2170 fld: TDynField;
2171 begin
2172 fld := getFieldByName(aname);
2173 if (fld = nil) then result := Unassigned else result := fld.varvalue;
2174 end;
2177 procedure TDynRecord.setUserVar (const aname: AnsiString; val: Variant);
2178 var
2179 fld: TDynField;
2180 begin
2181 fld := getFieldByName(aname);
2182 if (fld = nil) then
2183 begin
2184 if (Length(aname) = 0) then raise Exception.Create('cannot create nameless user field');
2185 fld := TDynField.Create(aname, val);
2186 fld.mOwner := self;
2187 fld.mInternal := true;
2188 addField(fld);
2189 end
2190 else
2191 begin
2192 fld.varvalue := val;
2193 end;
2194 end;
2197 procedure TDynRecord.parseDef (pr: TTextParser);
2198 var
2199 fld: TDynField;
2200 tdn: AnsiString;
2201 begin
2202 if pr.eatId('TriggerData') then
2203 begin
2204 pr.expectId('for');
2205 if pr.eatDelim('(') then
2206 begin
2207 while true do
2208 begin
2209 while pr.eatTT(pr.TTComma) do begin end;
2210 if pr.eatDelim(')') then break;
2211 tdn := pr.expectId();
2212 if isForTrig[tdn] then raise Exception.Create(Format('duplicate trigdata ''%s'' trigtype ''%s''', [mName, tdn]));
2213 SetLength(mTrigTypes, Length(mTrigTypes)+1);
2214 mTrigTypes[High(mTrigTypes)] := tdn;
2215 end;
2216 end
2217 else
2218 begin
2219 tdn := pr.expectId();
2220 SetLength(mTrigTypes, 1);
2221 mTrigTypes[0] := tdn;
2222 end;
2223 mName := 'TriggerData';
2224 end
2225 else
2226 begin
2227 mName := pr.expectStrOrId();
2228 while (pr.tokType <> pr.TTBegin) do
2229 begin
2230 if pr.eatId('header') then begin mHeader := true; continue; end;
2231 if pr.eatId('size') then
2232 begin
2233 if (mSize > 0) then raise Exception.Create(Format('duplicate `size` in record ''%s''', [mName]));
2234 mSize := pr.expectInt();
2235 if (mSize < 1) then raise Exception.Create(Format('invalid record ''%s'' size: %d', [mName, mSize]));
2236 pr.expectId('bytes');
2237 continue;
2238 end;
2239 if pr.eatId('binblock') then
2240 begin
2241 if (mBinBlock >= 0) then raise Exception.Create(Format('duplicate `binblock` in record ''%s''', [mName]));
2242 mBinBlock := pr.expectInt();
2243 if (mBinBlock < 1) then raise Exception.Create(Format('invalid record ''%s'' binblock: %d', [mName, mBinBlock]));
2244 continue;
2245 end;
2246 end;
2247 end;
2249 pr.expectTT(pr.TTBegin);
2250 // load fields
2251 while (pr.tokType <> pr.TTEnd) do
2252 begin
2253 fld := TDynField.Create(pr);
2254 //if hasByName(fld.name) then begin fld.Free(); raise Exception.Create(Format('duplicate field ''%s''', [fld.name])); end;
2255 // append
2256 fld.mOwner := self;
2257 if addFieldChecked(fld) then
2258 begin
2259 fld.Free();
2260 raise Exception.Create(Format('duplicate field ''%s''', [fld.name]));
2261 end;
2262 // done with field
2263 end;
2264 pr.expectTT(pr.TTEnd);
2265 end;
2268 function TDynRecord.definition (): AnsiString;
2269 var
2270 f: Integer;
2271 begin
2272 if isTrigData then
2273 begin
2274 // trigger data
2275 result := 'TriggerData for ';
2276 if (Length(mTrigTypes) > 1) then
2277 begin
2278 result += '(';
2279 for f := 0 to High(mTrigTypes) do
2280 begin
2281 if (f <> 0) then result += ', ';
2282 result += mTrigTypes[f];
2283 end;
2284 result += ')';
2285 end
2286 else
2287 begin
2288 result += mTrigTypes[0];
2289 end;
2290 end
2291 else
2292 begin
2293 // record
2294 result := quoteStr(mName);
2295 if (mSize >= 0) then result += Format(' size %d bytes', [mSize]);
2296 if mHeader then result += ' header';
2297 end;
2298 result += ' {'#10;
2299 for f := 0 to mFields.count-1 do
2300 begin
2301 result += ' ';
2302 result += mFields[f].definition;
2303 result += ';'#10;
2304 end;
2305 result += '}';
2306 end;
2309 procedure TDynRecord.parseBinValue (st: TStream; forceData: Boolean=false);
2310 var
2311 sign: string[4];
2312 btype: Integer;
2313 bsize: Integer;
2314 buf: PByte = nil;
2315 loaded: array[0..255] of Boolean;
2316 rec, rect: TDynRecord;
2317 fld: TDynField;
2318 f: Integer;
2319 mst: TSFSMemoryChunkStream = nil;
2321 procedure linkNames (rec: TDynRecord);
2322 var
2323 fld: TDynField;
2324 rt: TDynRecord;
2325 begin
2326 //writeln('*** rec: ', rec.mName, '.', rec.mId, ' (', rec.mFields.count, ')');
2327 for fld in rec.mFields do
2328 begin
2329 if (fld.mType = TDynField.TType.TTrigData) then
2330 begin
2331 if (fld.mRecRef <> nil) then linkNames(fld.mRecRef);
2332 continue;
2333 end;
2334 if (Length(fld.mRecRefId) = 0) then continue;
2335 assert(fld.mEBSType <> nil);
2336 rt := findRecordByTypeId(fld.mEBSTypeName, fld.mRecRefId);
2337 if (rt = nil) then
2338 begin
2339 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);
2340 //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]));
2341 end;
2342 //writeln(' ', rec.mName, '.', rec.mId, ':', fld.mName, ' -> ', rt.mName, '.', rt.mId, ' (', fld.mEBSTypeName, '.', fld.mRecRefId, ')');
2343 fld.mRecRefId := '';
2344 fld.mRecRef := rt;
2345 fld.mDefined := true;
2346 end;
2347 for fld in rec.mFields do
2348 begin
2349 //writeln(' ', fld.mName);
2350 fld.fixDefaultValue(); // just in case
2351 end;
2352 end;
2354 begin
2355 for f := 0 to High(loaded) do loaded[f] := false;
2356 mst := TSFSMemoryChunkStream.Create(nil, 0);
2357 try
2358 if mHeader and not forceData then
2359 begin
2360 // parse map file as sequence of blocks
2361 sign[0] := #4;
2362 st.ReadBuffer(sign[1], 4);
2363 if (sign <> 'MAP'#1) then raise Exception.Create('invalid binary map signature');
2364 // parse blocks
2365 while (st.position < st.size) do
2366 begin
2367 btype := readByte(st);
2368 if (btype = 0) then break; // no more blocks
2369 readLongWord(st); // reserved
2370 bsize := readLongInt(st);
2371 {$IF DEFINED(D2D_XDYN_DEBUG)}writeln('btype=', btype, '; bsize=', bsize);{$ENDIF}
2372 if (bsize < 0) or (bsize > $1fffffff) then raise Exception.Create(Format('block of type %d has invalid size %d', [btype, bsize]));
2373 if loaded[btype] then raise Exception.Create(Format('block of type %d already loaded', [btype]));
2374 loaded[btype] := true;
2375 // find record type for this block
2376 rect := nil;
2377 for rec in mOwner.recTypes do if (rec.mBinBlock = btype) then begin rect := rec; break; end;
2378 if (rect = nil) then raise Exception.Create(Format('block of type %d has no corresponding record', [btype]));
2379 //writeln('found type ''', rec.mName, ''' for block type ', btype);
2380 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]));
2381 // header?
2382 if (rect.mHeader) then
2383 begin
2384 if (bsize <> mSize) then raise Exception.Create(Format('header block of type %d has invalid number of records', [btype]));
2385 GetMem(buf, bsize);
2386 st.ReadBuffer(buf^, bsize);
2387 mst.setup(buf, mSize);
2388 parseBinValue(mst, true); // force parsing data
2389 end
2390 else
2391 begin
2392 // create list for this type
2393 fld := TDynField.Create(rec.mName, TDynField.TType.TList);
2394 fld.mOwner := self;
2395 addField(fld);
2396 if (bsize > 0) then
2397 begin
2398 GetMem(buf, bsize);
2399 st.ReadBuffer(buf^, bsize);
2400 for f := 0 to (bsize div rec.mSize)-1 do
2401 begin
2402 mst.setup(buf+f*rec.mSize, rec.mSize);
2403 rec := rect.clone(self);
2404 rec.mHeaderRec := self;
2405 rec.parseBinValue(mst);
2406 rec.mId := Format('%s%d', [rec.mName, f]);
2407 fld.addListItem(rec);
2408 //writeln('parsed ''', rec.mId, '''...');
2409 end;
2410 end;
2411 end;
2412 FreeMem(buf);
2413 buf := nil;
2414 //st.position := st.position+bsize;
2415 end;
2416 // link fields
2417 for fld in mFields do
2418 begin
2419 if (fld.mType <> TDynField.TType.TList) then continue;
2420 for rec in fld.mRVal do linkNames(rec);
2421 end;
2422 exit;
2423 end;
2425 // read fields
2426 if StrEqu(mName, 'TriggerData') then mSize := Integer(st.size-st.position);
2427 if (mSize < 1) then raise Exception.Create(Format('cannot read record of type ''%s'' with unknown size', [mName]));
2428 GetMem(buf, mSize);
2429 st.ReadBuffer(buf^, mSize);
2430 for fld in mFields do
2431 begin
2432 if fld.mInternal then continue;
2433 if (fld.mBinOfs < 0) then continue;
2434 if (fld.mBinOfs >= st.size) then raise Exception.Create(Format('record of type ''%s'' has invalid field ''%s''', [fld.mName]));
2435 mst.setup(buf+fld.mBinOfs, mSize-fld.mBinOfs);
2436 //writeln('parsing ''', mName, '.', fld.mName, '''...');
2437 fld.parseBinValue(mst);
2438 end;
2439 finally
2440 mst.Free();
2441 if (buf <> nil) then FreeMem(buf);
2442 end;
2443 end;
2446 procedure TDynRecord.writeBinTo (st: TStream; trigbufsz: Integer=-1; onlyFields: Boolean=false);
2447 var
2448 fld: TDynField;
2449 rec, rv: TDynRecord;
2450 buf: PByte = nil;
2451 ws: TStream = nil;
2452 blk, blkmax: Integer;
2453 //f, c: Integer;
2454 bufsz: Integer = 0;
2455 blksz: Integer;
2456 begin
2457 if (trigbufsz < 0) then
2458 begin
2459 if (mBinBlock < 1) then raise Exception.Create('cannot write binary record without block number');
2460 if (mSize < 1) then raise Exception.Create('cannot write binary record without size');
2461 bufsz := mSize;
2462 end
2463 else
2464 begin
2465 bufsz := trigbufsz;
2466 end;
2467 try
2468 GetMem(buf, bufsz);
2469 FillChar(buf^, bufsz, 0);
2470 ws := TSFSMemoryChunkStream.Create(buf, bufsz);
2472 // write normal fields
2473 for fld in mFields do
2474 begin
2475 // record list?
2476 if (fld.mType = fld.TType.TList) then continue; // later
2477 if fld.mInternal then continue;
2478 if (fld.mBinOfs < 0) then continue;
2479 if (fld.mBinOfs >= bufsz) then raise Exception.Create('binary value offset is outside of the buffer');
2480 TSFSMemoryChunkStream(ws).setup(buf+fld.mBinOfs, bufsz-fld.mBinOfs);
2481 //writeln('writing field <', fld.mName, '>');
2482 fld.writeBinTo(ws);
2483 end;
2485 // write block with normal fields
2486 if mHeader and not onlyFields then
2487 begin
2488 //writeln('writing header...');
2489 // signature and version
2490 writeIntBE(st, LongWord($4D415001));
2491 writeInt(st, Byte(mBinBlock)); // type
2492 writeInt(st, LongWord(0)); // reserved
2493 writeInt(st, LongWord(bufsz)); // size
2494 end;
2495 st.WriteBuffer(buf^, bufsz);
2497 ws.Free(); ws := nil;
2498 FreeMem(buf); buf := nil;
2500 // write other blocks, if any
2501 if mHeader and not onlyFields then
2502 begin
2503 // calculate blkmax
2504 blkmax := 0;
2505 for fld in mFields do
2506 begin
2507 // record list?
2508 if (fld.mType = fld.TType.TList) then
2509 begin
2510 if (fld.mRVal = nil) or (fld.mRVal.count = 0) then continue;
2511 rec := mOwner.findRecType(fld.mName);
2512 if (rec = nil) then continue;
2513 if (rec.mBinBlock <= 0) then continue;
2514 if (blkmax < rec.mBinBlock) then blkmax := rec.mBinBlock;
2515 end;
2516 end;
2517 // write blocks
2518 for blk := 1 to blkmax do
2519 begin
2520 if (blk = mBinBlock) then continue;
2521 ws := nil;
2522 for fld in mFields do
2523 begin
2524 // record list?
2525 if (fld.mType = fld.TType.TList) then
2526 begin
2527 if (fld.mRVal = nil) or (fld.mRVal.count = 0) then continue;
2528 rec := mOwner.findRecType(fld.mName);
2529 if (rec = nil) then continue;
2530 if (rec.mBinBlock <> blk) then continue;
2531 if (ws = nil) then ws := TMemoryStream.Create();
2532 for rv in fld.mRVal do rv.writeBinTo(ws);
2533 end;
2534 end;
2535 // flush block
2536 if (ws <> nil) then
2537 begin
2538 blksz := Integer(ws.position);
2539 ws.position := 0;
2540 writeInt(st, Byte(blk)); // type
2541 writeInt(st, LongWord(0)); // reserved
2542 writeInt(st, LongWord(blksz)); // size
2543 st.CopyFrom(ws, blksz);
2544 ws.Free();
2545 ws := nil;
2546 end;
2547 end;
2548 // write end marker
2549 writeInt(st, Byte(0));
2550 writeInt(st, LongWord(0));
2551 writeInt(st, LongWord(0));
2552 end;
2553 finally
2554 ws.Free();
2555 if (buf <> nil) then FreeMem(buf);
2556 end;
2557 end;
2560 procedure TDynRecord.writeTo (wr: TTextWriter; putHeader: Boolean=true);
2561 var
2562 fld: TDynField;
2563 rec: TDynRecord;
2564 begin
2565 if putHeader then
2566 begin
2567 wr.put(mName);
2568 if (Length(mId) > 0) then begin wr.put(' '); wr.put(mId); end;
2569 wr.put(' ');
2570 end;
2571 wr.put('{'#10);
2572 wr.indent();
2573 try
2574 for fld in mFields do
2575 begin
2576 // record list?
2577 if (fld.mType = fld.TType.TList) then
2578 begin
2579 if not mHeader then raise Exception.Create('record list in non-header record');
2580 if (fld.mRVal <> nil) then
2581 begin
2582 for rec in fld.mRVal do
2583 begin
2584 if (Length(rec.mId) = 0) then continue;
2585 wr.putIndent();
2586 rec.writeTo(wr, true);
2587 end;
2588 end;
2589 continue;
2590 end;
2591 if fld.mInternal then continue;
2592 if (not fld.mWriteDef) and fld.isDefaultValue then continue;
2593 wr.putIndent();
2594 fld.writeTo(wr);
2595 end;
2596 finally
2597 wr.unindent();
2598 end;
2599 wr.putIndent();
2600 wr.put('}'#10);
2601 end;
2604 {$IF DEFINED(D2D_DYNREC_PROFILER)}
2605 var
2606 profCloneRec: UInt64 = 0;
2607 profFindRecType: UInt64 = 0;
2608 profFieldSearching: UInt64 = 0;
2609 profListDupChecking: UInt64 = 0;
2610 profAddRecByType: UInt64 = 0;
2611 profFieldValParsing: UInt64 = 0;
2612 profFixDefaults: UInt64 = 0;
2613 profRecValParse: UInt64 = 0;
2615 procedure xdynDumpProfiles ();
2616 begin
2617 writeln('=== XDYNREC PROFILES ===');
2618 writeln('record cloning: ', profCloneRec div 1000, '.', profCloneRec mod 1000, ' milliseconds');
2619 writeln('findRecType : ', profFindRecType div 1000, '.', profFindRecType mod 1000, ' milliseconds');
2620 writeln('field[] : ', profFieldSearching div 1000, '.', profFieldSearching mod 1000, ' milliseconds');
2621 writeln('list dup check: ', profListDupChecking div 1000, '.', profListDupChecking mod 1000, ' milliseconds');
2622 writeln('addRecByType : ', profAddRecByType div 1000, '.', profAddRecByType mod 1000, ' milliseconds');
2623 writeln('field valparse: ', profFieldValParsing div 1000, '.', profFieldValParsing mod 1000, ' milliseconds');
2624 writeln('fix defaults : ', profFixDefaults div 1000, '.', profFixDefaults mod 1000, ' milliseconds');
2625 writeln('recvalparse : ', profRecValParse div 1000, '.', profRecValParse mod 1000, ' milliseconds');
2626 end;
2627 {$ENDIF}
2630 procedure TDynRecord.parseValue (pr: TTextParser; beginEaten: Boolean=false);
2631 var
2632 fld: TDynField;
2633 rec: TDynRecord = nil;
2634 trc{, rv}: TDynRecord;
2635 {$IF DEFINED(D2D_DYNREC_PROFILER)}
2636 stt, stall: UInt64;
2637 {$ENDIF}
2639 procedure linkNames (rec: TDynRecord);
2640 var
2641 fld: TDynField;
2642 rt: TDynRecord;
2643 begin
2644 //writeln('*** rec: ', rec.mName, '.', rec.mId, ' (', rec.mFields.count, ')');
2645 for fld in rec.mFields do
2646 begin
2647 if (fld.mType = TDynField.TType.TTrigData) then
2648 begin
2649 if (fld.mRecRef <> nil) then linkNames(fld.mRecRef);
2650 continue;
2651 end;
2652 if (Length(fld.mRecRefId) = 0) then continue;
2653 assert(fld.mEBSType <> nil);
2654 rt := findRecordByTypeId(fld.mEBSTypeName, fld.mRecRefId);
2655 if (rt = nil) then
2656 begin
2657 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);
2658 //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]));
2659 end;
2660 //writeln(' ', rec.mName, '.', rec.mId, ':', fld.mName, ' -> ', rt.mName, '.', rt.mId, ' (', fld.mEBSTypeName, '.', fld.mRecRefId, ')');
2661 fld.mRecRefId := '';
2662 fld.mRecRef := rt;
2663 fld.mDefined := true;
2664 end;
2665 for fld in rec.mFields do
2666 begin
2667 //writeln(' ', fld.mName);
2668 fld.fixDefaultValue(); // just in case
2669 end;
2670 end;
2672 begin
2673 if (mOwner = nil) then raise Exception.Create(Format('can''t parse record ''%s'' value without owner', [mName]));
2675 {$IF DEFINED(D2D_DYNREC_PROFILER)}stall := curTimeMicro();{$ENDIF}
2677 // not a header?
2678 if not mHeader then
2679 begin
2680 // id?
2681 if (not beginEaten) and (pr.tokType = pr.TTId) then mId := pr.expectId();
2682 end
2683 else
2684 begin
2685 assert(mHeaderRec = self);
2686 end;
2688 //writeln('parsing record <', mName, '>');
2689 if not beginEaten then pr.expectTT(pr.TTBegin);
2690 while (pr.tokType <> pr.TTEnd) do
2691 begin
2692 if (pr.tokType <> pr.TTId) then raise Exception.Create('identifier expected');
2693 //writeln('<', mName, '.', pr.tokStr, '>');
2695 // records
2696 if mHeader then
2697 begin
2698 // add records with this type (if any)
2699 {$IF DEFINED(D2D_DYNREC_PROFILER)}stt := curTimeMicro();{$ENDIF}
2700 trc := mOwner.findRecType(pr.tokStr);
2701 {$IF DEFINED(D2D_DYNREC_PROFILER)}profFindRecType := curTimeMicro()-stt;{$ENDIF}
2702 if (trc <> nil) then
2703 begin
2704 {$IF DEFINED(D2D_DYNREC_PROFILER)}stt := curTimeMicro();{$ENDIF}
2705 rec := trc.clone(mHeaderRec);
2706 {$IF DEFINED(D2D_DYNREC_PROFILER)}profCloneRec := curTimeMicro()-stt;{$ENDIF}
2707 rec.mHeaderRec := mHeaderRec;
2708 // on error, it will be freed be memowner
2709 pr.skipToken();
2710 rec.parseValue(pr);
2711 (*
2712 if (Length(rec.mId) > 0) then
2713 begin
2714 {$IF DEFINED(D2D_DYNREC_PROFILER)}stt := curTimeMicro();{$ENDIF}
2715 fld := field[pr.tokStr];
2716 {$IF DEFINED(D2D_DYNREC_PROFILER)}profFieldSearching := curTimeMicro()-stt;{$ENDIF}
2717 (*
2718 if (fld <> nil) and (fld.mRVal <> nil) then
2719 begin
2720 {$IF DEFINED(D2D_DYNREC_PROFILER)}stt := curTimeMicro();{$ENDIF}
2721 //idtmp := trc.mName+':'+rec.mId;
2722 //if ids.put(idtmp, 1) then raise Exception.Create(Format('duplicate thing ''%s'' in record ''%s''', [fld.mName, mName]));
2723 if fld.mRHash.has(rec.mId) then raise Exception.Create(Format('duplicate thing ''%s'' in record ''%s''', [fld.mName, mName]));
2724 {$IF DEFINED(D2D_DYNREC_PROFILER)}profListDupChecking := curTimeMicro()-stt;{$ENDIF}
2725 end;
2726 end;
2727 *)
2728 {$IF DEFINED(D2D_DYNREC_PROFILER)}stt := curTimeMicro();{$ENDIF}
2729 addRecordByType(rec.mName, rec);
2730 {$IF DEFINED(D2D_DYNREC_PROFILER)}profAddRecByType := curTimeMicro()-stt;{$ENDIF}
2731 continue;
2732 end;
2733 end;
2735 // fields
2736 {$IF DEFINED(D2D_DYNREC_PROFILER)}stt := curTimeMicro();{$ENDIF}
2737 //writeln('0: <', mName, '.', pr.tokStr, '>');
2738 fld := field[pr.tokStr];
2739 //writeln('1: <', mName, '.', pr.tokStr, '>');
2740 {$IF DEFINED(D2D_DYNREC_PROFILER)}profFieldSearching := curTimeMicro()-stt;{$ENDIF}
2741 if (fld <> nil) then
2742 begin
2743 //writeln('2: <', mName, '.', pr.tokStr, '>');
2744 if fld.defined then raise Exception.Create(Format('duplicate field ''%s'' in record ''%s''', [fld.mName, mName]));
2745 if fld.internal then raise Exception.Create(Format('internal field ''%s'' in record ''%s''', [fld.mName, mName]));
2746 pr.skipToken(); // skip field name
2747 //writeln('3: <', mName, '.', pr.tokStr, '>:', pr.tokType);
2748 {$IF DEFINED(D2D_DYNREC_PROFILER)}stt := curTimeMicro();{$ENDIF}
2749 fld.parseValue(pr);
2750 {$IF DEFINED(D2D_DYNREC_PROFILER)}profFieldValParsing := curTimeMicro()-stt;{$ENDIF}
2751 continue;
2752 end;
2754 // something is wrong
2755 raise Exception.Create(Format('unknown field ''%s'' in record ''%s''', [pr.tokStr, mName]));
2756 end;
2757 pr.expectTT(pr.TTEnd);
2759 if mHeader then
2760 begin
2761 // link fields
2762 for fld in mFields do
2763 begin
2764 if (fld.mType <> TDynField.TType.TList) then continue;
2765 for rec in fld.mRVal do linkNames(rec);
2766 end;
2767 end;
2769 // fix field defaults
2770 {$IF DEFINED(D2D_DYNREC_PROFILER)}stt := curTimeMicro();{$ENDIF}
2771 for fld in mFields do fld.fixDefaultValue();
2772 {$IF DEFINED(D2D_DYNREC_PROFILER)}profFixDefaults := curTimeMicro()-stt;{$ENDIF}
2773 //writeln('done parsing record <', mName, '>');
2774 //{$IF DEFINED(D2D_DYNREC_PROFILER)}writeln('stall: ', curTimeMicro()-stall);{$ENDIF}
2775 {$IF DEFINED(D2D_DYNREC_PROFILER)}profRecValParse := curTimeMicro()-stall;{$ENDIF}
2776 end;
2779 // ////////////////////////////////////////////////////////////////////////// //
2780 constructor TDynEBS.Create (pr: TTextParser);
2781 begin
2782 cleanup();
2783 parseDef(pr);
2784 end;
2787 destructor TDynEBS.Destroy ();
2788 begin
2789 cleanup();
2790 inherited;
2791 end;
2794 procedure TDynEBS.cleanup ();
2795 begin
2796 mIsEnum := false;
2797 mName := '';
2798 mIds := nil;
2799 mVals := nil;
2800 mMaxName := '';
2801 mMaxVal := 0;
2802 end;
2805 function TDynEBS.findByName (const aname: AnsiString): Integer;
2806 begin
2807 result := 0;
2808 while (result < Length(mIds)) do
2809 begin
2810 if StrEqu(aname, mIds[result]) then exit;
2811 Inc(result);
2812 end;
2813 result := -1;
2814 end;
2817 function TDynEBS.hasByName (const aname: AnsiString): Boolean; inline;
2818 begin
2819 result := (findByName(aname) >= 0);
2820 end;
2823 function TDynEBS.getFieldByName (const aname: AnsiString): Integer; inline;
2824 var
2825 f: Integer;
2826 begin
2827 f := findByName(aname);
2828 if (f >= 0) then result := mVals[f] else result := 0;
2829 end;
2832 function TDynEBS.definition (): AnsiString;
2833 var
2834 f, cv: Integer;
2835 begin
2836 if mIsEnum then result :='enum ' else result := 'bitset ';
2837 result += mName;
2838 result += ' {'#10;
2839 // fields
2840 if mIsEnum then cv := 0 else cv := 1;
2841 for f := 0 to High(mIds) do
2842 begin
2843 if (mIds[f] = mMaxName) then continue;
2844 result += ' '+mIds[f];
2845 if (mVals[f] <> cv) then
2846 begin
2847 result += Format(' = %d', [mVals[f]]);
2848 if mIsEnum then cv := mVals[f];
2849 result += ','#10;
2850 end
2851 else
2852 begin
2853 result += Format(', // %d'#10, [mVals[f]]);
2854 end;
2855 if mIsEnum then Inc(cv) else if (mVals[f] = cv) then cv := cv shl 1;
2856 end;
2857 // max field
2858 if (Length(mMaxName) > 0) then result += ' '+mMaxName+' = MAX,'#10;
2859 result += '}';
2860 end;
2863 function TDynEBS.pasdef (): AnsiString;
2864 var
2865 f: Integer;
2866 begin
2867 result := '// '+mName+#10'const'#10;
2868 // fields
2869 for f := 0 to High(mIds) do
2870 begin
2871 result += formatstrf(' %s = %d;'#10, [mIds[f], mVals[f]]);
2872 end;
2873 end;
2876 function TDynEBS.nameByValue (v: Integer): AnsiString;
2877 var
2878 f: Integer;
2879 begin
2880 for f := 0 to High(mVals) do
2881 begin
2882 if (mVals[f] = v) then begin result := mIds[f]; exit; end;
2883 end;
2884 result := '';
2885 end;
2888 procedure TDynEBS.parseDef (pr: TTextParser);
2889 var
2890 idname: AnsiString;
2891 cv, v: Integer;
2892 f: Integer;
2893 skipAdd: Boolean;
2894 hasV: Boolean;
2895 begin
2896 if pr.eatId('enum') then mIsEnum := true
2897 else if pr.eatId('bitset') then mIsEnum := false
2898 else pr.expectId('enum');
2899 mName := pr.expectId();
2900 mMaxVal := Integer($80000000);
2901 if mIsEnum then cv := 0 else cv := 1;
2902 pr.expectTT(pr.TTBegin);
2903 while (pr.tokType <> pr.TTEnd) do
2904 begin
2905 idname := pr.expectId();
2906 for f := 0 to High(mIds) do
2907 begin
2908 if StrEqu(mIds[f], idname) then raise Exception.Create(Format('duplicate field ''%s'' in enum/bitset ''%s''', [idname, mName]));
2909 end;
2910 if StrEqu(mMaxName, idname) then raise Exception.Create(Format('duplicate field ''%s'' in enum/bitset ''%s''', [idname, mName]));
2911 skipAdd := false;
2912 hasV := false;
2913 v := cv;
2914 // has value?
2915 if pr.eatDelim('=') then
2916 begin
2917 if pr.eatId('MAX') then
2918 begin
2919 if (Length(mMaxName) > 0) then raise Exception.Create(Format('duplicate max field ''%s'' in enum/bitset ''%s''', [idname, mName]));
2920 mMaxName := idname;
2921 skipAdd := true;
2922 end
2923 else
2924 begin
2925 v := pr.expectInt();
2926 if mIsEnum then cv := v;
2927 hasV := true;
2928 end;
2929 end;
2930 // append it?
2931 if not skipAdd then
2932 begin
2933 // fix maxvalue
2934 if mIsEnum or (not hasV) then
2935 begin
2936 if (mMaxVal < v) then mMaxVal := v;
2937 end;
2938 SetLength(mIds, Length(mIds)+1);
2939 mIds[High(mIds)] := idname;
2940 SetLength(mVals, Length(mIds));
2941 mVals[High(mVals)] := v;
2942 // next cv
2943 if mIsEnum or (not hasV) then
2944 begin
2945 if mIsEnum then Inc(cv) else cv := cv shl 1;
2946 end;
2947 end;
2948 if (pr.tokType = pr.TTEnd) then break;
2949 pr.expectTT(pr.TTComma);
2950 while pr.eatTT(pr.TTComma) do begin end;
2951 end;
2952 pr.expectTT(pr.TTEnd);
2953 // add max field
2954 if (Length(mMaxName) > 0) then
2955 begin
2956 SetLength(mIds, Length(mIds)+1);
2957 mIds[High(mIds)] := mMaxName;
2958 SetLength(mVals, Length(mIds));
2959 mVals[High(mVals)] := mMaxVal;
2960 end;
2961 end;
2964 // ////////////////////////////////////////////////////////////////////////// //
2965 constructor TDynMapDef.Create (pr: TTextParser);
2966 begin
2967 recTypes := TDynRecList.Create();
2968 trigTypes := TDynRecList.Create();
2969 ebsTypes := TDynEBSList.Create();
2970 parseDef(pr);
2971 end;
2974 destructor TDynMapDef.Destroy ();
2975 var
2976 rec: TDynRecord;
2977 ebs: TDynEBS;
2978 begin
2979 //!!!FIXME!!! check who owns trigs and recs!
2980 for rec in recTypes do rec.Free();
2981 for rec in trigTypes do rec.Free();
2982 for ebs in ebsTypes do ebs.Free();
2983 recTypes.Free();
2984 trigTypes.Free();
2985 ebsTypes.Free();
2986 recTypes := nil;
2987 trigTypes := nil;
2988 ebsTypes := nil;
2989 inherited;
2990 end;
2993 function TDynMapDef.getHeaderRecType (): TDynRecord; inline;
2994 begin
2995 if (recTypes.count = 0) then raise Exception.Create('no header in empty mapdef');
2996 result := recTypes[0];
2997 end;
3000 function TDynMapDef.findRecType (const aname: AnsiString): TDynRecord;
3001 var
3002 rec: TDynRecord;
3003 begin
3004 for rec in recTypes do
3005 begin
3006 if StrEqu(rec.name, aname) then begin result := rec; exit; end;
3007 end;
3008 result := nil;
3009 end;
3012 function TDynMapDef.findTrigFor (const aname: AnsiString): TDynRecord;
3013 var
3014 rec: TDynRecord;
3015 begin
3016 for rec in trigTypes do
3017 begin
3018 if (rec.isForTrig[aname]) then begin result := rec; exit; end;
3019 end;
3020 result := nil;
3021 end;
3024 function TDynMapDef.findEBSType (const aname: AnsiString): TDynEBS;
3025 var
3026 ebs: TDynEBS;
3027 begin
3028 for ebs in ebsTypes do
3029 begin
3030 if StrEqu(ebs.name, aname) then begin result := ebs; exit; end;
3031 end;
3032 result := nil;
3033 end;
3036 procedure TDynMapDef.parseDef (pr: TTextParser);
3037 var
3038 rec, hdr: TDynRecord;
3039 eb: TDynEBS;
3040 f: Integer;
3042 // setup header links and type links
3043 procedure linkRecord (rec: TDynRecord);
3044 var
3045 fld: TDynField;
3046 begin
3047 rec.mHeaderRec := recTypes[0];
3048 for fld in rec.mFields do
3049 begin
3050 if (fld.mType = fld.TType.TTrigData) then continue;
3051 case fld.mEBS of
3052 TDynField.TEBS.TNone: begin end;
3053 TDynField.TEBS.TRec:
3054 begin
3055 fld.mEBSType := findRecType(fld.mEBSTypeName);
3056 if (fld.mEBSType = nil) then raise Exception.Create(Format('field ''%s'' of type ''%s'' has no correcponding record definition', [fld.mName, fld.mEBSTypeName]));
3057 end;
3058 TDynField.TEBS.TEnum,
3059 TDynField.TEBS.TBitSet:
3060 begin
3061 fld.mEBSType := findEBSType(fld.mEBSTypeName);
3062 if (fld.mEBSType = nil) then raise Exception.Create(Format('field ''%s'' of type ''%s'' has no correcponding enum/bitset', [fld.mName, fld.mEBSTypeName]));
3063 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 end;
3065 end;
3066 end;
3067 end;
3069 // setup default values
3070 procedure fixRecordDefaults (rec: TDynRecord);
3071 var
3072 fld: TDynField;
3073 begin
3074 for fld in rec.mFields do if fld.mHasDefault then fld.parseDefaultValue();
3075 end;
3077 begin
3078 hdr := nil;
3079 while true do
3080 begin
3081 if not pr.skipBlanks() then break;
3083 if (pr.tokType = pr.TTId) then
3084 begin
3085 // enum or bitset
3086 if (pr.tokStr = 'enum') or (pr.tokStr = 'bitset') then
3087 begin
3088 eb := TDynEBS.Create(pr);
3089 if (findEBSType(eb.name) <> nil) then
3090 begin
3091 eb.Free();
3092 raise Exception.Create(Format('duplicate enum/bitset ''%s''', [eb.name]));
3093 end;
3094 eb.mOwner := self;
3095 ebsTypes.append(eb);
3096 //writeln(eb.definition); writeln;
3097 continue;
3098 end;
3100 // triggerdata
3101 if (pr.tokStr = 'TriggerData') then
3102 begin
3103 rec := TDynRecord.Create(pr);
3104 for f := 0 to High(rec.mTrigTypes) do
3105 begin
3106 if (findTrigFor(rec.mTrigTypes[f]) <> nil) then
3107 begin
3108 rec.Free();
3109 raise Exception.Create(Format('duplicate trigdata ''%s''', [rec.mTrigTypes[f]]));
3110 end;
3111 end;
3112 rec.mOwner := self;
3113 trigTypes.append(rec);
3114 //writeln(dr.definition); writeln;
3115 continue;
3116 end;
3117 end;
3119 rec := TDynRecord.Create(pr);
3120 //writeln(dr.definition); writeln;
3121 if (findRecType(rec.name) <> nil) then begin rec.Free(); raise Exception.Create(Format('duplicate record ''%s''', [rec.name])); end;
3122 if (hdr <> nil) and StrEqu(rec.name, hdr.name) then begin rec.Free(); raise Exception.Create(Format('duplicate record ''%s''', [rec.name])); end;
3123 rec.mOwner := self;
3124 if rec.mHeader then
3125 begin
3126 if (hdr <> nil) then begin rec.Free(); raise Exception.Create(Format('duplicate header record ''%s'' (previous is ''%s'')', [rec.name, hdr.name])); end;
3127 hdr := rec;
3128 end
3129 else
3130 begin
3131 recTypes.append(rec);
3132 end;
3133 end;
3135 // put header record to top
3136 if (hdr = nil) then raise Exception.Create('header definition not found in mapdef');
3137 recTypes.append(nil);
3138 for f := recTypes.count-1 downto 1 do recTypes[f] := recTypes[f-1];
3139 recTypes[0] := hdr;
3141 // setup header links and type links
3142 for rec in recTypes do linkRecord(rec);
3143 for rec in trigTypes do linkRecord(rec);
3145 // setup default values
3146 for rec in recTypes do fixRecordDefaults(rec);
3147 for rec in trigTypes do fixRecordDefaults(rec);
3148 end;
3151 // ////////////////////////////////////////////////////////////////////////// //
3152 function TDynMapDef.parseMap (pr: TTextParser): TDynRecord;
3153 var
3154 res: TDynRecord = nil;
3155 begin
3156 result := nil;
3157 try
3158 pr.expectId(headerType.name);
3159 res := headerType.clone(nil);
3160 res.mHeaderRec := res;
3161 res.parseValue(pr);
3162 result := res;
3163 res := nil;
3164 finally
3165 res.Free();
3166 end;
3167 end;
3170 function TDynMapDef.parseBinMap (st: TStream): TDynRecord;
3171 var
3172 res: TDynRecord = nil;
3173 begin
3174 result := nil;
3175 try
3176 res := headerType.clone(nil);
3177 res.mHeaderRec := res;
3178 res.parseBinValue(st);
3179 result := res;
3180 res := nil;
3181 finally
3182 res.Free();
3183 end;
3184 end;
3187 function TDynMapDef.pasdefconst (): AnsiString;
3188 var
3189 ebs: TDynEBS;
3190 begin
3191 result := '';
3192 result += '// ////////////////////////////////////////////////////////////////////////// //'#10;
3193 result += '// enums and bitsets'#10;
3194 for ebs in ebsTypes do result += #10+ebs.pasdef();
3195 end;
3198 function TDynMapDef.getTrigTypeCount (): Integer; inline; begin result := trigTypes.count; end;
3199 function TDynMapDef.getTrigTypeAt (idx: Integer): TDynRecord; inline; begin if (idx >= 0) and (idx < trigTypes.count) then result := trigTypes[idx] else result := nil; end;
3202 end.