1 (* Copyright (C) DooM 2D:Forever Developers
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.
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.
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/>.
16 {$INCLUDE a_modes.inc}
26 // ////////////////////////////////////////////////////////////////////////// //
31 // this is base type for all scalars (and arrays)
35 TType
= (TBool
, TChar
, TByte
, TUByte
, TShort
, TUShort
, TInt
, TUInt
, TString
, TPoint
, TSize
, TList
, TTrigData
);
36 // TPoint: pair of Shorts
37 // TSize: pair of UShorts
38 // TList: actually, array of records
39 // TTrigData: array of bytes
40 // arrays of chars are pascal shortstrings (with counter in the first byte)
43 TDynFieldArray
= array of TDynField
;
44 TDynRecordArray
= array of TDynRecord
;
48 TEBS
= (TNone
, TRec
, TEnum
, TBitSet
);
55 mIVal
: Integer; // for all integer types
56 mIVal2
: Integer; // for point and size
57 mSVal
: AnsiString; // string; for byte and char arrays
58 mRVal
: TDynRecordArray
; // for list
59 mRecRef
: TDynRecord
; // for record
60 //mRecRefOwned: Boolean; // was mRecRef created from inline definition?
61 mMaxDim
: Integer; // for byte and char arrays; <0: not an array
62 mBinOfs
: Integer; // offset in binary; <0 - none
63 mRecOfs
: Integer; // offset in record; <0 - none
64 mSepPosSize
: Boolean; // for points and sizes, use separate fields
65 mAsT
: Boolean; // for points and sizes, use separate fields, names starts with `t`
68 mDefaultValueSet
: Boolean;
74 mEBSTypeName
: AnsiString; // name of enum, bitset or record
75 mBitSetUnique
: Boolean; // bitset can contain only one value
84 procedure parseDef (pr
: TTextParser
);
86 procedure setIVal (v
: Integer); inline;
87 procedure setSVal (const v
: AnsiString); inline;
89 procedure fixDefaultValue ();
90 function isDefaultValue (): Boolean;
93 constructor Create (const aname
: AnsiString; atype
: TType
);
94 constructor Create (pr
: TTextParser
);
95 destructor Destroy (); override;
97 class function getTypeName (t
: TType
): AnsiString;
99 function definition (): AnsiString;
101 function clone (): TDynField
;
103 procedure parseValue (pr
: TTextParser
);
104 procedure parseBinValue (st
: TStream
);
106 procedure writeTo (wr
: TTextWriter
);
107 procedure writeBinTo (st
: TStream
);
109 // won't work for lists
110 function isSimpleEqu (fld
: TDynField
): Boolean;
113 property pasname
: AnsiString read mPasName
;
114 property name
: AnsiString read mName
;
115 property baseType
: TType read mType
;
116 property defined
: Boolean read mDefined write mDefined
;
117 property internal
: Boolean read mInternal write mInternal
;
118 property ival
: Integer read mIVal write setIVal
;
119 property sval
: AnsiString read mSVal write setSVal
;
120 property list
: TDynRecordArray read mRVal write mRVal
;
121 property maxdim
: Integer read mMaxDim
; // for fixed-size arrays
122 property binOfs
: Integer read mBinOfs
; // offset in binary; <0 - none
123 property recOfs
: Integer read mRecOfs
; // offset in record; <0 - none
124 property hasDefault
: Boolean read mHasDefault
;
125 property defsval
: AnsiString read mDefSVal write mDefSVal
;
126 property ebs
: TEBS read mEBS write mEBS
;
127 property ebstypename
: AnsiString read mEBSTypeName write mEBSTypeName
; // enum/bitset name
129 property x
: Integer read mIVal
;
130 property w
: Integer read mIVal
;
131 property y
: Integer read mIVal2
;
132 property h
: Integer read mIVal2
;
140 mPasName
: AnsiString;
143 mFields
: TDynField
.TDynFieldArray
;
144 mTrigTypes
: array of AnsiString; // if this is triggerdata, we'll hold list of triggers here
145 mHeader
: Boolean; // true for header record
146 mBinBlock
: Integer; // -1: none
149 procedure parseDef (pr
: TTextParser
); // parse definition
151 function findByName (const aname
: AnsiString): Integer; inline;
152 function hasByName (const aname
: AnsiString): Boolean; inline;
153 function getFieldByName (const aname
: AnsiString): TDynField
; inline;
155 function getIsTrigData (): Boolean; inline;
156 function getIsForTrig (const aname
: AnsiString): Boolean; inline;
159 constructor Create ();
160 constructor Create (pr
: TTextParser
); // parse definition
161 destructor Destroy (); override;
163 function definition (): AnsiString;
165 function clone (): TDynRecord
;
167 procedure parseValue (pr
: TTextParser
; asheader
: Boolean=false);
168 procedure parseBinValue (st
: TStream
);
170 procedure writeTo (wr
: TTextWriter
; putHeader
: Boolean=true);
171 procedure writeBinTo (st
: TStream
; trigbufsz
: Integer=-1);
174 property id
: AnsiString read mId
; // for map parser
175 property pasname
: AnsiString read mPasName
;
176 property name
: AnsiString read mName
; // record name
177 property size
: Integer read mSize
; // size in bytes
178 property fields
: TDynField
.TDynFieldArray read mFields write mFields
;
179 property has
[const aname
: AnsiString]: Boolean read hasByName
;
180 property field
[const aname
: AnsiString]: TDynField read getFieldByName
;
181 property isTrigData
: Boolean read getIsTrigData
;
182 property isForTrig
[const aname
: AnsiString]: Boolean read getIsForTrig
;
191 mIds
: array of AnsiString;
192 mVals
: array of Integer;
193 mMaxName
: AnsiString; // MAX field
194 mMaxVal
: Integer; // max value
197 procedure cleanup ();
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): Integer; inline;
206 constructor Create (pr
: TTextParser
); // parse definition
207 destructor Destroy (); override;
209 function definition (): AnsiString;
212 property name
: AnsiString read mName
; // record name
213 property isEnum
: Boolean read mIsEnum
;
214 property has
[const aname
: AnsiString]: Boolean read hasByName
;
215 property field
[const aname
: AnsiString]: Integer read getFieldByName
;
221 curheader
: TDynRecord
; // for parser
224 procedure addRecordByType (const atypename
: AnsiString; rc
: TDynRecord
);
225 function findRecordByTypeId (const atypename
, aid
: AnsiString): TDynRecord
;
226 function findRecordNumByType (const atypename
: AnsiString; rc
: TDynRecord
): Integer;
229 records
: array of TDynRecord
; // [0] is always header
230 trigDatas
: array of TDynRecord
;
231 ebs
: array of TDynEBS
;
234 procedure parseDef (pr
: TTextParser
);
236 function getHeader (): TDynRecord
; inline;
239 constructor Create (pr
: TTextParser
); // parses data definition
240 destructor Destroy (); override;
242 function findRec (const aname
: AnsiString): TDynRecord
;
243 function findTrigDataFor (const aname
: AnsiString): TDynRecord
;
244 function findEBS (const aname
: AnsiString): TDynEBS
;
246 function parseMap (pr
: TTextParser
): TDynRecord
;
248 function parseBinMap (st
: TStream
): TDynRecord
;
251 property header
: TDynRecord read getHeader
;
262 // ////////////////////////////////////////////////////////////////////////// //
263 constructor TDynField
.Create (const aname
: AnsiString; atype
: TType
);
267 //mRecRefOwned := false;
274 constructor TDynField
.Create (pr
: TTextParser
);
281 destructor TDynField
.Destroy ();
288 procedure TDynField
.cleanup ();
296 //if mRecRefOwned then mRecRef.Free();
298 //mRecRefOwned := false;
302 mSepPosSize
:= false;
304 mHasDefault
:= false;
311 mBitSetUnique
:= false;
314 mDefaultValueSet
:= false;
318 function TDynField
.clone (): TDynField
;
322 result
:= TDynField
.Create(mName
, mType
);
323 result
.mOwner
:= mOwner
;
324 result
.mPasName
:= mPasName
;
325 result
.mName
:= mName
;
326 result
.mType
:= mType
;
327 result
.mIVal
:= mIVal
;
328 result
.mIVal2
:= mIVal2
;
329 result
.mSVal
:= mSVal
;
330 SetLength(result
.mRVal
, Length(mRVal
));
331 for f
:= 0 to High(mRVal
) do result
.mRVal
[f
] := mRVal
[f
].clone();
332 result
.mRecRef
:= mRecRef
;
334 result.mRecRefOwned := mRecRefOwned;
337 if (mRecRef <> nil) then result.mRecRef := mRecRef.clone();
341 result.mRecRef := mRecRef;
344 result
.mMaxDim
:= mMaxDim
;
345 result
.mBinOfs
:= mBinOfs
;
346 result
.mRecOfs
:= mRecOfs
;
347 result
.mSepPosSize
:= mSepPosSize
;
349 result
.mDefined
:= mDefined
;
350 result
.mHasDefault
:= mHasDefault
;
351 result
.mOmitDef
:= mOmitDef
;
352 result
.mInternal
:= mInternal
;
353 result
.mDefSVal
:= mDefSVal
;
355 result
.mEBSTypeName
:= mEBSTypeName
;
356 result
.mBitSetUnique
:= mBitSetUnique
;
357 result
.mNegBool
:= mNegBool
;
358 result
.mDefId
:= mDefId
;
359 result
.mDefaultValueSet
:= mDefaultValueSet
;
363 procedure TDynField
.setIVal (v
: Integer); inline; begin mIVal
:= v
; mDefined
:= true; end;
364 procedure TDynField
.setSVal (const v
: AnsiString); inline; begin mSVal
:= v
; mDefined
:= true; end;
367 // won't work for lists
368 function TDynField
.isSimpleEqu (fld
: TDynField
): Boolean;
370 if (fld
= nil) or (mType
<> fld
.mType
) then begin result
:= false; exit
; end;
372 TType
.TBool
: result
:= ((mIVal
<> 0) = (fld
.mIVal
<> 0));
373 TType
.TChar
: result
:= (mSVal
= fld
.mSVal
);
380 result
:= (mIVal
= fld
.mIVal
);
381 TType
.TString
: result
:= (mSVal
= fld
.mSVal
);
384 result
:= ((mIVal
= fld
.mIVal
) and (mIVal2
= fld
.mIVal2
));
385 TType
.TList
: result
:= false;
386 TType
.TTrigData
: result
:= false;
387 else raise Exception
.Create('ketmar forgot to handle some field type');
392 procedure TDynField
.fixDefaultValue ();
399 if not mHasDefault
then
401 if mInternal
then exit
;
402 raise Exception
.Create(Format('field ''%s'' in record ''%s'' of record type ''%s'' is not set', [mName
, mOwner
.mId
, mOwner
.mName
]));
404 if (mEBS
= TEBS
.TRec
) then
406 if (CompareText(mDefSVal
, 'null') <> 0) then raise Exception
.Create(Format('field ''%s'' in record ''%s'' of record type ''%s'' has non-null default value ''%s''', [mName
, mOwner
.mId
, mOwner
.mName
, mDefSVal
]));
408 assert(mRecRef
= nil);
409 mDefaultValueSet
:= true;
414 TType
.TChar
, TType
.TString
: s
:= TTextParser
.quote(mDefSVal
)+';';
415 TType
.TPoint
, TType
.TSize
: assert(false); // no default values for these types yet
416 else s
:= mDefSVal
+';';
419 //writeln('DEFAULT for <', mName, '>: <', s, '>');
420 stp
:= TStrTextParser
.Create(s
);
427 mDefaultValueSet
:= true;
432 function TDynField
.isDefaultValue (): Boolean;
434 fld
: TDynField
= nil;
435 stp
: TTextParser
= nil;
438 if not mHasDefault
then begin result
:= false; exit
; end;
439 //result := mDefaultValueSet;
440 if (mEBS
= TEBS
.TRec
) then begin result
:= (mRecRef
= nil); exit
; end;
443 TType
.TChar
, TType
.TString
: s
:= TTextParser
.quote(mDefSVal
)+';';
444 TType
.TPoint
, TType
.TSize
: begin result
:= false; exit
; end; // no default values for these types yet
445 else s
:= mDefSVal
+';';
447 stp
:= TStrTextParser
.Create(s
);
451 result
:= isSimpleEqu(fld
);
459 class function TDynField
.getTypeName (t
: TType
): AnsiString;
462 TType
.TBool
: result
:= 'bool';
463 TType
.TChar
: result
:= 'char';
464 TType
.TByte
: result
:= 'byte';
465 TType
.TUByte
: result
:= 'ubyte';
466 TType
.TShort
: result
:= 'short';
467 TType
.TUShort
: result
:= 'ushort';
468 TType
.TInt
: result
:= 'int';
469 TType
.TUInt
: result
:= 'uint';
470 TType
.TString
: result
:= 'string';
471 TType
.TPoint
: result
:= 'point';
472 TType
.TSize
: result
:= 'size';
473 TType
.TList
: result
:= 'array';
474 TType
.TTrigData
: result
:= 'trigdata';
475 else raise Exception
.Create('ketmar forgot to handle some field type');
480 function TDynField
.definition (): AnsiString;
482 result
:= mPasName
+' is '+TTextParser
.quote(mName
)+' type ';
483 result
+= getTypeName(mType
);
484 if (mMaxDim
>= 0) then result
+= Format('[%d]', [mMaxDim
]);
485 if (mRecOfs
>= 0) then result
+= Format(' offset %d', [mRecOfs
]);
487 TEBS
.TNone
: begin end;
488 TEBS
.TRec
: result
+= ' '+mEBSTypeName
;
489 TEBS
.TEnum
: result
+= ' enum '+mEBSTypeName
;
490 TEBS
.TBitSet
: begin result
+= ' bitset '; if mBitSetUnique
then result
+= 'unique '; result
+= mEBSTypeName
; end;
494 if (mType
= TType
.TChar
) or (mType
= TType
.TString
) then result
+= ' default '+TTextParser
.quote(mDefSVal
)
495 else if (Length(mDefSVal
) > 0) then result
+= ' default '+mDefSVal
;
499 if (mType = TType.TBool) then
501 result += ' default ';
502 if (mDefIVal <> 0) then result += 'true' else result += 'false';
506 result += Format(' default %d', [mDefIVal]);
513 if (mType
= TType
.TPoint
) then begin if (mAsT
) then result
+= ' as txy' else result
+= ' as xy'; end
514 else if (mType
= TType
.TSize
) then begin if (mAsT
) then result
+= ' as twh' else result
+= ' as wh'; end;
516 if mOmitDef
then result
+= ' omitdefault';
517 if mInternal
then result
+= ' internal';
521 procedure TDynField
.parseDef (pr
: TTextParser
);
526 fldrecname
: AnsiString;
527 fldpasname
: AnsiString;
528 asxy
, aswh
, ast
: Boolean;
537 lebs
: TDynField
.TEBS
;
557 lebs
:= TDynField
.TEBS
.TNone
;
559 fldpasname
:= pr
.expectId(); // pascal field name
562 fldname
:= pr
.expectStr();
565 fldtype
:= pr
.expectId();
568 if pr
.eatDelim('[') then
570 lmaxdim
:= pr
.expectInt();
571 if (lmaxdim
< 1) then raise Exception
.Create(Format('invali field ''%s'' array size', [fldname
]));
575 while (pr
.tokType
<> pr
.TTSemi
) do
577 if pr
.eatId('offset') then
579 if (fldofs
>= 0) then raise Exception
.Create(Format('duplicate field ''%s'' offset', [fldname
]));
580 fldofs
:= pr
.expectInt();
581 if (fldofs
< 0) then raise Exception
.Create(Format('invalid field ''%s'' offset', [fldname
]));
585 if pr
.eatId('as') then
587 if pr
.eatId('xy') then asxy
:= true
588 else if pr
.eatId('wh') then aswh
:= true
589 else if pr
.eatId('txy') then begin asxy
:= true; ast
:= true; end
590 else if pr
.eatId('twh') then begin aswh
:= true; ast
:= true; end
591 else raise Exception
.Create(Format('invalid field ''%s'' as what?', [fldname
]));
595 if pr
.eatId('enum') then
597 lebs
:= TDynField
.TEBS
.TEnum
;
598 if (Length(fldrecname
) <> 0) then raise Exception
.Create(Format('field ''%s'' already typed as ''%s''', [fldname
, fldrecname
]));
599 fldrecname
:= pr
.expectId();
603 if pr
.eatId('bitset') then
605 lebs
:= TDynField
.TEBS
.TBitSet
;
606 if (Length(fldrecname
) <> 0) then raise Exception
.Create(Format('field ''%s'' already typed as ''%s''', [fldname
, fldrecname
]));
607 unique
:= pr
.eatId('unique');
608 fldrecname
:= pr
.expectId();
612 if pr
.eatId('default') then
614 if hasdefStr
or hasdefInt
or hasdefId
then raise Exception
.Create(Format('field ''%s'' has duplicate default', [fldname
]));
619 defstr
:= pr
.expectStr(true); // allow empty strings
624 defstr
:= pr
.expectId();
629 defint
:= pr
.expectInt();
632 raise Exception
.Create(Format('field ''%s'' has invalid default', [fldname
]));
637 if pr
.eatId('omitdefault') then
643 if pr
.eatId('internal') then
649 if (pr
.tokType
<> pr
.TTId
) then raise Exception
.Create(Format('field ''%s'' has something unexpected in definition', [fldname
]));
651 if (Length(fldrecname
) <> 0) then raise Exception
.Create(Format('field ''%s'' already typed as ''%s''', [fldname
, fldrecname
]));
652 fldrecname
:= pr
.expectId();
653 lebs
:= TDynField
.TEBS
.TRec
;
656 pr
.expectTT(pr
.TTSemi
);
660 if (fldtype
= 'bool') then mType
:= TType
.TBool
661 else if (fldtype
= 'negbool') then begin mType
:= TType
.TBool
; mNegBool
:= true; end
662 else if (fldtype
= 'char') then mType
:= TType
.TChar
663 else if (fldtype
= 'byte') then mType
:= TType
.TByte
664 else if (fldtype
= 'ubyte') then mType
:= TType
.TUByte
665 else if (fldtype
= 'short') then mType
:= TType
.TShort
666 else if (fldtype
= 'ushort') then mType
:= TType
.TUShort
667 else if (fldtype
= 'int') then mType
:= TType
.TInt
668 else if (fldtype
= 'uint') then mType
:= TType
.TUInt
669 else if (fldtype
= 'string') then mType
:= TType
.TString
670 else if (fldtype
= 'point') then mType
:= TType
.TPoint
671 else if (fldtype
= 'size') then mType
:= TType
.TSize
672 else if (fldtype
= 'trigdata') then mType
:= TType
.TTrigData
673 else raise Exception
.Create(Format('field ''%s'' has invalid type ''%s''', [fldname
, fldtype
]));
675 {if hasdefId and (self.baseType = self.TType.TBool) then
677 if (defstr = 'true') or (defstr = 'tan') or (defstr = 'yes') then self.mDefIVal := 1
678 else if (defstr = 'false') or (defstr = 'ona') or (defstr = 'no') then self.mDefIVal := 0
679 else raise Exception.Create(Format('field ''%s'' has invalid boolean default ''%s''', [fldname, defstr]));
683 if hasdefStr
then self
.mDefSVal
:= defstr
684 else if hasdefInt
then self
.mDefSVal
:= Format('%d', [defint
])
685 else if hasdefId
then self
.mDefSVal
:= defstr
;
688 self
.mHasDefault
:= (hasdefStr
or hasdefId
or hasdefInt
);
689 self
.mPasName
:= fldpasname
;
691 self
.mEBSTypeName
:= fldrecname
;
692 self
.mBitSetUnique
:= unique
;
693 self
.mMaxDim
:= lmaxdim
;
694 self
.mBinOfs
:= fldofs
;
695 self
.mRecOfs
:= fldofs
;
696 self
.mSepPosSize
:= (asxy
or aswh
);
698 self
.mOmitDef
:= omitdef
;
699 self
.mInternal
:= ainternal
;
703 procedure TDynField
.writeBinTo (st
: TStream
);
712 TEBS
.TNone
: begin end;
715 // this must be byte/word/int
716 if (mMaxDim
>= 0) then
718 // this must be triggerdata
719 if (CompareText(mEBSTypeName
, 'triggerdata') <> 0) then
721 raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName
, mName
]));
725 TType
.TChar
, TType
.TByte
, TType
.TUByte
: begin end;
726 else raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName
, mName
]));
728 //writeln('trigdata size: ', mMaxDim);
729 GetMem(buf
, mMaxDim
);
730 if (buf
= nil) then raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName
, mName
]));
732 FillChar(buf
^, mMaxDim
, 0);
733 if (mRecRef
<> nil) then
735 ws
:= TSFSMemoryChunkStream
.Create(buf
, mMaxDim
);
736 mRecRef
.writeBinTo(ws
, mMaxDim
); // as trigdata
738 st
.WriteBuffer(buf
^, mMaxDim
);
741 if (buf
<> nil) then FreeMem(buf
);
745 if (mRecRef
= nil) then
749 TType
.TByte
, TType
.TUByte
: writeInt(st
, Byte(-1));
750 TType
.TShort
, TType
.TUShort
: writeInt(st
, SmallInt(-1));
751 TType
.TInt
, TType
.TUInt
: writeInt(st
, Integer(-1));
752 else raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName
, mName
]));
757 TType
.TByte
: maxv
:= 127;
758 TType
.TUByte
: maxv
:= 254;
759 TType
.TShort
: maxv
:= 32767;
760 TType
.TUShort
: maxv
:= 65534;
761 TType
.TInt
: maxv
:= $7fffffff;
762 TType
.TUInt
: maxv
:= $7fffffff;
763 else raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName
, mName
]));
765 // find record number
766 f
:= mOwner
.mOwner
.findRecordNumByType(mEBSTypeName
, mRecRef
);
767 if (f
< 0) then raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' not found in record list', [mEBSTypeName
, mName
]));
768 if (f
> maxv
) then raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' has too big index', [mEBSTypeName
, mName
]));
770 TType
.TByte
, TType
.TUByte
: writeInt(st
, Byte(f
));
771 TType
.TShort
, TType
.TUShort
: writeInt(st
, SmallInt(f
));
772 TType
.TInt
, TType
.TUInt
: writeInt(st
, Integer(f
));
773 else raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName
, mName
]));
777 TEBS
.TEnum
: begin end;
778 TEBS
.TBitSet
: begin end;
779 else raise Exception
.Create('ketmar forgot to handle some EBS type');
785 if (mIVal
<> 0) then writeInt(st
, Byte(1)) else writeInt(st
, Byte(0));
790 if (mMaxDim
= 0) then raise Exception
.Create(Format('invalid string size definition for field ''%s''', [mName
]));
791 if (mMaxDim
< 0) then
793 if (Length(mSVal
) <> 1) then raise Exception
.Create(Format('invalid string size definition for field ''%s''', [mName
]));
794 writeInt(st
, Byte(mSVal
[1]));
798 if (Length(mSVal
) > mMaxDim
) then raise Exception
.Create(Format('invalid string size definition for field ''%s''', [mName
]));
799 //FillChar(s[0], sizeof(s), 0);
800 s
:= utfTo1251(mSVal
);
801 //writeln('writing char[', mMaxDim, '] <', mName, '>: ', TTextParser.quote(s));
802 if (Length(s
) > 0) then st
.WriteBuffer(PChar(s
)^, Length(s
));
803 for f
:= Length(s
) to mMaxDim
do writeInt(st
, Byte(0));
810 // either array, and this should be triggerdata, or byte
811 if (mMaxDim
< 0) then
814 writeInt(st
, Byte(mIVal
));
819 raise Exception
.Create(Format('byte array in field ''%s'' cannot be written', [mName
]));
826 if (mMaxDim
> 0) then raise Exception
.Create(Format('short array in field ''%s'' cannot be written', [mName
]));
827 writeInt(st
, Word(mIVal
));
833 if (mMaxDim
> 0) then raise Exception
.Create(Format('int array in field ''%s'' cannot be written', [mName
]));
834 writeInt(st
, LongWord(mIVal
));
839 raise Exception
.Create(Format('cannot write string field ''%s''', [mName
]));
844 if (mMaxDim
> 0) then raise Exception
.Create(Format('pos/size array in field ''%s'' cannot be written', [mName
]));
845 writeInt(st
, Word(mIVal
));
846 writeInt(st
, Word(mIVal2
));
859 else raise Exception
.Create('ketmar forgot to handle some field type');
864 procedure TDynField
.writeTo (wr
: TTextWriter
);
869 first
, found
: Boolean;
873 // if this field should contain struct, convert type and parse struct
875 TEBS
.TNone
: begin end;
878 if (mRecRef
= nil) then
882 else if (Length(mRecRef
.mId
) = 0) then
884 mRecRef
.writeTo(wr
, false); // only data, no header
895 def
:= mOwner
.mOwner
;
896 es
:= def
.findEBS(mEBSTypeName
);
897 if (es
= nil) then raise Exception
.Create(Format('record enum type ''%s'' for field ''%s'' not found', [mEBSTypeName
, mName
]));
898 for f
:= 0 to High(es
.mVals
) do
900 if (es
.mVals
[f
] = mIVal
) then
907 raise Exception
.Create(Format('value %d in record enum type ''%s'' for field ''%s'' not found', [mIVal
, mEBSTypeName
, mName
]));
911 def
:= mOwner
.mOwner
;
912 es
:= def
.findEBS(mEBSTypeName
);
913 if (es
= nil) then raise Exception
.Create(Format('record bitset type ''%s'' for field ''%s'' not found', [mEBSTypeName
, mName
]));
917 for f
:= 0 to High(es
.mVals
) do
919 if (es
.mVals
[f
] = 0) then
926 raise Exception
.Create(Format('value %d in record bitset type ''%s'' for field ''%s'' not found', [0, mEBSTypeName
, mName
]));
933 if ((mIVal
and mask
) <> 0) then
936 for f
:= 0 to High(es
.mVals
) do
938 if (es
.mVals
[f
] = mask
) then
940 if not first
then wr
.put('+') else first
:= false;
946 if not found
then raise Exception
.Create(Format('value %d in record bitset type ''%s'' for field ''%s'' not found', [mask
, mEBSTypeName
, mName
]));
953 else raise Exception
.Create('ketmar forgot to handle some EBS type');
959 if (mIVal
= 0) then wr
.put('false;'#10) else wr
.put('true;'#10);
964 if (mMaxDim
= 0) then raise Exception
.Create(Format('invalid string size definition for field ''%s''', [mName
]));
965 wr
.put(TTextParser
.quote(mSVal
));
976 wr
.put('%d;'#10, [mIVal
]);
981 wr
.put(TTextParser
.quote(mSVal
));
988 wr
.put('(%d %d);'#10, [mIVal
, mIVal2
]);
1001 else raise Exception
.Create('ketmar forgot to handle some field type');
1003 raise Exception
.Create(Format('cannot parse field ''%s'' yet', [mName
]));
1007 procedure TDynField
.parseValue (pr
: TTextParser
);
1009 procedure parseInt (min
, max
: Integer);
1011 mIVal
:= pr
.expectInt();
1012 if (mIVal
< min
) or (mIVal
> max
) then raise Exception
.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType
), mName
]));
1017 rec
, rc
: TDynRecord
;
1023 // if this field should contain struct, convert type and parse struct
1025 TEBS
.TNone
: begin end;
1028 def
:= mOwner
.mOwner
;
1029 // ugly hack. sorry.
1030 if (CompareText(mEBSTypeName
, 'triggerdata') = 0) then
1033 // find trigger definition
1034 tfld
:= rec
.field
['type'];
1035 if (tfld
= nil) then raise Exception
.Create(Format('triggerdata value for field ''%s'' in record ''%s'' without ''type'' field', [mName
, rec
.mName
]));
1036 if (tfld
.mEBS
<> TEBS
.TEnum
) then raise Exception
.Create(Format('triggerdata value for field ''%s'' in record ''%s'' with bad ''type'' field', [mName
, rec
.mName
]));
1037 rc
:= def
.findTrigDataFor(tfld
.mSVal
);
1038 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
]));
1041 //if mRecRefOwned then mRecRef.Free();
1042 //mRecRefOwned := true;
1047 // other record types
1048 if (pr
.tokType
= pr
.TTId
) then
1050 rec
:= def
.findRecordByTypeId(mEBSTypeName
, pr
.tokStr
);
1051 if (rec
= nil) then raise Exception
.Create(Format('record ''%s'' (%s) value for field ''%s'' not found', [pr
.tokStr
, mEBSTypeName
, mName
]));
1053 //if mRecRefOwned then mRecRef.Free();
1054 //mRecRefOwned := false;
1057 pr
.expectTT(pr
.TTSemi
);
1060 else if (pr
.tokType
= pr
.TTBegin
) then
1062 rec
:= def
.findRec(mEBSTypeName
);
1063 if (rec
= nil) then raise Exception
.Create(Format('record type ''%s'' for field ''%s'' not found', [mEBSTypeName
, mName
]));
1066 //if mRecRefOwned then mRecRef.Free();
1067 //mRecRefOwned := true;
1070 mOwner
.mOwner
.addRecordByType(mEBSTypeName
, rc
);
1073 pr
.expectTT(pr
.TTBegin
);
1077 def
:= mOwner
.mOwner
;
1078 es
:= def
.findEBS(mEBSTypeName
);
1079 if (es
= nil) then raise Exception
.Create(Format('record enum type ''%s'' for field ''%s'' not found', [mEBSTypeName
, mName
]));
1080 tk
:= pr
.expectId();
1081 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
]));
1082 mIVal
:= es
.field
[tk
];
1084 //writeln('ENUM ', mEBSName, '; element <', mSVal, '> with value ', mIVal);
1086 pr
.expectTT(pr
.TTSemi
);
1091 def
:= mOwner
.mOwner
;
1092 es
:= def
.findEBS(mEBSTypeName
);
1093 if (es
= nil) then raise Exception
.Create(Format('record bitset type ''%s'' for field ''%s'' not found', [mEBSTypeName
, mName
]));
1097 tk
:= pr
.expectId();
1098 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
]));
1099 mIVal
:= mIVal
or es
.field
[tk
];
1101 if (pr
.tokType
<> pr
.TTDelim
) or ((pr
.tokChar
<> '|') and (pr
.tokChar
<> '+')) then break
;
1102 if mBitSetUnique
then raise Exception
.Create(Format('record bitset of type ''%s'' for field ''%s'' expects only one value', [tk
, mEBSTypeName
, mName
]));
1103 //pr.expectDelim('|');
1104 pr
.skipToken(); // plus or pipe
1107 pr
.expectTT(pr
.TTSemi
);
1110 else raise Exception
.Create('ketmar forgot to handle some EBS type');
1116 if pr
.eatId('true') or pr
.eatId('tan') or pr
.eatId('yes') then mIVal
:= 1
1117 else if pr
.eatId('false') or pr
.eatId('ona') or pr
.eatId('no') then mIVal
:= 0
1118 else raise Exception
.Create(Format('invalid bool value for field ''%s''', [mName
]));
1120 pr
.expectTT(pr
.TTSemi
);
1125 if (mMaxDim
= 0) then raise Exception
.Create(Format('invalid string size definition for field ''%s''', [mName
]));
1126 mSVal
:= pr
.expectStr(true);
1127 if (mMaxDim
< 0) then
1130 if (Length(mSVal
) <> 1) then raise Exception
.Create(Format('invalid string size for field ''%s''', [mName
]));
1131 mIVal
:= Integer(mSVal
[1]);
1137 if (Length(mSVal
) > mMaxDim
) then raise Exception
.Create(Format('invalid string size for field ''%s''', [mName
]));
1140 pr
.expectTT(pr
.TTSemi
);
1145 parseInt(-128, 127);
1146 pr
.expectTT(pr
.TTSemi
);
1152 pr
.expectTT(pr
.TTSemi
);
1157 parseInt(-32768, 32768);
1158 pr
.expectTT(pr
.TTSemi
);
1164 pr
.expectTT(pr
.TTSemi
);
1169 parseInt(Integer($80000000), $7fffffff);
1170 pr
.expectTT(pr
.TTSemi
);
1175 parseInt(0, $7fffffff); //FIXME
1176 pr
.expectTT(pr
.TTSemi
);
1181 mSVal
:= pr
.expectStr(true);
1183 pr
.expectTT(pr
.TTSemi
);
1189 pr
.expectDelim('(');
1190 mIVal
:= pr
.expectInt();
1191 if (mType
= TType
.TPoint
) then
1193 if (mIVal
< -32768) or (mIVal
> 32767) then raise Exception
.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType
), mName
]));
1197 if (mIVal
< 0) or (mIVal
> 32767) then raise Exception
.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType
), mName
]));
1199 mIVal2
:= pr
.expectInt();
1200 if (mType
= TType
.TPoint
) then
1202 if (mIVal2
< -32768) or (mIVal2
> 32767) then raise Exception
.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType
), mName
]));
1206 if (mIVal2
< 0) or (mIVal2
> 32767) then raise Exception
.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType
), mName
]));
1209 pr
.expectDelim(')');
1210 pr
.expectTT(pr
.TTSemi
);
1223 else raise Exception
.Create('ketmar forgot to handle some field type');
1225 raise Exception
.Create(Format('cannot parse field ''%s'' yet', [mName
]));
1229 procedure TDynField
.parseBinValue (st
: TStream
);
1234 // ////////////////////////////////////////////////////////////////////////// //
1235 constructor TDynRecord
.Create (pr
: TTextParser
);
1237 if (pr
= nil) then raise Exception
.Create('cannot create record type without type definition');
1249 constructor TDynRecord
.Create ();
1259 destructor TDynRecord
.Destroy ();
1268 function TDynRecord
.findByName (const aname
: AnsiString): Integer; inline;
1271 while (result
< Length(mFields
)) do
1273 if (CompareText(aname
, mFields
[result
].mName
) = 0) then exit
;
1280 function TDynRecord
.hasByName (const aname
: AnsiString): Boolean; inline;
1282 result
:= (findByName(aname
) >= 0);
1286 function TDynRecord
.getFieldByName (const aname
: AnsiString): TDynField
; inline;
1290 f
:= findByName(aname
);
1291 if (f
>= 0) then result
:= mFields
[f
] else result
:= nil;
1295 function TDynRecord
.getIsTrigData (): Boolean; inline;
1297 result
:= (Length(mTrigTypes
) > 0);
1301 function TDynRecord
.getIsForTrig (const aname
: AnsiString): Boolean; inline;
1306 for f
:= 0 to High(mTrigTypes
) do if (CompareText(mTrigTypes
[f
], aname
) = 0) then exit
;
1311 function TDynRecord
.clone (): TDynRecord
;
1315 result
:= TDynRecord
.Create();
1316 result
.mOwner
:= mOwner
;
1318 result
.mPasName
:= mPasName
;
1319 result
.mName
:= mName
;
1320 result
.mSize
:= mSize
;
1321 result
.mHeader
:= mHeader
;
1322 result
.mBinBlock
:= mBinBlock
;
1323 SetLength(result
.mFields
, Length(mFields
));
1324 for f
:= 0 to High(mFields
) do
1326 result
.mFields
[f
] := mFields
[f
].clone();
1327 result
.mFields
[f
].mOwner
:= result
;
1329 SetLength(result
.mTrigTypes
, Length(mTrigTypes
));
1330 for f
:= 0 to High(mTrigTypes
) do result
.mTrigTypes
[f
] := mTrigTypes
[f
];
1334 procedure TDynRecord
.parseDef (pr
: TTextParser
);
1339 if pr
.eatId('TriggerData') then
1342 if pr
.eatDelim('(') then
1346 while pr
.eatTT(pr
.TTComma
) do begin end;
1347 if pr
.eatDelim(')') then break
;
1348 tdn
:= pr
.expectId();
1349 if isForTrig
[tdn
] then raise Exception
.Create(Format('duplicate trigdata ''%s'' trigtype ''%s''', [mName
, tdn
]));
1350 SetLength(mTrigTypes
, Length(mTrigTypes
)+1);
1351 mTrigTypes
[High(mTrigTypes
)] := tdn
;
1356 tdn
:= pr
.expectId();
1357 SetLength(mTrigTypes
, 1);
1358 mTrigTypes
[0] := tdn
;
1363 mPasName
:= pr
.expectId(); // pascal record name
1365 mName
:= pr
.expectStr();
1366 while (pr
.tokType
<> pr
.TTBegin
) do
1368 if pr
.eatId('header') then begin mHeader
:= true; continue
; end;
1369 if pr
.eatId('size') then
1371 if (mSize
> 0) then raise Exception
.Create(Format('duplicate `size` in record ''%s''', [mName
]));
1372 mSize
:= pr
.expectInt();
1373 if (mSize
< 1) then raise Exception
.Create(Format('invalid record ''%s'' size: %d', [mName
, mSize
]));
1374 pr
.expectId('bytes');
1377 if pr
.eatId('binblock') then
1379 if (mBinBlock
>= 0) then raise Exception
.Create(Format('duplicate `binblock` in record ''%s''', [mName
]));
1380 mBinBlock
:= pr
.expectInt();
1381 if (mBinBlock
< 1) then raise Exception
.Create(Format('invalid record ''%s'' binblock: %d', [mName
, mBinBlock
]));
1387 pr
.expectTT(pr
.TTBegin
);
1389 while (pr
.tokType
<> pr
.TTEnd
) do
1391 fld
:= TDynField
.Create(pr
);
1392 if hasByName(fld
.name
) then begin fld
.Free(); raise Exception
.Create(Format('duplicate field ''%s''', [fld
.name
])); end;
1395 SetLength(mFields
, Length(mFields
)+1);
1396 mFields
[High(mFields
)] := fld
;
1398 //writeln('DEF: ', fld.definition);
1400 pr
.expectTT(pr
.TTEnd
);
1404 function TDynRecord
.definition (): AnsiString;
1411 result
:= 'TriggerData for ';
1412 if (Length(mTrigTypes
) > 1) then
1415 for f
:= 0 to High(mTrigTypes
) do
1417 if (f
<> 0) then result
+= ', ';
1418 result
+= mTrigTypes
[f
];
1424 result
+= mTrigTypes
[0];
1430 result
:= mPasName
+' is '+TTextParser
.quote(mName
);
1431 if (mSize
>= 0) then result
+= Format(' size %d bytes', [mSize
]);
1432 if mHeader
then result
+= ' header';
1435 for f
:= 0 to High(mFields
) do
1438 result
+= mFields
[f
].definition
;
1445 procedure TDynRecord
.writeBinTo (st
: TStream
; trigbufsz
: Integer=-1);
1451 blk
, blkmax
: Integer;
1456 if (trigbufsz
< 0) then
1458 if (mBinBlock
< 1) then raise Exception
.Create('cannot write binary record without block number');
1459 if (mSize
< 1) then raise Exception
.Create('cannot write binary record without size');
1466 oldh
:= mOwner
.curheader
;
1469 if (mOwner
.curheader
<> nil) then raise Exception
.Create('`writeBinTo()` cannot be called recursively');
1470 mOwner
.curheader
:= self
;
1474 FillChar(buf
^, bufsz
, 0);
1475 ws
:= TSFSMemoryChunkStream
.Create(buf
, bufsz
);
1477 // write normal fields
1478 for f
:= 0 to High(mFields
) do
1482 if (fld
.mType
= fld
.TType
.TList
) then continue
; // later
1483 if fld
.mInternal
then continue
;
1484 if (fld
.mBinOfs
< 0) then continue
;
1485 if (fld
.mBinOfs
>= bufsz
) then raise Exception
.Create('binary value offset is outside of the buffer');
1486 TSFSMemoryChunkStream(ws
).setup(buf
+fld
.mBinOfs
, bufsz
-fld
.mBinOfs
);
1487 writeln('writing field <', fld
.mName
, '>');
1491 // write block with normal fields
1494 writeln('writing header...');
1495 // signature and version
1496 writeIntBE(st
, LongWord($4D415001));
1497 writeInt(st
, Byte(mBinBlock
)); // type
1498 writeInt(st
, LongWord(0)); // reserved
1499 writeInt(st
, LongWord(bufsz
)); // size
1501 st
.WriteBuffer(buf
^, bufsz
);
1503 ws
.Free(); ws
:= nil;
1504 FreeMem(buf
); buf
:= nil;
1506 // write other blocks, if any
1511 for f
:= 0 to High(mFields
) do
1515 if (fld
.mType
= fld
.TType
.TList
) then
1517 if (Length(fld
.mRVal
) = 0) then continue
;
1518 rec
:= mOwner
.findRec(fld
.mName
);
1519 if (rec
= nil) then continue
;
1520 if (rec
.mBinBlock
<= 0) then continue
;
1521 if (blkmax
< rec
.mBinBlock
) then blkmax
:= rec
.mBinBlock
;
1525 for blk
:= 1 to blkmax
do
1527 if (blk
= mBinBlock
) then continue
;
1529 for f
:= 0 to High(mFields
) do
1533 if (fld
.mType
= fld
.TType
.TList
) then
1535 if (Length(fld
.mRVal
) = 0) then continue
;
1536 rec
:= mOwner
.findRec(fld
.mName
);
1537 if (rec
= nil) then continue
;
1538 if (rec
.mBinBlock
<> blk
) then continue
;
1539 if (ws
= nil) then ws
:= TMemoryStream
.Create();
1540 //rec.writeBinTo(ws);
1541 for c
:= 0 to High(fld
.mRVal
) do fld
.mRVal
[c
].writeBinTo(ws
);
1548 writeInt(st
, Byte(blk
)); // type
1549 writeInt(st
, LongWord(0)); // reserved
1550 writeInt(st
, LongWord(ws
.size
)); // size
1551 st
.CopyFrom(ws
, ws
.size
);
1558 mOwner
.curheader
:= oldh
;
1560 if (buf
<> nil) then FreeMem(buf
);
1565 procedure TDynRecord
.writeTo (wr
: TTextWriter
; putHeader
: Boolean=true);
1573 if (Length(mId
) > 0) then begin wr
.put(' '); wr
.put(mId
); end;
1579 for f
:= 0 to High(mFields
) do
1583 if (fld
.mType
= fld
.TType
.TList
) then
1585 if not mHeader
then raise Exception
.Create('record list in non-header record');
1586 for c
:= 0 to High(fld
.mRVal
) do
1589 fld
.mRVal
[c
].writeTo(wr
, true);
1593 if fld
.mInternal
then continue
;
1594 if fld
.mOmitDef
and fld
.isDefaultValue
then continue
;
1606 procedure TDynRecord
.parseValue (pr
: TTextParser
; asheader
: Boolean=false);
1610 rec
, trc
: TDynRecord
;
1613 if (mOwner
= nil) then raise Exception
.Create(Format('can''t parse record ''%s'' value without owner', [mName
]));
1615 if not asheader
then
1618 if (pr
.tokType
= pr
.TTId
) then mId
:= pr
.expectId();
1621 writeln('parsing record <', mName
, '>');
1622 pr
.expectTT(pr
.TTBegin
);
1623 while (pr
.tokType
<> pr
.TTEnd
) do
1625 if (pr
.tokType
<> pr
.TTId
) then raise Exception
.Create('identifier expected');
1627 writeln('<', pr
.tokStr
, ':', asheader
, '>');
1632 assert(self
= mOwner
.curheader
);
1633 // add records with this type (if any)
1634 trc
:= mOwner
.findRec(pr
.tokStr
);
1635 if (trc
<> nil) then
1641 if (Length(rec
.mId
) > 0) then
1643 fld
:= field
[pr
.tokStr
];
1644 if (fld
<> nil) then
1646 for c
:= 0 to High(fld
.mRVal
) do
1648 if (Length(fld
.mRVal
[c
].mId
) > 0) and (CompareText(fld
.mRVal
[c
].mId
, rec
.mId
) = 0) then raise Exception
.Create(Format('duplicate thing ''%s'' in record ''%s''', [fld
.mName
, mName
]));
1652 mOwner
.addRecordByType(rec
.mName
, rec
);
1661 for f := 0 to High(mOwner.records) do
1663 if (CompareText(mOwner.records[f].mName, pr.tokStr) = 0) then
1665 // find (or create) list of records with this type
1666 fld := field[pr.tokStr];
1670 fld := TDynField.Create(mOwner.records[f].mName, TDynField.TType.TList);
1672 SetLength(mFields, Length(mFields)+1);
1673 mFields[High(mFields)] := fld;
1675 if (fld.mType <> TDynField.TType.TList) then raise Exception.Create(Format('thing ''%s'' in record ''%s'' must be record', [fld.mName, mName]));
1676 rec := mOwner.records[f].clone();
1680 if (Length(rec.mId) > 0) then
1682 for c := 0 to High(fld.mRVal) do
1684 if (Length(fld.mRVal[c].mId) > 0) and (CompareText(fld.mRVal[c].mId, rec.mId) = 0) then raise Exception.Create(Format('duplicate thing ''%s'' in record ''%s''', [fld.mName, mName]));
1687 SetLength(fld.mRVal, Length(fld.mRVal)+1);
1688 fld.mRVal[High(fld.mRVal)] := rec;
1689 writeln('added ''', mOwner.records[f].mName, ''' with id ''', rec.mId, ''' (total:', Length(fld.mRVal), ')');
1690 //assert(mOwner.findRecordById(mOwner.records[f].mName, rec.mId) <> nil);
1699 if success then continue;
1704 fld
:= field
[pr
.tokStr
];
1705 if (fld
<> nil) then
1707 if fld
.defined
then raise Exception
.Create(Format('duplicate field ''%s'' in record ''%s''', [fld
.mName
, mName
]));
1708 if fld
.internal
then raise Exception
.Create(Format('internal field ''%s'' in record ''%s''', [fld
.mName
, mName
]));
1714 // something is wrong
1715 raise Exception
.Create(Format('unknown field ''%s'' in record ''%s''', [pr
.tokStr
, mName
]));
1717 pr
.expectTT(pr
.TTEnd
);
1718 // fix field defaults
1719 for f
:= 0 to High(mFields
) do mFields
[f
].fixDefaultValue();
1720 writeln('done parsing record <', mName
, '>');
1724 procedure TDynRecord
.parseBinValue (st
: TStream
);
1729 // ////////////////////////////////////////////////////////////////////////// //
1730 constructor TDynEBS
.Create (pr
: TTextParser
);
1737 destructor TDynEBS
.Destroy ();
1744 procedure TDynEBS
.cleanup ();
1755 function TDynEBS
.findByName (const aname
: AnsiString): Integer;
1758 while (result
< Length(mIds
)) do
1760 if (CompareText(aname
, mIds
[result
]) = 0) then exit
;
1767 function TDynEBS
.hasByName (const aname
: AnsiString): Boolean; inline;
1769 result
:= (findByName(aname
) >= 0);
1773 function TDynEBS
.getFieldByName (const aname
: AnsiString): Integer; inline;
1777 f
:= findByName(aname
);
1778 if (f
>= 0) then result
:= mVals
[f
] else result
:= 0;
1782 function TDynEBS
.definition (): AnsiString;
1786 if mIsEnum
then result
:='enum ' else result
:= 'bitset ';
1790 if mIsEnum
then cv
:= 0 else cv
:= 1;
1791 for f
:= 0 to High(mIds
) do
1793 if (mIds
[f
] = mMaxName
) then continue
;
1794 result
+= ' '+mIds
[f
];
1795 if (mVals
[f
] <> cv
) then
1797 result
+= Format(' = %d', [mVals
[f
]]);
1798 if mIsEnum
then cv
:= mVals
[f
];
1803 result
+= Format(', // %d'#10, [mVals
[f
]]);
1805 if mIsEnum
then Inc(cv
) else if (mVals
[f
] = cv
) then cv
:= cv
shl 1;
1808 if (Length(mMaxName
) > 0) then result
+= ' '+mMaxName
+' = MAX,'#10;
1813 procedure TDynEBS
.parseDef (pr
: TTextParser
);
1821 if pr
.eatId('enum') then mIsEnum
:= true
1822 else if pr
.eatId('bitset') then mIsEnum
:= false
1823 else pr
.expectId('enum');
1824 mName
:= pr
.expectId();
1825 mMaxVal
:= Integer($80000000);
1826 if mIsEnum
then cv
:= 0 else cv
:= 1;
1827 pr
.expectTT(pr
.TTBegin
);
1828 while (pr
.tokType
<> pr
.TTEnd
) do
1830 idname
:= pr
.expectId();
1831 for f
:= 0 to High(mIds
) do
1833 if (CompareText(mIds
[f
], idname
) = 0) then raise Exception
.Create(Format('duplicate field ''%s'' in enum/bitset ''%s''', [idname
, mName
]));
1835 if (CompareText(mMaxName
, idname
) = 0) then raise Exception
.Create(Format('duplicate field ''%s'' in enum/bitset ''%s''', [idname
, mName
]));
1840 if pr
.eatDelim('=') then
1842 if pr
.eatId('MAX') then
1844 if (Length(mMaxName
) > 0) then raise Exception
.Create(Format('duplicate max field ''%s'' in enum/bitset ''%s''', [idname
, mName
]));
1850 v
:= pr
.expectInt();
1851 if mIsEnum
then cv
:= v
;
1859 if mIsEnum
or (not hasV
) then
1861 if (mMaxVal
< v
) then mMaxVal
:= v
;
1863 SetLength(mIds
, Length(mIds
)+1);
1864 mIds
[High(mIds
)] := idname
;
1865 SetLength(mVals
, Length(mIds
));
1866 mVals
[High(mVals
)] := v
;
1868 if mIsEnum
or (not hasV
) then
1870 if mIsEnum
then Inc(cv
) else cv
:= cv
shl 1;
1873 if (pr
.tokType
= pr
.TTEnd
) then break
;
1874 pr
.expectTT(pr
.TTComma
);
1875 while pr
.eatTT(pr
.TTComma
) do begin end;
1877 pr
.expectTT(pr
.TTEnd
);
1879 if (Length(mMaxName
) > 0) then
1881 SetLength(mIds
, Length(mIds
)+1);
1882 mIds
[High(mIds
)] := mMaxName
;
1883 SetLength(mVals
, Length(mIds
));
1884 mVals
[High(mVals
)] := mMaxVal
;
1889 // ////////////////////////////////////////////////////////////////////////// //
1890 constructor TDynMapDef
.Create (pr
: TTextParser
);
1900 destructor TDynMapDef
.Destroy ();
1904 for f
:= 0 to High(records
) do records
[f
].Free();
1905 for f
:= 0 to High(trigDatas
) do trigDatas
[f
].Free();
1906 for f
:= 0 to High(ebs
) do ebs
[f
].Free();
1914 function TDynMapDef
.getHeader (): TDynRecord
; inline;
1916 if (Length(records
) = 0) then raise Exception
.Create('no header in empty mapdef');
1917 result
:= records
[0];
1921 function TDynMapDef
.findRec (const aname
: AnsiString): TDynRecord
;
1925 for f
:= 0 to High(records
) do
1927 if (CompareText(records
[f
].name
, aname
) = 0) then begin result
:= records
[f
]; exit
; end;
1933 function TDynMapDef
.findTrigDataFor (const aname
: AnsiString): TDynRecord
;
1937 for f
:= 0 to High(trigDatas
) do
1939 if (trigDatas
[f
].isForTrig
[aname
]) then begin result
:= trigDatas
[f
]; exit
; end;
1945 function TDynMapDef
.findEBS (const aname
: AnsiString): TDynEBS
;
1949 for f
:= 0 to High(ebs
) do
1951 if (CompareText(ebs
[f
].name
, aname
) = 0) then begin result
:= ebs
[f
]; exit
; end;
1957 function TDynMapDef
.findRecordByTypeId (const atypename
, aid
: AnsiString): TDynRecord
;
1964 if (curheader
= nil) then exit
;
1966 //writeln('searching for type <', atypename, '>');
1967 rec
:= findRec(atypename
);
1968 if (rec
= nil) then exit
;
1970 //writeln('searching for data of type <', atypename, '>');
1971 fld
:= curheader
.field
[atypename
];
1972 if (fld
= nil) then exit
;
1973 if (fld
.mType
<> fld
.TType
.TList
) then exit
;
1975 //writeln('searching for data of type <', atypename, '> with id <', aid, '> (', Length(fld.mRVal), ')');
1976 for f
:= 0 to High(fld
.mRVal
) do
1978 if (CompareText(fld
.mRVal
[f
].mId
, aid
) = 0) then
1980 //writeln(' FOUND!');
1981 result
:= fld
.mRVal
[f
];
1989 procedure TDynMapDef
.addRecordByType (const atypename
: AnsiString; rc
: TDynRecord
);
1994 assert(curheader
<> nil);
1996 rec
:= findRec(atypename
);
1999 //writeln('searching for data of type <', atypename, '>');
2000 fld
:= curheader
.field
[atypename
];
2004 fld
:= TDynField
.Create(atypename
, TDynField
.TType
.TList
);
2005 fld
.mOwner
:= curheader
;
2006 SetLength(curheader
.mFields
, Length(curheader
.mFields
)+1);
2007 curheader
.mFields
[High(curheader
.mFields
)] := fld
;
2009 if (fld
.mType
<> fld
.TType
.TList
) then exit
;
2011 SetLength(fld
.mRVal
, Length(fld
.mRVal
)+1);
2012 fld
.mRVal
[High(fld
.mRVal
)] := rc
;
2016 function TDynMapDef
.findRecordNumByType (const atypename
: AnsiString; rc
: TDynRecord
): Integer;
2023 if (curheader
= nil) then exit
;
2025 rec
:= findRec(atypename
);
2026 if (rec
= nil) then exit
;
2028 fld
:= curheader
.field
[atypename
];
2029 if (fld
= nil) then exit
;
2030 if (fld
.mType
<> fld
.TType
.TList
) then exit
;
2032 for f
:= 0 to High(fld
.mRVal
) do
2034 if (fld
.mRVal
[f
] = rc
) then
2044 procedure TDynMapDef
.parseDef (pr
: TTextParser
);
2046 dr
, hdr
: TDynRecord
;
2053 if not pr
.skipBlanks() then break
;
2054 if (pr
.tokType
<> pr
.TTId
) then raise Exception
.Create('identifier expected');
2056 if (pr
.tokStr
= 'enum') or (pr
.tokStr
= 'bitset') then
2058 eb
:= TDynEBS
.Create(pr
);
2059 if (findEBS(eb
.name
) <> nil) then
2062 raise Exception
.Create(Format('duplicate enum/bitset ''%s''', [eb
.name
]));
2065 SetLength(ebs
, Length(ebs
)+1);
2066 ebs
[High(ebs
)] := eb
;
2067 //writeln(eb.definition); writeln;
2071 if (pr
.tokStr
= 'TriggerData') then
2073 dr
:= TDynRecord
.Create(pr
);
2074 for f
:= 0 to High(dr
.mTrigTypes
) do
2076 if (findTrigDataFor(dr
.mTrigTypes
[f
]) <> nil) then
2079 raise Exception
.Create(Format('duplicate trigdata ''%s''', [dr
.mTrigTypes
[f
]]));
2083 SetLength(trigDatas
, Length(trigDatas
)+1);
2084 trigDatas
[High(trigDatas
)] := dr
;
2085 //writeln(dr.definition); writeln;
2089 dr
:= TDynRecord
.Create(pr
);
2090 //writeln(dr.definition); writeln;
2091 if (findRec(dr
.name
) <> nil) then begin dr
.Free(); raise Exception
.Create(Format('duplicate record ''%s''', [dr
.name
])); end;
2092 if (hdr
<> nil) and (CompareText(dr
.name
, hdr
.name
) = 0) then begin dr
.Free(); raise Exception
.Create(Format('duplicate record ''%s''', [dr
.name
])); end;
2096 if (hdr
<> nil) then begin dr
.Free(); raise Exception
.Create(Format('duplicate header record ''%s'' (previous is ''%s'')', [dr
.name
, hdr
.name
])); end;
2101 SetLength(records
, Length(records
)+1);
2102 records
[High(records
)] := dr
;
2106 if (hdr
= nil) then raise Exception
.Create('header definition not found in mapdef');
2107 SetLength(records
, Length(records
)+1);
2108 for f
:= High(records
) downto 1 do records
[f
] := records
[f
-1];
2113 // ////////////////////////////////////////////////////////////////////////// //
2114 function TDynMapDef
.parseMap (pr
: TTextParser
): TDynRecord
;
2116 res
: TDynRecord
= nil;
2118 if (curheader
<> nil) then raise Exception
.Create('cannot call `parseMap()` recursively, sorry');
2121 pr
.expectId(header
.name
);
2122 res
:= header
.clone();
2124 res
.parseValue(pr
, true); // as header
2134 function TDynMapDef
.parseBinMap (st
: TStream
): TDynRecord
;