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}
23 xparser
, xstreams
, utils
;
26 // ////////////////////////////////////////////////////////////////////////// //
33 TDynFieldList
= specialize TSimpleList
<TDynField
>;
34 TDynRecList
= specialize TSimpleList
<TDynRecord
>;
35 TDynEBSList
= specialize TSimpleList
<TDynEBS
>;
37 // this is base type for all scalars (and arrays)
41 TType
= (TBool
, TChar
, TByte
, TUByte
, TShort
, TUShort
, TInt
, TUInt
, TString
, TPoint
, TSize
, TList
, TTrigData
);
42 // TPoint: pair of Integers
43 // TSize: pair of UShorts
44 // TList: actually, array of records
45 // TTrigData: array of mMaxDim bytes, but internally a record (mRecRef)
46 // arrays of chars are pascal shortstrings (with counter in the first byte)
50 TEBS
= (TNone
, TRec
, TEnum
, TBitSet
);
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 mRecRef
: TDynRecord
; // for TEBS.TRec
62 mMaxDim
: Integer; // for byte and char arrays; <0: not an array; 0: impossible value
63 mBinOfs
: Integer; // offset in binary; <0 - none
64 mRecOfs
: Integer; // offset in record; <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`
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")
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
84 mRecRefId
: AnsiString;
89 procedure parseDef (pr
: TTextParser
);
91 procedure parseDefaultValue (); // parse `mDefUnparsed` to `mDefSVal`, `mDefIVal`, `mDefIVal2`, `mDefRecRef`
92 procedure fixDefaultValue (); // this will NOT clone `mDefRecRef`
93 function isDefaultValue (): Boolean;
96 constructor Create (const aname
: AnsiString; atype
: TType
);
97 constructor Create (pr
: TTextParser
);
98 destructor Destroy (); override;
100 class function getTypeName (t
: TType
): AnsiString;
102 function definition (): AnsiString;
103 function pasdef (): AnsiString;
105 function clone (newOwner
: TDynRecord
=nil): TDynField
;
107 procedure parseValue (pr
: TTextParser
);
108 procedure parseBinValue (st
: TStream
);
110 procedure writeTo (wr
: TTextWriter
);
111 procedure writeBinTo (st
: TStream
);
113 // won't work for lists
114 function isSimpleEqu (fld
: TDynField
): Boolean;
116 procedure setValue (const s
: AnsiString);
119 property pasname
: AnsiString read mPasName
;
120 property name
: AnsiString read mName
;
121 property baseType
: TType read mType
;
122 property defined
: Boolean read mDefined write mDefined
;
123 property internal
: Boolean read mInternal write mInternal
;
124 property ival
: Integer read mIVal
;
125 property sval
: AnsiString read mSVal
;
126 property hasDefault
: Boolean read mHasDefault
;
127 property defsval
: AnsiString read mDefSVal
;
128 property ebs
: TEBS read mEBS
;
129 property ebstype
: TObject read mEBSType
;
130 property ebstypename
: AnsiString read mEBSTypeName
; // enum/bitset name
131 property list
: TDynRecList read mRVal
; // for list
133 property x
: Integer read mIVal
;
134 property w
: Integer read mIVal
;
135 property y
: Integer read mIVal2
;
136 property h
: Integer read mIVal2
;
140 // "value" header record contains TList fields, with name equal to record type
145 mPasName
: AnsiString;
148 mFields
: TDynFieldList
;
149 mTrigTypes
: array of AnsiString; // if this is triggerdata, we'll hold list of triggers here
150 mHeader
: Boolean; // true for header record
151 mBinBlock
: Integer; // -1: none
152 mHeaderRec
: TDynRecord
; // for "value" records this is header record with data, for "type" records this is header type record
155 procedure parseDef (pr
: TTextParser
); // parse definition
157 function findByName (const aname
: AnsiString): Integer; inline;
158 function hasByName (const aname
: AnsiString): Boolean; inline;
159 function getFieldByName (const aname
: AnsiString): TDynField
; inline;
161 function getIsTrigData (): Boolean; inline;
162 function getIsForTrig (const aname
: AnsiString): Boolean; inline;
165 function findRecordByTypeId (const atypename
, aid
: AnsiString): TDynRecord
;
166 function findRecordNumByType (const atypename
: AnsiString; rc
: TDynRecord
): Integer;
167 procedure addRecordByType (const atypename
: AnsiString; rc
: TDynRecord
);
170 constructor Create ();
171 constructor Create (pr
: TTextParser
); // parse definition
172 destructor Destroy (); override;
174 function definition (): AnsiString;
175 function pasdef (): AnsiString;
177 function clone (): TDynRecord
;
179 function isSimpleEqu (rec
: TDynRecord
): Boolean;
181 procedure parseValue (pr
: TTextParser
; beginEaten
: Boolean=false);
182 procedure parseBinValue (st
: TStream
; forceData
: Boolean=false);
184 procedure writeTo (wr
: TTextWriter
; putHeader
: Boolean=true);
185 procedure writeBinTo (st
: TStream
; trigbufsz
: Integer=-1; onlyFields
: Boolean=false);
187 // find field with `TriggerType` type
188 function trigTypeField (): TDynField
;
190 // number of records of the given instance
191 function instanceCount (const typename
: AnsiString): Integer;
194 property id
: AnsiString read mId
; // for map parser
195 property pasname
: AnsiString read mPasName
;
196 property name
: AnsiString read mName
; // record name
197 property size
: Integer read mSize
; // size in bytes
198 property fields
: TDynFieldList read mFields
;
199 property has
[const aname
: AnsiString]: Boolean read hasByName
;
200 property field
[const aname
: AnsiString]: TDynField read getFieldByName
;
201 property isTrigData
: Boolean read getIsTrigData
;
202 property isForTrig
[const aname
: AnsiString]: Boolean read getIsForTrig
;
203 property headerType
: TDynRecord read mHeaderRec
;
204 property isHeader
: Boolean read mHeader
;
212 mIds
: array of AnsiString;
213 mVals
: array of Integer;
214 mMaxName
: AnsiString; // MAX field
215 mMaxVal
: Integer; // max value
218 procedure cleanup ();
220 procedure parseDef (pr
: TTextParser
); // parse definition
222 function findByName (const aname
: AnsiString): Integer; inline;
223 function hasByName (const aname
: AnsiString): Boolean; inline;
224 function getFieldByName (const aname
: AnsiString): Integer; inline;
227 constructor Create (pr
: TTextParser
); // parse definition
228 destructor Destroy (); override;
230 function definition (): AnsiString;
231 function pasdef (): AnsiString;
233 // return empty string if not found
234 function nameByValue (v
: Integer): AnsiString;
237 property name
: AnsiString read mName
; // record name
238 property isEnum
: Boolean read mIsEnum
;
239 property has
[const aname
: AnsiString]: Boolean read hasByName
;
240 property field
[const aname
: AnsiString]: Integer read getFieldByName
;
246 recTypes
: TDynRecList
; // [0] is always header
247 trigTypes
: TDynRecList
; // trigdata
248 ebsTypes
: TDynEBSList
; // enums, bitsets
251 procedure parseDef (pr
: TTextParser
);
253 function getHeaderRecType (): TDynRecord
; inline;
256 constructor Create (pr
: TTextParser
); // parses data definition
257 destructor Destroy (); override;
259 function findRecType (const aname
: AnsiString): TDynRecord
;
260 function findTrigFor (const aname
: AnsiString): TDynRecord
;
261 function findEBSType (const aname
: AnsiString): TDynEBS
;
263 function pasdef (): AnsiString;
265 // creates new header record
266 function parseMap (pr
: TTextParser
): TDynRecord
;
268 // creates new header record
269 function parseBinMap (st
: TStream
): TDynRecord
;
272 property headerType
: TDynRecord read getHeaderRecType
;
282 // ////////////////////////////////////////////////////////////////////////// //
283 function StrEqu (const a
, b
: AnsiString): Boolean; inline; begin result
:= (a
= b
); end;
286 // ////////////////////////////////////////////////////////////////////////// //
287 constructor TDynField
.Create (const aname
: AnsiString; atype
: TType
);
294 if (mType
= TType
.TList
) then mRVal
:= TDynRecList
.Create();
298 constructor TDynField
.Create (pr
: TTextParser
);
305 destructor TDynField
.Destroy ();
312 procedure TDynField
.cleanup ();
325 mSepPosSize
:= false;
327 mHasDefault
:= false;
339 mBitSetUnique
:= false;
340 mAsMonsterId
:= false;
343 if (mType
= TType
.TList
) then mRVal
:= TDynRecList
.Create();
347 function TDynField
.clone (newOwner
: TDynRecord
=nil): TDynField
;
351 result
:= TDynField
.Create(mName
, mType
);
352 result
.mOwner
:= mOwner
;
353 if (newOwner
<> nil) then result
.mOwner
:= newOwner
else result
.mOwner
:= mOwner
;
354 result
.mPasName
:= mPasName
;
355 result
.mName
:= mName
;
356 result
.mType
:= mType
;
357 result
.mIVal
:= mIVal
;
358 result
.mIVal2
:= mIVal2
;
359 result
.mSVal
:= mSVal
;
360 if (mRVal
<> nil) then
362 result
.mRVal
:= TDynRecList
.Create(mRVal
.count
);
363 for rec
in mRVal
do result
.mRVal
.append(rec
.clone());
367 if (mType
= TType
.TList
) then result
.mRVal
:= TDynRecList
.Create() else result
.mRVal
:= nil;
369 result
.mRecRef
:= mRecRef
;
370 result
.mMaxDim
:= mMaxDim
;
371 result
.mBinOfs
:= mBinOfs
;
372 result
.mRecOfs
:= mRecOfs
;
373 result
.mSepPosSize
:= mSepPosSize
;
375 result
.mDefined
:= mDefined
;
376 result
.mHasDefault
:= mHasDefault
;
377 result
.mOmitDef
:= mOmitDef
;
378 result
.mInternal
:= mInternal
;
379 result
.mNegBool
:= mNegBool
;
380 result
.mBitSetUnique
:= mBitSetUnique
;
381 result
.mAsMonsterId
:= mAsMonsterId
;
382 result
.mDefUnparsed
:= mDefUnparsed
;
383 result
.mDefSVal
:= mDefSVal
;
384 result
.mDefIVal
:= mDefIVal
;
385 result
.mDefIVal2
:= mDefIVal2
;
386 result
.mDefRecRef
:= mDefRecRef
;
388 result
.mEBSTypeName
:= mEBSTypeName
;
389 result
.mEBSType
:= mEBSType
;
390 result
.mRecRefId
:= mRecRefId
;
394 // won't work for lists
395 function TDynField
.isSimpleEqu (fld
: TDynField
): Boolean;
397 if (fld
= nil) or (mType
<> fld
.mType
) then begin result
:= false; exit
; end;
399 TType
.TBool
: result
:= ((mIVal
<> 0) = (fld
.mIVal
<> 0));
400 TType
.TChar
: result
:= (mSVal
= fld
.mSVal
);
407 result
:= (mIVal
= fld
.mIVal
);
408 TType
.TString
: result
:= (mSVal
= fld
.mSVal
);
411 result
:= ((mIVal
= fld
.mIVal
) and (mIVal2
= fld
.mIVal2
));
412 TType
.TList
: result
:= false;
415 if (mRecRef
= nil) then begin result
:= (fld
.mRecRef
= nil); exit
; end;
416 result
:= mRecRef
.isSimpleEqu(fld
.mRecRef
);
418 else raise Exception
.Create('ketmar forgot to handle some field type');
423 procedure TDynField
.setValue (const s
: AnsiString);
427 stp
:= TStrTextParser
.Create(s
+';');
436 procedure TDynField
.parseDefaultValue ();
438 stp
: TTextParser
= nil;
440 oIVal
, oIVal2
: Integer;
444 if not mHasDefault
then
459 stp
:= TStrTextParser
.Create(mDefUnparsed
+';');
464 mDefRecRef
:= mRecRef
;
477 // default value should be parsed
478 procedure TDynField
.fixDefaultValue ();
480 if mDefined
then exit
;
481 if not mHasDefault
then
483 if mInternal
then exit
;
484 raise Exception
.Create(Format('field ''%s'' in record ''%s'' of record type ''%s'' is not set', [mName
, mOwner
.mId
, mOwner
.mName
]));
486 if (mEBS
= TEBS
.TRec
) then mRecRef
:= mDefRecRef
;
494 // default value should be parsed
495 function TDynField
.isDefaultValue (): Boolean;
497 if not mHasDefault
then begin result
:= false; exit
; end;
498 if (mEBS
= TEBS
.TRec
) then begin result
:= (mRecRef
= mDefRecRef
); exit
; end;
500 TType
.TChar
, TType
.TString
: result
:= (mSVal
= mDefSVal
);
501 TType
.TPoint
, TType
.TSize
: result
:= (mIVal
= mDefIVal2
) and (mIVal2
= mDefIVal2
);
502 TType
.TList
, TType
.TTrigData
: result
:= false; // no default values for those types
503 else result
:= (mIVal
= mDefIVal
);
508 class function TDynField
.getTypeName (t
: TType
): AnsiString;
511 TType
.TBool
: result
:= 'bool';
512 TType
.TChar
: result
:= 'char';
513 TType
.TByte
: result
:= 'byte';
514 TType
.TUByte
: result
:= 'ubyte';
515 TType
.TShort
: result
:= 'short';
516 TType
.TUShort
: result
:= 'ushort';
517 TType
.TInt
: result
:= 'int';
518 TType
.TUInt
: result
:= 'uint';
519 TType
.TString
: result
:= 'string';
520 TType
.TPoint
: result
:= 'point';
521 TType
.TSize
: result
:= 'size';
522 TType
.TList
: result
:= 'array';
523 TType
.TTrigData
: result
:= 'trigdata';
524 else raise Exception
.Create('ketmar forgot to handle some field type');
529 function TDynField
.definition (): AnsiString;
531 result
:= mPasName
+' is '+quoteStr(mName
)+' type ';
532 result
+= getTypeName(mType
);
533 if (mMaxDim
>= 0) then result
+= Format('[%d]', [mMaxDim
]);
534 if (mRecOfs
>= 0) then result
+= Format(' offset %d', [mRecOfs
]);
536 TEBS
.TNone
: begin end;
537 TEBS
.TRec
: result
+= ' '+mEBSTypeName
;
538 TEBS
.TEnum
: result
+= ' enum '+mEBSTypeName
;
539 TEBS
.TBitSet
: begin result
+= ' bitset '; if mBitSetUnique
then result
+= 'unique '; result
+= mEBSTypeName
; end;
541 if mAsMonsterId
then result
+= ' as monsterid';
542 if mHasDefault
and (Length(mDefUnparsed
) > 0) then result
+= ' default '+mDefUnparsed
;
545 if (mType
= TType
.TPoint
) then begin if (mAsT
) then result
+= ' as txy' else result
+= ' as xy'; end
546 else if (mType
= TType
.TSize
) then begin if (mAsT
) then result
+= ' as twh' else result
+= ' as wh'; end;
548 if mOmitDef
then result
+= ' omitdefault';
549 if mInternal
then result
+= ' internal';
553 function TDynField
.pasdef (): AnsiString;
555 result
:= mPasName
+': ';
557 TType
.TBool
: result
+= 'Boolean;';
558 TType
.TChar
: if (mMaxDim
> 0) then result
+= formatstrf('Char%d;', [mMaxDim
]) else result
+= 'Char;';
559 TType
.TByte
: result
+= 'ShortInt;';
560 TType
.TUByte
: result
+= 'Byte;';
561 TType
.TShort
: result
+= 'SmallInt;';
562 TType
.TUShort
: result
+= 'Word;';
563 TType
.TInt
: result
+= 'LongInt;';
564 TType
.TUInt
: result
+= 'LongWord;';
565 TType
.TString
: result
+= 'AnsiString;';
567 if mAsT
then result
:= 'tX, tY: Integer;'
568 else if mSepPosSize
then result
:= 'X, Y: Integer;'
569 else result
+= 'TDFPoint;';
571 if mAsT
then result
:= 'tWidth, tHeight: Word;'
572 else if mSepPosSize
then result
:= 'Width, Height: Word;'
573 else result
+= 'TSize;';
574 TType
.TList
: assert(false);
575 TType
.TTrigData
: result
+= formatstrf('Byte%d;', [mMaxDim
]);
576 else raise Exception
.Create('ketmar forgot to handle some field type');
581 procedure TDynField
.parseDef (pr
: TTextParser
);
586 fldrecname
: AnsiString;
587 fldpasname
: AnsiString;
588 asxy
, aswh
, ast
: Boolean;
597 lebs
: TDynField
.TEBS
;
619 lebs
:= TDynField.TEBS.TNone
;
621 fldpasname
:= pr.expectId
(); // pascal field name
624 fldname
:= pr.expectStr
();
627 fldtype
:= pr.expectId
();
630 if pr.eatDelim
('[') then
632 lmaxdim
:= pr.expectInt
();
633 if
(lmaxdim
< 1) then raise Exception.Create
(Format
('invalid field ''%s'' array size', [fldname
]));
637 while (pr
.tokType
<> pr
.TTSemi
) do
639 if pr
.eatId('offset') then
641 if (fldofs
>= 0) then raise Exception
.Create(Format('duplicate field ''%s'' offset', [fldname
]));
642 fldofs
:= pr
.expectInt();
643 if (fldofs
< 0) then raise Exception
.Create(Format('invalid field ''%s'' offset', [fldname
]));
647 if pr
.eatId('as') then
649 if pr
.eatId('xy') then asxy
:= true
650 else if pr
.eatId('wh') then aswh
:= true
651 else if pr
.eatId('txy') then begin asxy
:= true; ast
:= true; end
652 else if pr
.eatId('twh') then begin aswh
:= true; ast
:= true; end
653 else if pr
.eatId('monsterid') then begin asmonid
:= true
; end
654 else raise Exception.Create
(Format
('invalid field ''%s'' as what?', [fldname
]));
658 if pr
.eatId('enum') then
660 lebs
:= TDynField
.TEBS
.TEnum
;
661 if (Length(fldrecname
) <> 0) then raise Exception
.Create(Format('field ''%s'' already typed as ''%s''', [fldname
, fldrecname
]));
662 fldrecname
:= pr
.expectId();
666 if pr
.eatId('bitset') then
668 lebs
:= TDynField
.TEBS
.TBitSet
;
669 if (Length(fldrecname
) <> 0) then raise Exception
.Create(Format('field ''%s'' already typed as ''%s''', [fldname
, fldrecname
]));
670 unique
:= pr
.eatId('unique');
671 fldrecname
:= pr
.expectId();
675 if pr
.eatId('default') then
677 if hasdefStr
or hasdefInt
or hasdefId
then raise Exception
.Create(Format('field ''%s'' has duplicate default', [fldname
]));
682 defstr
:= pr
.expectStr(true); // allow empty strings
687 defstr
:= pr
.expectId();
692 defint
:= pr
.expectInt();
695 raise Exception
.Create(Format('field ''%s'' has invalid default', [fldname
]));
700 if pr
.eatId('omitdefault') then
706 if pr
.eatId('internal') then
712 if (pr
.tokType
<> pr
.TTId
) then raise Exception
.Create(Format('field ''%s'' has something unexpected in definition', [fldname
]));
714 if (Length(fldrecname
) <> 0) then raise Exception
.Create(Format('field ''%s'' already typed as ''%s''', [fldname
, fldrecname
]));
715 fldrecname
:= pr
.expectId();
716 lebs
:= TDynField
.TEBS
.TRec
;
719 pr
.expectTT(pr
.TTSemi
);
723 if (fldtype
= 'bool') then mType
:= TType
.TBool
724 else if (fldtype
= 'negbool') then begin mType
:= TType
.TBool
; mNegBool
:= true; end
725 else if (fldtype
= 'char') then mType
:= TType
.TChar
726 else if (fldtype
= 'byte') then mType
:= TType
.TByte
727 else if (fldtype
= 'ubyte') then mType
:= TType
.TUByte
728 else if (fldtype
= 'short') then mType
:= TType
.TShort
729 else if (fldtype
= 'ushort') then mType
:= TType
.TUShort
730 else if (fldtype
= 'int') then mType
:= TType
.TInt
731 else if (fldtype
= 'uint') then mType
:= TType
.TUInt
732 else if (fldtype
= 'string') then mType
:= TType
.TString
733 else if (fldtype
= 'point') then mType
:= TType
.TPoint
734 else if (fldtype
= 'size') then mType
:= TType
.TSize
735 else if (fldtype
= 'trigdata') then mType
:= TType
.TTrigData
736 else raise Exception
.Create(Format('field ''%s'' has invalid type ''%s''', [fldname
, fldtype
]));
738 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
]));
739 if (mType
= TType
.TTrigData
) then
741 if (lmaxdim
< 1) then raise Exception
.Create(Format('field ''%s'' of type ''%s'' cannot be array', [fldname
, fldtype
]));
742 if (Length(fldrecname
) > 0) then raise Exception
.Create(Format('field ''%s'' of type ''%s'' cannot have another type', [fldname
, fldtype
]));
743 lebs
:= TDynField
.TEBS
.TRec
;
746 if hasdefStr
then self
.mDefUnparsed
:= quoteStr(defstr
)
747 else if hasdefInt
then self
.mDefUnparsed
:= Format('%d', [defint
])
748 else if hasdefId
then self
.mDefUnparsed
:= defstr
;
750 self
.mHasDefault
:= (hasdefStr
or hasdefId
or hasdefInt
);
751 self
.mPasName
:= fldpasname
;
753 self
.mEBSTypeName
:= fldrecname
;
754 self
.mBitSetUnique
:= unique
;
755 self
.mAsMonsterId
:= asmonid
;
756 self.mMaxDim
:= lmaxdim
;
757 self.mBinOfs
:= fldofs
;
758 self.mRecOfs
:= fldofs
;
759 self.mSepPosSize
:= (asxy
or aswh
);
761 self.mOmitDef
:= omitdef
;
762 self.mInternal
:= ainternal
;
766 procedure TDynField
.writeBinTo (st
: TStream
);
775 TEBS
.TNone
: begin end;
778 if (mMaxDim
>= 0) then
780 // this must be triggerdata
781 if (mType
<> TType
.TTrigData
) then
783 raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName
, mName
]));
786 GetMem(buf
, mMaxDim
);
787 if (buf
= nil) then raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName
, mName
]));
789 FillChar(buf
^, mMaxDim
, 0);
790 if (mRecRef
<> nil) then
792 ws
:= TSFSMemoryChunkStream
.Create(buf
, mMaxDim
);
793 mRecRef
.writeBinTo(ws
, mMaxDim
); // as trigdata
795 st
.WriteBuffer(buf
^, mMaxDim
);
798 if (buf
<> nil) then FreeMem(buf
);
804 TType
.TByte
: maxv
:= 127;
805 TType
.TUByte
: maxv
:= 254;
806 TType
.TShort
: maxv
:= 32767;
807 TType
.TUShort
: maxv
:= 65534;
808 TType
.TInt
: maxv
:= $7fffffff;
809 TType
.TUInt
: maxv
:= $7fffffff;
810 else raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName
, mName
]));
812 // find record number
813 if (mRecRef
<> nil) then
815 f
:= mOwner
.findRecordNumByType(mEBSTypeName
, mRecRef
);
816 if (f
< 0) then raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' not found in record list', [mEBSTypeName
, mName
]));
817 if mAsMonsterId
then Inc(f
);
818 if (f
> maxv
) then raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' has too big index', [mEBSTypeName
, mName
]));
822 if mAsMonsterId
then f
:= 0 else f
:= -1;
825 TType
.TByte
, TType
.TUByte
: writeInt(st
, Byte(f
));
826 TType
.TShort
, TType
.TUShort
: writeInt(st
, SmallInt(f
));
827 TType
.TInt
, TType
.TUInt
: writeInt(st
, LongWord(f
));
828 else raise Exception
.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName
, mName
]));
832 TEBS
.TEnum
: begin end;
833 TEBS
.TBitSet
: begin end;
834 else raise Exception
.Create('ketmar forgot to handle some EBS type');
842 if (mIVal
<> 0) then writeInt(st
, Byte(1)) else writeInt(st
, Byte(0));
846 if (mIVal
= 0) then writeInt(st
, Byte(1)) else writeInt(st
, Byte(0));
852 if (mMaxDim
= 0) then raise Exception
.Create(Format('invalid string size definition for field ''%s''', [mName
]));
853 if (mMaxDim
< 0) then
855 if (Length(mSVal
) <> 1) then raise Exception
.Create(Format('invalid string size definition for field ''%s''', [mName
]));
856 writeInt(st
, Byte(mSVal
[1]));
860 if (Length(mSVal
) > mMaxDim
) then raise Exception
.Create(Format('invalid string size definition for field ''%s''', [mName
]));
862 if (Length(s
) > 0) then st
.WriteBuffer(PChar(s
)^, Length(s
));
863 for f
:= Length(s
) to mMaxDim
do writeInt(st
, Byte(0));
870 // triggerdata array was processed earlier
871 if (mMaxDim
>= 0) then Exception
.Create(Format('byte array in field ''%s'' cannot be written', [mName
]));
872 writeInt(st
, Byte(mIVal
));
878 if (mMaxDim
>= 0) then raise Exception
.Create(Format('short array in field ''%s'' cannot be written', [mName
]));
879 writeInt(st
, Word(mIVal
));
885 if (mMaxDim
>= 0) then raise Exception
.Create(Format('int array in field ''%s'' cannot be written', [mName
]));
886 writeInt(st
, LongWord(mIVal
));
891 raise Exception
.Create(Format('cannot write string field ''%s''', [mName
]));
895 if (mMaxDim
>= 0) then raise Exception
.Create(Format('pos/size array in field ''%s'' cannot be written', [mName
]));
896 writeInt(st
, LongInt(mIVal
));
897 writeInt(st
, LongInt(mIVal2
));
902 if (mMaxDim
>= 0) then raise Exception
.Create(Format('pos/size array in field ''%s'' cannot be written', [mName
]));
903 writeInt(st
, Word(mIVal
));
904 writeInt(st
, Word(mIVal2
));
917 else raise Exception
.Create('ketmar forgot to handle some field type');
922 procedure TDynField
.writeTo (wr
: TTextWriter
);
926 first
, found
: Boolean;
931 TEBS
.TNone
: begin end;
934 if (mRecRef
= nil) then
936 if (mType
= TType
.TTrigData
) then wr
.put('{}'#10) else wr
.put('null;'#10);
938 else if (Length(mRecRef
.mId
) = 0) then
940 mRecRef
.writeTo(wr
, false); // only data, no header
951 //def := mOwner.mOwner;
952 //es := def.findEBSType(mEBSTypeName);
954 if (mEBSType
<> nil) and (mEBSType
is TDynEBS
) then es
:= (mEBSType
as TDynEBS
);
955 if (es
= nil) or (not es
.mIsEnum
) then raise Exception
.Create(Format('record enum type ''%s'' for field ''%s'' not found', [mEBSTypeName
, mName
]));
956 for f
:= 0 to High(es
.mVals
) do
958 if (es
.mVals
[f
] = mIVal
) then
965 raise Exception
.Create(Format('value %d in record enum type ''%s'' for field ''%s'' not found', [mIVal
, mEBSTypeName
, mName
]));
969 //def := mOwner.mOwner;
970 //es := def.findEBSType(mEBSTypeName);
972 if (mEBSType
<> nil) and (mEBSType
is TDynEBS
) then es
:= (mEBSType
as TDynEBS
);
973 if (es
= nil) or es
.mIsEnum
then raise Exception
.Create(Format('record bitset type ''%s'' for field ''%s'' not found', [mEBSTypeName
, mName
]));
977 for f
:= 0 to High(es
.mVals
) do
979 if (es
.mVals
[f
] = 0) then
986 raise Exception
.Create(Format('value %d in record bitset type ''%s'' for field ''%s'' not found', [0, mEBSTypeName
, mName
]));
993 if ((mIVal
and mask
) <> 0) then
996 for f
:= 0 to High(es
.mVals
) do
998 if (es
.mVals
[f
] = mask
) then
1000 if not first
then wr
.put('+') else first
:= false;
1006 if not found
then raise Exception
.Create(Format('value %d in record bitset type ''%s'' for field ''%s'' not found', [mask
, mEBSTypeName
, mName
]));
1013 else raise Exception
.Create('ketmar forgot to handle some EBS type');
1019 if (mIVal
= 0) then wr
.put('false;'#10) else wr
.put('true;'#10);
1024 if (mMaxDim
= 0) then raise Exception
.Create(Format('invalid string size definition for field ''%s''', [mName
]));
1025 wr
.put(quoteStr(mSVal
));
1036 wr
.put('%d;'#10, [mIVal
]);
1041 wr
.put(quoteStr(mSVal
));
1048 wr
.put('(%d %d);'#10, [mIVal
, mIVal2
]);
1061 else raise Exception
.Create('ketmar forgot to handle some field type');
1063 raise Exception
.Create(Format('cannot parse field ''%s'' yet', [mName
]));
1066 procedure TDynField
.parseBinValue (st
: TStream
);
1068 rec
, rc
: TDynRecord
;
1076 TEBS
.TNone
: begin end;
1079 // this must be triggerdata
1080 if (mType
= TType
.TTrigData
) then
1082 assert(mMaxDim
> 0);
1084 // find trigger definition
1085 tfld
:= rec
.trigTypeField();
1086 if (tfld
= nil) then raise Exception
.Create(Format('triggerdata value for field ''%s'' in record ''%s'' without TriggerType field', [mName
, rec
.mName
]));
1087 rc
:= mOwner
.mOwner
.findTrigFor(tfld
.mSVal
); // find in mapdef
1088 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
]));
1090 rc
.mHeaderRec
:= mOwner
.mHeaderRec
;
1092 rc
.parseBinValue(st
, true);
1103 // not a trigger data
1105 TType
.TByte
: f
:= readShortInt(st
);
1106 TType
.TUByte
: f
:= readByte(st
);
1107 TType
.TShort
: f
:= readSmallInt(st
);
1108 TType
.TUShort
: f
:= readWord(st
);
1109 TType
.TInt
: f
:= readLongInt(st
);
1110 TType
.TUInt
: f
:= readLongWord(st
);
1111 else raise Exception
.Create(Format('invalid non-numeric type ''%s'' for field ''%s'' of record ''%s''', [getTypeName(mType
), mName
, mEBSTypeName
]));
1113 if mAsMonsterId
then Dec(f
);
1114 if (f
< 0) then mRecRefId
:= '' else mRecRefId
:= Format('%s%d', [mEBSTypeName
, f
]);
1122 assert(mMaxDim
< 0);
1124 TType
.TByte
: f
:= readShortInt(st
);
1125 TType
.TUByte
: f
:= readByte(st
);
1126 TType
.TShort
: f
:= readSmallInt(st
);
1127 TType
.TUShort
: f
:= readWord(st
);
1128 TType
.TInt
: f
:= readLongInt(st
);
1129 TType
.TUInt
: f
:= readLongWord(st
);
1130 else raise Exception
.Create(Format('invalid non-numeric type ''%s'' for field ''%s'' of record ''%s''', [getTypeName(mType
), mName
, mEBSTypeName
]));
1133 if (mEBSType
<> nil) and (mEBSType
is TDynEBS
) then es
:= (mEBSType
as TDynEBS
);
1134 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
]));
1136 // build enum/bitfield values
1137 if (mEBS
= TEBS
.TEnum
) then
1139 mSVal
:= es
.nameByValue(mIVal
);
1140 if (Length(mSVal
) = 0) then raise Exception
.Create(Format('record enum type ''%s'' for field ''%s'' has invalid value %d', [mEBSTypeName
, mName
, mIVal
]));
1144 // special for 'none'
1147 mSVal
:= es
.nameByValue(mIVal
);
1148 if (Length(mSVal
) = 0) then raise Exception
.Create(Format('record bitset type ''%s'' for field ''%s'' has invalid value %d', [mEBSTypeName
, mName
, mIVal
]));
1154 while (mask
<> 0) do
1156 if ((mIVal
and mask
) <> 0) then
1158 s
:= es
.nameByValue(mask
);
1159 if (Length(s
) = 0) then raise Exception
.Create(Format('record bitset type ''%s'' for field ''%s'' has invalid value %d', [mEBSTypeName
, mName
, mask
]));
1160 if (Length(mSVal
) <> 0) then mSVal
+= '+';
1167 //writeln('ebs <', es.mName, '>: ', mSVal);
1171 else raise Exception
.Create('ketmar forgot to handle some EBS type');
1178 if (f
<> 0) then f
:= 1;
1179 if mNegBool
then f
:= 1-f
;
1186 if (mMaxDim
< 0) then
1188 mIVal
:= readByte(st
);
1193 GetMem(tdata
, mMaxDim
);
1195 st
.ReadBuffer(tdata
^, mMaxDim
);
1197 while (f
< mMaxDim
) and (tdata
[f
] <> 0) do Inc(f
);
1200 SetLength(mSVal
, f
);
1201 Move(tdata
^, PChar(mSVal
)^, f
);
1202 mSVal
:= win2utf(mSVal
);
1211 TType
.TByte
: begin mIVal
:= readShortInt(st
); mDefined
:= true; exit
; end;
1212 TType
.TUByte
: begin mIVal
:= readByte(st
); mDefined
:= true; exit
; end;
1213 TType
.TShort
: begin mIVal
:= readSmallInt(st
); mDefined
:= true; exit
; end;
1214 TType
.TUShort
: begin mIVal
:= readWord(st
); mDefined
:= true; exit
; end;
1215 TType
.TInt
: begin mIVal
:= readLongInt(st
); mDefined
:= true; exit
; end;
1216 TType
.TUInt
: begin mIVal
:= readLongWord(st
); mDefined
:= true; exit
; end;
1219 raise Exception
.Create('cannot read strings from binaries yet');
1224 mIVal
:= readLongInt(st
);
1225 mIVal2
:= readLongInt(st
);
1231 mIVal
:= readWord(st
);
1232 mIVal2
:= readWord(st
);
1246 else raise Exception
.Create('ketmar forgot to handle some field type');
1248 raise Exception
.Create(Format('cannot parse field ''%s'' yet', [mName
]));
1252 procedure TDynField
.parseValue (pr
: TTextParser
);
1254 procedure parseInt (min
, max
: Integer);
1256 mIVal
:= pr
.expectInt();
1257 if (mIVal
< min
) or (mIVal
> max
) then raise Exception
.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType
), mName
]));
1262 rec
, rc
: TDynRecord
;
1268 // if this field should contain struct, convert type and parse struct
1270 TEBS
.TNone
: begin end;
1273 // ugly hack. sorry.
1274 if (mType
= TType
.TTrigData
) then
1276 pr
.expectTT(pr
.TTBegin
);
1277 if (pr
.tokType
= pr
.TTEnd
) then
1281 pr
.expectTT(pr
.TTEnd
);
1286 // find trigger definition
1287 tfld
:= rec
.trigTypeField();
1288 if (tfld
= nil) then raise Exception
.Create(Format('triggerdata value for field ''%s'' in record ''%s'' without ''type'' field', [mName
, rec
.mName
]));
1289 rc
:= mOwner
.mOwner
.findTrigFor(tfld
.mSVal
); // find in mapdef
1290 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
]));
1292 rc
.mHeaderRec
:= mOwner
.mHeaderRec
;
1293 //writeln(rc.definition);
1295 rc
.parseValue(pr
, true);
1303 pr
.eatTT(pr
.TTSemi
); // hack: allow (but don't require) semicolon after inline records
1306 // other record types
1307 if (pr
.tokType
= pr
.TTId
) then
1309 if pr
.eatId('null') then
1315 rec
:= mOwner
.findRecordByTypeId(mEBSTypeName
, pr
.tokStr
);
1316 if (rec
= nil) then raise Exception
.Create(Format('record ''%s'' (%s) value for field ''%s'' not found', [pr
.tokStr
, mEBSTypeName
, mName
]));
1321 pr
.expectTT(pr
.TTSemi
);
1324 else if (pr
.tokType
= pr
.TTBegin
) then
1326 //rec := mOwner.mOwner.findRecType(mEBSTypeName); // find in mapdef
1328 if (mEBSType
<> nil) and (mEBSType
is TDynRecord
) then rec
:= (mEBSType
as TDynRecord
);
1329 if (rec
= nil) then raise Exception
.Create(Format('record type ''%s'' for field ''%s'' not found', [mEBSTypeName
, mName
]));
1331 rc
.mHeaderRec
:= mOwner
.mHeaderRec
;
1335 mOwner
.addRecordByType(mEBSTypeName
, rc
);
1336 pr
.eatTT(pr
.TTSemi
); // hack: allow (but don't require) semicolon after inline records
1339 pr
.expectTT(pr
.TTBegin
);
1343 //es := mOwner.mOwner.findEBSType(mEBSTypeName); // find in mapdef
1345 if (mEBSType
<> nil) and (mEBSType
is TDynEBS
) then es
:= (mEBSType
as TDynEBS
);
1346 if (es
= nil) or (not es
.mIsEnum
) then raise Exception
.Create(Format('record enum type ''%s'' for field ''%s'' not found', [mEBSTypeName
, mName
]));
1347 tk
:= pr
.expectId();
1348 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
]));
1349 mIVal
:= es
.field
[tk
];
1351 //writeln('ENUM ', mEBSName, '; element <', mSVal, '> with value ', mIVal);
1353 pr
.expectTT(pr
.TTSemi
);
1358 //es := mOwner.mOwner.findEBSType(mEBSTypeName); // find in mapdef
1360 if (mEBSType
<> nil) and (mEBSType
is TDynEBS
) then es
:= (mEBSType
as TDynEBS
);
1361 if (es
= nil) or es
.mIsEnum
then raise Exception
.Create(Format('record bitset type ''%s'' for field ''%s'' not found', [mEBSTypeName
, mName
]));
1365 tk
:= pr
.expectId();
1366 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
]));
1367 mIVal
:= mIVal
or es
.field
[tk
];
1369 if (pr
.tokType
<> pr
.TTDelim
) or ((pr
.tokChar
<> '|') and (pr
.tokChar
<> '+')) then break
;
1370 if mBitSetUnique
then raise Exception
.Create(Format('record bitset of type ''%s'' for field ''%s'' expects only one value', [tk
, mEBSTypeName
, mName
]));
1371 //pr.expectDelim('|');
1372 pr
.skipToken(); // plus or pipe
1375 pr
.expectTT(pr
.TTSemi
);
1378 else raise Exception
.Create('ketmar forgot to handle some EBS type');
1384 if pr
.eatId('true') or pr
.eatId('tan') or pr
.eatId('yes') then mIVal
:= 1
1385 else if pr
.eatId('false') or pr
.eatId('ona') or pr
.eatId('no') then mIVal
:= 0
1386 else raise Exception
.Create(Format('invalid bool value for field ''%s''', [mName
]));
1388 pr
.expectTT(pr
.TTSemi
);
1393 if (mMaxDim
= 0) then raise Exception
.Create(Format('invalid string size definition for field ''%s''', [mName
]));
1394 mSVal
:= pr
.expectStr(true);
1395 if (mMaxDim
< 0) then
1398 if (Length(mSVal
) <> 1) then raise Exception
.Create(Format('invalid string size for field ''%s''', [mName
]));
1399 mIVal
:= Integer(mSVal
[1]);
1405 if (Length(mSVal
) > mMaxDim
) then raise Exception
.Create(Format('invalid string size for field ''%s''', [mName
]));
1408 pr
.expectTT(pr
.TTSemi
);
1413 parseInt(-128, 127);
1414 pr
.expectTT(pr
.TTSemi
);
1420 pr
.expectTT(pr
.TTSemi
);
1425 parseInt(-32768, 32768);
1426 pr
.expectTT(pr
.TTSemi
);
1432 pr
.expectTT(pr
.TTSemi
);
1437 parseInt(Integer($80000000), $7fffffff);
1438 pr
.expectTT(pr
.TTSemi
);
1443 parseInt(0, $7fffffff); //FIXME
1444 pr
.expectTT(pr
.TTSemi
);
1449 mSVal
:= pr
.expectStr(true);
1451 pr
.expectTT(pr
.TTSemi
);
1457 if pr
.eatDelim('[') then edim
:= ']' else begin pr
.expectDelim('('); edim
:= ')'; end;
1458 mIVal
:= pr
.expectInt();
1459 if (mType
= TType
.TSize
) then
1461 if (mIVal
< 0) or (mIVal
> 32767) then raise Exception
.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType
), mName
]));
1463 mIVal2
:= pr
.expectInt();
1464 if (mType
= TType
.TSize
) then
1466 if (mIVal2
< 0) or (mIVal2
> 32767) then raise Exception
.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType
), mName
]));
1469 pr
.expectDelim(edim
);
1470 pr
.expectTT(pr
.TTSemi
);
1483 else raise Exception
.Create('ketmar forgot to handle some field type');
1485 raise Exception
.Create(Format('cannot parse field ''%s'' yet', [mName
]));
1489 // ////////////////////////////////////////////////////////////////////////// //
1490 constructor TDynRecord
.Create (pr
: TTextParser
);
1492 if (pr
= nil) then raise Exception
.Create('cannot create record type without type definition');
1496 mFields
:= TDynFieldList
.Create();
1505 constructor TDynRecord
.Create ();
1509 mFields
:= TDynFieldList
.Create();
1516 destructor TDynRecord
.Destroy ();
1527 function TDynRecord
.findByName (const aname
: AnsiString): Integer; inline;
1530 while (result
< mFields
.count
) do
1532 if StrEqu(aname
, mFields
[result
].mName
) then exit
;
1539 function TDynRecord
.hasByName (const aname
: AnsiString): Boolean; inline;
1541 result
:= (findByName(aname
) >= 0);
1545 function TDynRecord
.getFieldByName (const aname
: AnsiString): TDynField
; inline;
1549 f
:= findByName(aname
);
1550 if (f
>= 0) then result
:= mFields
[f
] else result
:= nil;
1554 function TDynRecord
.getIsTrigData (): Boolean; inline;
1556 result
:= (Length(mTrigTypes
) > 0);
1560 function TDynRecord
.getIsForTrig (const aname
: AnsiString): Boolean; inline;
1565 for f
:= 0 to High(mTrigTypes
) do if StrEqu(mTrigTypes
[f
], aname
) then exit
;
1570 function TDynRecord
.clone (): TDynRecord
;
1575 result
:= TDynRecord
.Create();
1576 result
.mOwner
:= mOwner
;
1578 result
.mPasName
:= mPasName
;
1579 result
.mName
:= mName
;
1580 result
.mSize
:= mSize
;
1581 if (mFields
.count
> 0) then
1583 result
.mFields
.capacity
:= mFields
.count
;
1584 for fld
in mFields
do result
.mFields
.append(fld
.clone(result
));
1586 SetLength(result
.mTrigTypes
, Length(mTrigTypes
));
1587 for f
:= 0 to High(mTrigTypes
) do result
.mTrigTypes
[f
] := mTrigTypes
[f
];
1588 result
.mHeader
:= mHeader
;
1589 result
.mBinBlock
:= mBinBlock
;
1590 result
.mHeaderRec
:= mHeaderRec
;
1594 function TDynRecord
.findRecordByTypeId (const atypename
, aid
: AnsiString): TDynRecord
;
1600 if (Length(aid
) = 0) then exit
;
1602 fld
:= mHeaderRec
.field
[atypename
];
1603 if (fld
= nil) then exit
;
1604 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
]));
1606 if (fld
.mRVal
<> nil) then
1608 for rec
in fld
.mRVal
do
1610 if StrEqu(rec
.mId
, aid
) then begin result
:= rec
; exit
; end;
1617 function TDynRecord
.findRecordNumByType (const atypename
: AnsiString; rc
: TDynRecord
): Integer;
1624 fld
:= mHeaderRec
.field
[atypename
];
1625 if (fld
= nil) then exit
;
1626 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
]));
1628 if (fld
.mRVal
<> nil) then
1630 for f
:= 0 to fld
.mRVal
.count
-1 do
1632 if (fld
.mRVal
[f
] = rc
) then begin result
:= f
; exit
; end;
1639 procedure TDynRecord
.addRecordByType (const atypename
: AnsiString; rc
: TDynRecord
);
1644 fld
:= mHeaderRec
.field
[atypename
];
1648 fld
:= TDynField
.Create(atypename
, TDynField
.TType
.TList
);
1649 fld
.mOwner
:= mHeaderRec
;
1650 mHeaderRec
.mFields
.append(fld
);
1652 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
]));
1654 if (fld
.mRVal
= nil) then fld
.mRVal
:= TDynRecList
.Create();
1655 fld
.mRVal
.append(rc
);
1659 function TDynRecord
.isSimpleEqu (rec
: TDynRecord
): Boolean;
1663 if (rec
= nil) then begin result
:= false; exit
; end; // self.mRecRef can't be `nil` here
1664 if (rec
= self
) then begin result
:= true; exit
; end;
1665 if (mFields
.count
<> rec
.mFields
.count
) then begin result
:= false; exit
; end;
1667 for f
:= 0 to mFields
.count
-1 do
1669 if not mFields
[f
].isSimpleEqu(rec
.mFields
[f
]) then exit
;
1675 function TDynRecord
.trigTypeField (): TDynField
;
1680 for fld
in mFields
do
1682 if (fld
.mEBS
<> TDynField
.TEBS
.TEnum
) then continue
;
1683 if not (fld
.mEBSType
is TDynEBS
) then continue
;
1684 es
:= (fld
.mEBSType
as TDynEBS
);
1686 if StrEqu(es
.mName
, 'TriggerType') then begin result
:= fld
; exit
; end;
1692 // number of records of the given instance
1693 function TDynRecord
.instanceCount (const typename
: AnsiString): Integer;
1698 fld
:= field
[typename
];
1699 if (fld
<> nil) and (fld
.mType
= fld
.TType
.TList
) then result
:= fld
.mRVal
.count
;
1703 procedure TDynRecord
.parseDef (pr
: TTextParser
);
1708 if pr
.eatId('TriggerData') then
1711 if pr
.eatDelim('(') then
1715 while pr
.eatTT(pr
.TTComma
) do begin end;
1716 if pr
.eatDelim(')') then break
;
1717 tdn
:= pr
.expectId();
1718 if isForTrig
[tdn
] then raise Exception
.Create(Format('duplicate trigdata ''%s'' trigtype ''%s''', [mName
, tdn
]));
1719 SetLength(mTrigTypes
, Length(mTrigTypes
)+1);
1720 mTrigTypes
[High(mTrigTypes
)] := tdn
;
1725 tdn
:= pr
.expectId();
1726 SetLength(mTrigTypes
, 1);
1727 mTrigTypes
[0] := tdn
;
1729 mName
:= 'TriggerData';
1733 mPasName
:= pr
.expectId(); // pascal record name
1735 mName
:= pr
.expectStr();
1736 while (pr
.tokType
<> pr
.TTBegin
) do
1738 if pr
.eatId('header') then begin mHeader
:= true; continue
; end;
1739 if pr
.eatId('size') then
1741 if (mSize
> 0) then raise Exception
.Create(Format('duplicate `size` in record ''%s''', [mName
]));
1742 mSize
:= pr
.expectInt();
1743 if (mSize
< 1) then raise Exception
.Create(Format('invalid record ''%s'' size: %d', [mName
, mSize
]));
1744 pr
.expectId('bytes');
1747 if pr
.eatId('binblock') then
1749 if (mBinBlock
>= 0) then raise Exception
.Create(Format('duplicate `binblock` in record ''%s''', [mName
]));
1750 mBinBlock
:= pr
.expectInt();
1751 if (mBinBlock
< 1) then raise Exception
.Create(Format('invalid record ''%s'' binblock: %d', [mName
, mBinBlock
]));
1757 pr
.expectTT(pr
.TTBegin
);
1759 while (pr
.tokType
<> pr
.TTEnd
) do
1761 fld
:= TDynField
.Create(pr
);
1762 if hasByName(fld
.name
) then begin fld
.Free(); raise Exception
.Create(Format('duplicate field ''%s''', [fld
.name
])); end;
1765 mFields
.append(fld
);
1768 pr
.expectTT(pr
.TTEnd
);
1772 function TDynRecord
.pasdef (): AnsiString;
1784 result
:= ' '+mPasName
+' = packed record'#10;
1786 for fld
in mFields
do
1788 if fld
.mInternal
then continue
;
1789 if (fld
.mBinOfs
< 0) then continue
;
1790 result
+= ' '+fld
.pasdef
+#10;
1792 result
+= ' end;'#10;
1796 function TDynRecord
.definition (): AnsiString;
1803 result
:= 'TriggerData for ';
1804 if (Length(mTrigTypes
) > 1) then
1807 for f
:= 0 to High(mTrigTypes
) do
1809 if (f
<> 0) then result
+= ', ';
1810 result
+= mTrigTypes
[f
];
1816 result
+= mTrigTypes
[0];
1822 result
:= mPasName
+' is '+quoteStr(mName
);
1823 if (mSize
>= 0) then result
+= Format(' size %d bytes', [mSize
]);
1824 if mHeader
then result
+= ' header';
1827 for f
:= 0 to mFields
.count
-1 do
1830 result
+= mFields
[f
].definition
;
1837 procedure TDynRecord
.parseBinValue (st
: TStream
; forceData
: Boolean=false);
1843 loaded
: array[0..255] of Boolean;
1844 rec
, rect
: TDynRecord
;
1847 mst
: TSFSMemoryChunkStream
= nil;
1849 procedure linkNames (rec
: TDynRecord
);
1854 //writeln('*** rec: ', rec.mName, '.', rec.mId, ' (', rec.mFields.count, ')');
1855 for fld
in rec
.mFields
do
1857 if (fld
.mType
= TDynField
.TType
.TTrigData
) then
1859 if (fld
.mRecRef
<> nil) then linkNames(fld
.mRecRef
);
1862 if (Length(fld
.mRecRefId
) = 0) then continue
;
1863 assert(fld
.mEBSType
<> nil);
1864 rt
:= findRecordByTypeId(fld
.mEBSTypeName
, fld
.mRecRefId
);
1867 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
);
1868 //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]));
1870 //writeln(' ', rec.mName, '.', rec.mId, ':', fld.mName, ' -> ', rt.mName, '.', rt.mId, ' (', fld.mEBSTypeName, '.', fld.mRecRefId, ')');
1871 fld
.mRecRefId
:= '';
1873 fld
.mDefined
:= true;
1875 for fld
in rec
.mFields
do
1877 //writeln(' ', fld.mName);
1878 fld
.fixDefaultValue(); // just in case
1883 for f
:= 0 to High(loaded
) do loaded
[f
] := false;
1884 mst
:= TSFSMemoryChunkStream
.Create(nil, 0);
1886 if mHeader
and not forceData
then
1888 // parse map file as sequence of blocks
1890 st
.ReadBuffer(sign
[1], 4);
1891 if (sign
<> 'MAP'#1) then raise Exception
.Create('invalid binary map signature');
1893 while (st
.position
< st
.size
) do
1895 btype
:= readByte(st
);
1896 if (btype
= 0) then break
; // no more blocks
1897 readLongWord(st
); // reserved
1898 bsize
:= readLongInt(st
);
1899 {$IF DEFINED(D2D_XDYN_DEBUG)}writeln('btype=', btype
, '; bsize=', bsize
);{$ENDIF}
1900 if (bsize
< 0) or (bsize
> $1fffffff) then raise Exception
.Create(Format('block of type %d has invalid size %d', [btype
, bsize
]));
1901 if loaded
[btype
] then raise Exception
.Create(Format('block of type %d already loaded', [btype
]));
1902 loaded
[btype
] := true;
1903 // find record type for this block
1905 for rec
in mOwner
.recTypes
do if (rec
.mBinBlock
= btype
) then begin rect
:= rec
; break
; end;
1906 if (rect
= nil) then raise Exception
.Create(Format('block of type %d has no corresponding record', [btype
]));
1907 //writeln('found type ''', rec.mName, ''' for block type ', btype);
1908 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
]));
1910 if (rect
.mHeader
) then
1912 if (bsize
<> mSize
) then raise Exception
.Create(Format('header block of type %d has invalid number of records', [btype
]));
1914 st
.ReadBuffer(buf
^, bsize
);
1915 mst
.setup(buf
, mSize
);
1916 parseBinValue(mst
, true); // force parsing data
1920 // create list for this type
1921 fld
:= TDynField
.Create(rec
.mName
, TDynField
.TType
.TList
);
1923 mFields
.append(fld
);
1927 st
.ReadBuffer(buf
^, bsize
);
1928 for f
:= 0 to (bsize
div rec
.mSize
)-1 do
1930 mst
.setup(buf
+f
*rec
.mSize
, rec
.mSize
);
1931 rec
:= rect
.clone();
1932 rec
.mHeaderRec
:= self
;
1933 rec
.parseBinValue(mst
);
1934 rec
.mId
:= Format('%s%d', [rec
.mName
, f
]);
1935 fld
.mRVal
.append(rec
);
1936 //writeln('parsed ''', rec.mId, '''...');
1942 //st.position := st.position+bsize;
1945 for fld
in mFields
do
1947 if (fld
.mType
<> TDynField
.TType
.TList
) then continue
;
1948 for rec
in fld
.mRVal
do linkNames(rec
);
1954 if StrEqu(mName
, 'TriggerData') then mSize
:= Integer(st
.size
-st
.position
);
1955 if (mSize
< 1) then raise Exception
.Create(Format('cannot read record of type ''%s'' with unknown size', [mName
]));
1957 st
.ReadBuffer(buf
^, mSize
);
1958 for fld
in mFields
do
1960 if fld
.mInternal
then continue
;
1961 if (fld
.mBinOfs
< 0) then continue
;
1962 if (fld
.mBinOfs
>= st
.size
) then raise Exception
.Create(Format('record of type ''%s'' has invalid field ''%s''', [fld
.mName
]));
1963 mst
.setup(buf
+fld
.mBinOfs
, mSize
-fld
.mBinOfs
);
1964 //writeln('parsing ''', mName, '.', fld.mName, '''...');
1965 fld
.parseBinValue(mst
);
1969 if (buf
<> nil) then FreeMem(buf
);
1974 procedure TDynRecord
.writeBinTo (st
: TStream
; trigbufsz
: Integer=-1; onlyFields
: Boolean=false);
1977 rec
, rv
: TDynRecord
;
1980 blk
, blkmax
: Integer;
1985 if (trigbufsz
< 0) then
1987 if (mBinBlock
< 1) then raise Exception
.Create('cannot write binary record without block number');
1988 if (mSize
< 1) then raise Exception
.Create('cannot write binary record without size');
1997 FillChar(buf
^, bufsz
, 0);
1998 ws
:= TSFSMemoryChunkStream
.Create(buf
, bufsz
);
2000 // write normal fields
2001 for fld
in mFields
do
2004 if (fld
.mType
= fld
.TType
.TList
) then continue
; // later
2005 if fld
.mInternal
then continue
;
2006 if (fld
.mBinOfs
< 0) then continue
;
2007 if (fld
.mBinOfs
>= bufsz
) then raise Exception
.Create('binary value offset is outside of the buffer');
2008 TSFSMemoryChunkStream(ws
).setup(buf
+fld
.mBinOfs
, bufsz
-fld
.mBinOfs
);
2009 //writeln('writing field <', fld.mName, '>');
2013 // write block with normal fields
2014 if mHeader
and not onlyFields
then
2016 //writeln('writing header...');
2017 // signature and version
2018 writeIntBE(st
, LongWord($4D415001));
2019 writeInt(st
, Byte(mBinBlock
)); // type
2020 writeInt(st
, LongWord(0)); // reserved
2021 writeInt(st
, LongWord(bufsz
)); // size
2023 st
.WriteBuffer(buf
^, bufsz
);
2025 ws
.Free(); ws
:= nil;
2026 FreeMem(buf
); buf
:= nil;
2028 // write other blocks, if any
2029 if mHeader
and not onlyFields
then
2033 for fld
in mFields
do
2036 if (fld
.mType
= fld
.TType
.TList
) then
2038 if (fld
.mRVal
= nil) or (fld
.mRVal
.count
= 0) then continue
;
2039 rec
:= mOwner
.findRecType(fld
.mName
);
2040 if (rec
= nil) then continue
;
2041 if (rec
.mBinBlock
<= 0) then continue
;
2042 if (blkmax
< rec
.mBinBlock
) then blkmax
:= rec
.mBinBlock
;
2046 for blk
:= 1 to blkmax
do
2048 if (blk
= mBinBlock
) then continue
;
2050 for fld
in mFields
do
2053 if (fld
.mType
= fld
.TType
.TList
) then
2055 if (fld
.mRVal
= nil) or (fld
.mRVal
.count
= 0) then continue
;
2056 rec
:= mOwner
.findRecType(fld
.mName
);
2057 if (rec
= nil) then continue
;
2058 if (rec
.mBinBlock
<> blk
) then continue
;
2059 if (ws
= nil) then ws
:= TMemoryStream
.Create();
2060 for rv
in fld
.mRVal
do rv
.writeBinTo(ws
);
2066 blksz
:= Integer(ws
.position
);
2068 writeInt(st
, Byte(blk
)); // type
2069 writeInt(st
, LongWord(0)); // reserved
2070 writeInt(st
, LongWord(blksz
)); // size
2071 st
.CopyFrom(ws
, blksz
);
2077 writeInt(st
, Byte(0));
2078 writeInt(st
, LongWord(0));
2079 writeInt(st
, LongWord(0));
2083 if (buf
<> nil) then FreeMem(buf
);
2088 procedure TDynRecord
.writeTo (wr
: TTextWriter
; putHeader
: Boolean=true);
2096 if (Length(mId
) > 0) then begin wr
.put(' '); wr
.put(mId
); end;
2102 for fld
in mFields
do
2105 if (fld
.mType
= fld
.TType
.TList
) then
2107 if not mHeader
then raise Exception
.Create('record list in non-header record');
2108 if (fld
.mRVal
<> nil) then
2110 for rec
in fld
.mRVal
do
2112 if (Length(rec
.mId
) = 0) then continue
;
2114 rec
.writeTo(wr
, true);
2119 if fld
.mInternal
then continue
;
2120 if fld
.mOmitDef
and fld
.isDefaultValue
then continue
;
2132 procedure TDynRecord
.parseValue (pr
: TTextParser
; beginEaten
: Boolean=false);
2135 rec
, trc
, rv
: TDynRecord
;
2137 if (mOwner
= nil) then raise Exception
.Create(Format('can''t parse record ''%s'' value without owner', [mName
]));
2143 if (not beginEaten
) and (pr
.tokType
= pr
.TTId
) then mId
:= pr
.expectId();
2147 assert(mHeaderRec
= self
);
2150 //writeln('parsing record <', mName, '>');
2151 if not beginEaten
then pr
.expectTT(pr
.TTBegin
);
2152 while (pr
.tokType
<> pr
.TTEnd
) do
2154 if (pr
.tokType
<> pr
.TTId
) then raise Exception
.Create('identifier expected');
2155 //writeln('<', mName, '.', pr.tokStr, '>');
2160 // add records with this type (if any)
2161 trc
:= mOwner
.findRecType(pr
.tokStr
);
2162 if (trc
<> nil) then
2165 rec
.mHeaderRec
:= mHeaderRec
;
2169 if (Length(rec
.mId
) > 0) then
2171 fld
:= field
[pr
.tokStr
];
2172 if (fld
<> nil) and (fld
.mRVal
<> nil) then
2174 for rv
in fld
.mRVal
do
2176 if (Length(rv
.mId
) > 0) and StrEqu(rv
.mId
, rec
.mId
) then raise Exception
.Create(Format('duplicate thing ''%s'' in record ''%s''', [fld
.mName
, mName
]));
2180 addRecordByType(rec
.mName
, rec
);
2190 fld
:= field
[pr
.tokStr
];
2191 if (fld
<> nil) then
2193 if fld
.defined
then raise Exception
.Create(Format('duplicate field ''%s'' in record ''%s''', [fld
.mName
, mName
]));
2194 if fld
.internal
then raise Exception
.Create(Format('internal field ''%s'' in record ''%s''', [fld
.mName
, mName
]));
2200 // something is wrong
2201 raise Exception
.Create(Format('unknown field ''%s'' in record ''%s''', [pr
.tokStr
, mName
]));
2203 pr
.expectTT(pr
.TTEnd
);
2204 // fix field defaults
2205 for fld
in mFields
do fld
.fixDefaultValue();
2206 //writeln('done parsing record <', mName, '>');
2210 // ////////////////////////////////////////////////////////////////////////// //
2211 constructor TDynEBS
.Create (pr
: TTextParser
);
2218 destructor TDynEBS
.Destroy ();
2225 procedure TDynEBS
.cleanup ();
2236 function TDynEBS
.findByName (const aname
: AnsiString): Integer;
2239 while (result
< Length(mIds
)) do
2241 if StrEqu(aname
, mIds
[result
]) then exit
;
2248 function TDynEBS
.hasByName (const aname
: AnsiString): Boolean; inline;
2250 result
:= (findByName(aname
) >= 0);
2254 function TDynEBS
.getFieldByName (const aname
: AnsiString): Integer; inline;
2258 f
:= findByName(aname
);
2259 if (f
>= 0) then result
:= mVals
[f
] else result
:= 0;
2263 function TDynEBS
.definition (): AnsiString;
2267 if mIsEnum
then result
:='enum ' else result
:= 'bitset ';
2271 if mIsEnum
then cv
:= 0 else cv
:= 1;
2272 for f
:= 0 to High(mIds
) do
2274 if (mIds
[f
] = mMaxName
) then continue
;
2275 result
+= ' '+mIds
[f
];
2276 if (mVals
[f
] <> cv
) then
2278 result
+= Format(' = %d', [mVals
[f
]]);
2279 if mIsEnum
then cv
:= mVals
[f
];
2284 result
+= Format(', // %d'#10, [mVals
[f
]]);
2286 if mIsEnum
then Inc(cv
) else if (mVals
[f
] = cv
) then cv
:= cv
shl 1;
2289 if (Length(mMaxName
) > 0) then result
+= ' '+mMaxName
+' = MAX,'#10;
2294 function TDynEBS
.pasdef (): AnsiString;
2298 result
:= '// '+mName
+#10'const'#10;
2300 for f
:= 0 to High(mIds
) do
2302 result
+= formatstrf(' %s = %d;'#10, [mIds
[f
], mVals
[f
]]);
2307 function TDynEBS
.nameByValue (v
: Integer): AnsiString;
2311 for f
:= 0 to High(mVals
) do
2313 if (mVals
[f
] = v
) then begin result
:= mIds
[f
]; exit
; end;
2319 procedure TDynEBS
.parseDef (pr
: TTextParser
);
2327 if pr
.eatId('enum') then mIsEnum
:= true
2328 else if pr
.eatId('bitset') then mIsEnum
:= false
2329 else pr
.expectId('enum');
2330 mName
:= pr
.expectId();
2331 mMaxVal
:= Integer($80000000);
2332 if mIsEnum
then cv
:= 0 else cv
:= 1;
2333 pr
.expectTT(pr
.TTBegin
);
2334 while (pr
.tokType
<> pr
.TTEnd
) do
2336 idname
:= pr
.expectId();
2337 for f
:= 0 to High(mIds
) do
2339 if StrEqu(mIds
[f
], idname
) then raise Exception
.Create(Format('duplicate field ''%s'' in enum/bitset ''%s''', [idname
, mName
]));
2341 if StrEqu(mMaxName
, idname
) then raise Exception
.Create(Format('duplicate field ''%s'' in enum/bitset ''%s''', [idname
, mName
]));
2346 if pr
.eatDelim('=') then
2348 if pr
.eatId('MAX') then
2350 if (Length(mMaxName
) > 0) then raise Exception
.Create(Format('duplicate max field ''%s'' in enum/bitset ''%s''', [idname
, mName
]));
2356 v
:= pr
.expectInt();
2357 if mIsEnum
then cv
:= v
;
2365 if mIsEnum
or (not hasV
) then
2367 if (mMaxVal
< v
) then mMaxVal
:= v
;
2369 SetLength(mIds
, Length(mIds
)+1);
2370 mIds
[High(mIds
)] := idname
;
2371 SetLength(mVals
, Length(mIds
));
2372 mVals
[High(mVals
)] := v
;
2374 if mIsEnum
or (not hasV
) then
2376 if mIsEnum
then Inc(cv
) else cv
:= cv
shl 1;
2379 if (pr
.tokType
= pr
.TTEnd
) then break
;
2380 pr
.expectTT(pr
.TTComma
);
2381 while pr
.eatTT(pr
.TTComma
) do begin end;
2383 pr
.expectTT(pr
.TTEnd
);
2385 if (Length(mMaxName
) > 0) then
2387 SetLength(mIds
, Length(mIds
)+1);
2388 mIds
[High(mIds
)] := mMaxName
;
2389 SetLength(mVals
, Length(mIds
));
2390 mVals
[High(mVals
)] := mMaxVal
;
2395 // ////////////////////////////////////////////////////////////////////////// //
2396 constructor TDynMapDef
.Create (pr
: TTextParser
);
2398 recTypes
:= TDynRecList
.Create();
2399 trigTypes
:= TDynRecList
.Create();
2400 ebsTypes
:= TDynEBSList
.Create();
2405 destructor TDynMapDef
.Destroy ();
2410 for rec
in recTypes
do rec
.Free();
2411 for rec
in trigTypes
do rec
.Free();
2412 for ebs
in ebsTypes
do ebs
.Free();
2423 function TDynMapDef
.getHeaderRecType (): TDynRecord
; inline;
2425 if (recTypes
.count
= 0) then raise Exception
.Create('no header in empty mapdef');
2426 result
:= recTypes
[0];
2430 function TDynMapDef
.findRecType (const aname
: AnsiString): TDynRecord
;
2434 for rec
in recTypes
do
2436 if StrEqu(rec
.name
, aname
) then begin result
:= rec
; exit
; end;
2442 function TDynMapDef
.findTrigFor (const aname
: AnsiString): TDynRecord
;
2446 for rec
in trigTypes
do
2448 if (rec
.isForTrig
[aname
]) then begin result
:= rec
; exit
; end;
2454 function TDynMapDef
.findEBSType (const aname
: AnsiString): TDynEBS
;
2458 for ebs
in ebsTypes
do
2460 if StrEqu(ebs
.name
, aname
) then begin result
:= ebs
; exit
; end;
2466 procedure TDynMapDef
.parseDef (pr
: TTextParser
);
2468 rec
, hdr
: TDynRecord
;
2472 // setup header links and type links
2473 procedure linkRecord (rec
: TDynRecord
);
2477 rec
.mHeaderRec
:= recTypes
[0];
2478 for fld
in rec
.mFields
do
2480 if (fld
.mType
= fld
.TType
.TTrigData
) then continue
;
2482 TDynField
.TEBS
.TNone
: begin end;
2483 TDynField
.TEBS
.TRec
:
2485 fld
.mEBSType
:= findRecType(fld
.mEBSTypeName
);
2486 if (fld
.mEBSType
= nil) then raise Exception
.Create(Format('field ''%s'' of type ''%s'' has no correcponding record definition', [fld
.mName
, fld
.mEBSTypeName
]));
2488 TDynField
.TEBS
.TEnum
,
2489 TDynField
.TEBS
.TBitSet
:
2491 fld
.mEBSType
:= findEBSType(fld
.mEBSTypeName
);
2492 if (fld
.mEBSType
= nil) then raise Exception
.Create(Format('field ''%s'' of type ''%s'' has no correcponding enum/bitset', [fld
.mName
, fld
.mEBSTypeName
]));
2493 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
]));
2499 // setup default values
2500 procedure fixRecordDefaults (rec
: TDynRecord
);
2504 for fld
in rec
.mFields
do if fld
.mHasDefault
then fld
.parseDefaultValue();
2511 if not pr
.skipBlanks() then break
;
2512 if (pr
.tokType
<> pr
.TTId
) then raise Exception
.Create('identifier expected');
2514 if (pr
.tokStr
= 'enum') or (pr
.tokStr
= 'bitset') then
2516 eb
:= TDynEBS
.Create(pr
);
2517 if (findEBSType(eb
.name
) <> nil) then
2520 raise Exception
.Create(Format('duplicate enum/bitset ''%s''', [eb
.name
]));
2523 ebsTypes
.append(eb
);
2524 //writeln(eb.definition); writeln;
2528 if (pr
.tokStr
= 'TriggerData') then
2530 rec
:= TDynRecord
.Create(pr
);
2531 for f
:= 0 to High(rec
.mTrigTypes
) do
2533 if (findTrigFor(rec
.mTrigTypes
[f
]) <> nil) then
2536 raise Exception
.Create(Format('duplicate trigdata ''%s''', [rec
.mTrigTypes
[f
]]));
2540 trigTypes
.append(rec
);
2541 //writeln(dr.definition); writeln;
2545 rec
:= TDynRecord
.Create(pr
);
2546 //writeln(dr.definition); writeln;
2547 if (findRecType(rec
.name
) <> nil) then begin rec
.Free(); raise Exception
.Create(Format('duplicate record ''%s''', [rec
.name
])); end;
2548 if (hdr
<> nil) and StrEqu(rec
.name
, hdr
.name
) then begin rec
.Free(); raise Exception
.Create(Format('duplicate record ''%s''', [rec
.name
])); end;
2552 if (hdr
<> nil) then begin rec
.Free(); raise Exception
.Create(Format('duplicate header record ''%s'' (previous is ''%s'')', [rec
.name
, hdr
.name
])); end;
2557 recTypes
.append(rec
);
2561 // put header record to top
2562 if (hdr
= nil) then raise Exception
.Create('header definition not found in mapdef');
2563 recTypes
.append(nil);
2564 for f
:= recTypes
.count
-1 downto 1 do recTypes
[f
] := recTypes
[f
-1];
2567 // setup header links and type links
2568 for rec
in recTypes
do linkRecord(rec
);
2569 for rec
in trigTypes
do linkRecord(rec
);
2571 // setup default values
2572 for rec
in recTypes
do fixRecordDefaults(rec
);
2573 for rec
in trigTypes
do fixRecordDefaults(rec
);
2577 // ////////////////////////////////////////////////////////////////////////// //
2578 function TDynMapDef
.parseMap (pr
: TTextParser
): TDynRecord
;
2580 res
: TDynRecord
= nil;
2584 pr
.expectId(headerType
.name
);
2585 res
:= headerType
.clone();
2586 res
.mHeaderRec
:= res
;
2590 except on E
: Exception
do
2599 function TDynMapDef
.parseBinMap (st
: TStream
): TDynRecord
;
2601 res
: TDynRecord
= nil;
2605 res
:= headerType
.clone();
2606 res
.mHeaderRec
:= res
;
2607 res
.parseBinValue(st
);
2610 except on E
: Exception
do
2619 function TDynMapDef
.pasdef (): AnsiString;
2628 result
+= '// ////////////////////////////////////////////////////////////////////////// //'#10;
2629 result
+= '// enums and bitsets'#10;
2630 for ebs
in ebsTypes
do result
+= #10+ebs
.pasdef();
2631 result
+= #10#10'// ////////////////////////////////////////////////////////////////////////// //'#10;
2632 result
+= '// records'#10'type'#10;
2633 for rec
in recTypes
do
2635 if (rec
.mSize
< 1) then continue
;
2636 result
+= rec
.pasdef();
2639 result
+= #10#10'// ////////////////////////////////////////////////////////////////////////// //'#10;
2640 result
+= '// triggerdata'#10'type'#10;
2641 result
+= ' TTriggerData = record'#10;
2642 result
+= ' case Byte of'#10;
2643 result
+= ' 0: (Default: Byte128);'#10;
2644 for rec
in trigTypes
do
2648 for tn
in rec
.mTrigTypes
do
2650 if needComma
then result
+= ', ' else needComma
:= true;
2654 for fld
in rec
.mFields
do
2656 if fld
.mInternal
then continue
;
2657 if (fld
.mBinOfs
< 0) then continue
;
2658 result
+= ' '+fld
.pasdef
+#10;
2662 result
+= ' end;'#10;