DEADSOFTWARE

textmaps: some code cleanup
[d2df-sdl.git] / src / shared / xdynrec.pas
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE a_modes.inc}
17 unit xdynrec;
19 interface
21 uses
22 Classes,
23 xparser, xstreams;
26 // ////////////////////////////////////////////////////////////////////////// //
27 type
28 TDynMapDef = class;
29 TDynRecord = class;
31 // this is base type for all scalars (and arrays)
32 TDynField = class
33 public
34 type
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)
42 type
43 TDynFieldArray = array of TDynField;
44 TDynRecordArray = array of TDynRecord;
46 private
47 type
48 TEBS = (TNone, TRec, TEnum, TBitSet);
50 private
51 mOwner: TDynRecord;
52 mPasName: AnsiString;
53 mName: AnsiString;
54 mType: TType;
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 mMaxDim: Integer; // for byte and char arrays; <0: not an array
61 mBinOfs: Integer; // offset in binary; <0 - none
62 mRecOfs: Integer; // offset in record; <0 - none
63 mSepPosSize: Boolean; // for points and sizes, use separate fields
64 mAsT: Boolean; // for points and sizes, use separate fields, names starts with `t`
65 mDefined: Boolean;
66 mHasDefault: Boolean;
67 mDefaultValueSet: Boolean;
68 mOmitDef: Boolean;
69 mInternal: Boolean;
70 // default values
71 mDefSVal: AnsiString;
72 mEBS: TEBS;
73 mEBSTypeName: AnsiString; // name of enum, bitset or record
74 mBitSetUnique: Boolean; // bitset can contain only one value
75 mNegBool: Boolean;
77 // temp
78 mDefId: AnsiString;
80 private
81 procedure cleanup ();
83 procedure parseDef (pr: TTextParser);
85 procedure fixDefaultValue ();
86 function isDefaultValue (): Boolean;
88 public
89 constructor Create (const aname: AnsiString; atype: TType);
90 constructor Create (pr: TTextParser);
91 destructor Destroy (); override;
93 class function getTypeName (t: TType): AnsiString;
95 function definition (): AnsiString;
97 function clone (): TDynField;
99 procedure parseValue (pr: TTextParser; curheader: TDynRecord);
100 procedure parseBinValue (st: TStream);
102 procedure writeTo (wr: TTextWriter);
103 procedure writeBinTo (st: TStream; curheader: TDynRecord);
105 // won't work for lists
106 function isSimpleEqu (fld: TDynField): Boolean;
108 public
109 property pasname: AnsiString read mPasName;
110 property name: AnsiString read mName;
111 property baseType: TType read mType;
112 property defined: Boolean read mDefined write mDefined;
113 property internal: Boolean read mInternal write mInternal;
114 //property ival: Integer read mIVal write setIVal;
115 //property sval: AnsiString read mSVal write setSVal;
116 property list: TDynRecordArray read mRVal write mRVal;
117 property maxdim: Integer read mMaxDim; // for fixed-size arrays
118 property binOfs: Integer read mBinOfs; // offset in binary; <0 - none
119 property recOfs: Integer read mRecOfs; // offset in record; <0 - none
120 property hasDefault: Boolean read mHasDefault;
121 property defsval: AnsiString read mDefSVal write mDefSVal;
122 property ebstype: TEBS read mEBS write mEBS;
123 property ebstypename: AnsiString read mEBSTypeName write mEBSTypeName; // enum/bitset name
125 property x: Integer read mIVal;
126 property w: Integer read mIVal;
127 property y: Integer read mIVal2;
128 property h: Integer read mIVal2;
129 end;
132 TDynRecord = class
133 private
134 mOwner: TDynMapDef;
135 mId: AnsiString;
136 mPasName: AnsiString;
137 mName: AnsiString;
138 mSize: Integer;
139 mFields: TDynField.TDynFieldArray;
140 mTrigTypes: array of AnsiString; // if this is triggerdata, we'll hold list of triggers here
141 mHeader: Boolean; // true for header record
142 mBinBlock: Integer; // -1: none
144 private
145 procedure parseDef (pr: TTextParser); // parse definition
147 function findByName (const aname: AnsiString): Integer; inline;
148 function hasByName (const aname: AnsiString): Boolean; inline;
149 function getFieldByName (const aname: AnsiString): TDynField; inline;
151 function getIsTrigData (): Boolean; inline;
152 function getIsForTrig (const aname: AnsiString): Boolean; inline;
154 public
155 constructor Create ();
156 constructor Create (pr: TTextParser); // parse definition
157 destructor Destroy (); override;
159 function definition (): AnsiString;
161 function clone (): TDynRecord;
163 procedure parseValue (pr: TTextParser; curheader: TDynRecord);
164 procedure parseBinValue (st: TStream);
166 procedure writeTo (wr: TTextWriter; putHeader: Boolean=true);
167 procedure writeBinTo (st: TStream; curheader: TDynRecord; trigbufsz: Integer=-1);
169 public
170 property id: AnsiString read mId; // for map parser
171 property pasname: AnsiString read mPasName;
172 property name: AnsiString read mName; // record name
173 property size: Integer read mSize; // size in bytes
174 property fields: TDynField.TDynFieldArray read mFields write mFields;
175 property has[const aname: AnsiString]: Boolean read hasByName;
176 property field[const aname: AnsiString]: TDynField read getFieldByName;
177 property isTrigData: Boolean read getIsTrigData;
178 property isForTrig[const aname: AnsiString]: Boolean read getIsForTrig;
179 end;
182 TDynEBS = class
183 private
184 mOwner: TDynMapDef;
185 mIsEnum: Boolean;
186 mName: AnsiString;
187 mIds: array of AnsiString;
188 mVals: array of Integer;
189 mMaxName: AnsiString; // MAX field
190 mMaxVal: Integer; // max value
192 private
193 procedure cleanup ();
195 procedure parseDef (pr: TTextParser); // parse definition
197 function findByName (const aname: AnsiString): Integer; inline;
198 function hasByName (const aname: AnsiString): Boolean; inline;
199 function getFieldByName (const aname: AnsiString): Integer; inline;
201 public
202 constructor Create (pr: TTextParser); // parse definition
203 destructor Destroy (); override;
205 function definition (): AnsiString;
207 public
208 property name: AnsiString read mName; // record name
209 property isEnum: Boolean read mIsEnum;
210 property has[const aname: AnsiString]: Boolean read hasByName;
211 property field[const aname: AnsiString]: Integer read getFieldByName;
212 end;
215 TDynMapDef = class
216 private
217 procedure addRecordByType (const atypename: AnsiString; rc: TDynRecord; curheader: TDynRecord);
218 function findRecordByTypeId (const atypename, aid: AnsiString; curheader: TDynRecord): TDynRecord;
219 function findRecordNumByType (const atypename: AnsiString; rc: TDynRecord; curheader: TDynRecord): Integer;
221 public
222 recTypes: array of TDynRecord; // [0] is always header
223 trigTypes: array of TDynRecord; // trigdata
224 ebsTypes: array of TDynEBS; // enums, bitsets
226 private
227 procedure parseDef (pr: TTextParser);
229 function getHeaderRecType (): TDynRecord; inline;
231 public
232 constructor Create (pr: TTextParser); // parses data definition
233 destructor Destroy (); override;
235 function findRecType (const aname: AnsiString): TDynRecord;
236 function findTrigFor (const aname: AnsiString): TDynRecord;
237 function findEBSType (const aname: AnsiString): TDynEBS;
239 // creates new header record
240 function parseMap (pr: TTextParser): TDynRecord;
242 // creates new header record
243 function parseBinMap (st: TStream): TDynRecord;
245 public
246 property headerType: TDynRecord read getHeaderRecType;
247 end;
250 implementation
252 uses
253 SysUtils,
254 utils;
257 // ////////////////////////////////////////////////////////////////////////// //
258 constructor TDynField.Create (const aname: AnsiString; atype: TType);
259 begin
260 mRVal := nil;
261 mRecRef := nil;
262 cleanup();
263 mName := aname;
264 mType := atype;
265 end;
268 constructor TDynField.Create (pr: TTextParser);
269 begin
270 cleanup();
271 parseDef(pr);
272 end;
275 destructor TDynField.Destroy ();
276 begin
277 cleanup();
278 inherited;
279 end;
282 procedure TDynField.cleanup ();
283 begin
284 mName := '';
285 mType := TType.TInt;
286 mIVal := 0;
287 mIVal2 := 0;
288 mSVal := '';
289 mRVal := nil;
290 mRecRef := nil;
291 mMaxDim := -1;
292 mBinOfs := -1;
293 mRecOfs := -1;
294 mSepPosSize := false;
295 mAsT := false;
296 mHasDefault := false;
297 mDefined := false;
298 mOmitDef := false;
299 mInternal := true;
300 mDefSVal := '';
301 mEBS := TEBS.TNone;
302 mEBSTypeName := '';
303 mBitSetUnique := false;
304 mNegBool := false;
305 mDefId := '';
306 mDefaultValueSet := false;
307 end;
310 function TDynField.clone (): TDynField;
311 var
312 f: Integer;
313 begin
314 result := TDynField.Create(mName, mType);
315 result.mOwner := mOwner;
316 result.mPasName := mPasName;
317 result.mName := mName;
318 result.mType := mType;
319 result.mIVal := mIVal;
320 result.mIVal2 := mIVal2;
321 result.mSVal := mSVal;
322 SetLength(result.mRVal, Length(mRVal));
323 for f := 0 to High(mRVal) do result.mRVal[f] := mRVal[f].clone();
324 result.mRecRef := mRecRef;
325 result.mMaxDim := mMaxDim;
326 result.mBinOfs := mBinOfs;
327 result.mRecOfs := mRecOfs;
328 result.mSepPosSize := mSepPosSize;
329 result.mAsT := mAsT;
330 result.mDefined := mDefined;
331 result.mHasDefault := mHasDefault;
332 result.mOmitDef := mOmitDef;
333 result.mInternal := mInternal;
334 result.mDefSVal := mDefSVal;
335 result.mEBS := mEBS;
336 result.mEBSTypeName := mEBSTypeName;
337 result.mBitSetUnique := mBitSetUnique;
338 result.mNegBool := mNegBool;
339 result.mDefId := mDefId;
340 result.mDefaultValueSet := mDefaultValueSet;
341 end;
344 // won't work for lists
345 function TDynField.isSimpleEqu (fld: TDynField): Boolean;
346 begin
347 if (fld = nil) or (mType <> fld.mType) then begin result := false; exit; end;
348 case mType of
349 TType.TBool: result := ((mIVal <> 0) = (fld.mIVal <> 0));
350 TType.TChar: result := (mSVal = fld.mSVal);
351 TType.TByte,
352 TType.TUByte,
353 TType.TShort,
354 TType.TUShort,
355 TType.TInt,
356 TType.TUInt:
357 result := (mIVal = fld.mIVal);
358 TType.TString: result := (mSVal = fld.mSVal);
359 TType.TPoint,
360 TType.TSize:
361 result := ((mIVal = fld.mIVal) and (mIVal2 = fld.mIVal2));
362 TType.TList: result := false;
363 TType.TTrigData: result := false;
364 else raise Exception.Create('ketmar forgot to handle some field type');
365 end;
366 end;
369 procedure TDynField.fixDefaultValue ();
370 var
371 stp: TTextParser;
372 s: AnsiString;
373 begin
374 if not mDefined then
375 begin
376 if not mHasDefault then
377 begin
378 if mInternal then exit;
379 raise Exception.Create(Format('field ''%s'' in record ''%s'' of record type ''%s'' is not set', [mName, mOwner.mId, mOwner.mName]));
380 end;
381 if (mEBS = TEBS.TRec) then
382 begin
383 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]));
384 mDefined := true;
385 assert(mRecRef = nil);
386 mDefaultValueSet := true;
387 exit;
388 end;
389 s := '';
390 case mType of
391 TType.TChar, TType.TString: s := TTextParser.quote(mDefSVal)+';';
392 TType.TPoint, TType.TSize: assert(false); // no default values for these types yet
393 else s := mDefSVal+';';
394 end;
395 //mDefined := true;
396 //writeln('DEFAULT for <', mName, '>: <', s, '>');
397 stp := TStrTextParser.Create(s);
398 try
399 parseValue(stp, nil);
400 finally
401 stp.Free();
402 end;
403 assert(mDefined);
404 mDefaultValueSet := true;
405 end;
406 end;
409 function TDynField.isDefaultValue (): Boolean;
410 var
411 fld: TDynField = nil;
412 stp: TTextParser = nil;
413 s: AnsiString;
414 begin
415 if not mHasDefault then begin result := false; exit; end;
416 //result := mDefaultValueSet;
417 if (mEBS = TEBS.TRec) then begin result := (mRecRef = nil); exit; end;
418 s := '';
419 case mType of
420 TType.TChar, TType.TString: s := TTextParser.quote(mDefSVal)+';';
421 TType.TPoint, TType.TSize: begin result := false; exit; end; // no default values for these types yet
422 else s := mDefSVal+';';
423 end;
424 stp := TStrTextParser.Create(s);
425 try
426 fld := clone();
427 fld.parseValue(stp, nil);
428 result := isSimpleEqu(fld);
429 finally
430 fld.Free();
431 stp.Free();
432 end;
433 end;
436 class function TDynField.getTypeName (t: TType): AnsiString;
437 begin
438 case t of
439 TType.TBool: result := 'bool';
440 TType.TChar: result := 'char';
441 TType.TByte: result := 'byte';
442 TType.TUByte: result := 'ubyte';
443 TType.TShort: result := 'short';
444 TType.TUShort: result := 'ushort';
445 TType.TInt: result := 'int';
446 TType.TUInt: result := 'uint';
447 TType.TString: result := 'string';
448 TType.TPoint: result := 'point';
449 TType.TSize: result := 'size';
450 TType.TList: result := 'array';
451 TType.TTrigData: result := 'trigdata';
452 else raise Exception.Create('ketmar forgot to handle some field type');
453 end;
454 end;
457 function TDynField.definition (): AnsiString;
458 begin
459 result := mPasName+' is '+TTextParser.quote(mName)+' type ';
460 result += getTypeName(mType);
461 if (mMaxDim >= 0) then result += Format('[%d]', [mMaxDim]);
462 if (mRecOfs >= 0) then result += Format(' offset %d', [mRecOfs]);
463 case mEBS of
464 TEBS.TNone: begin end;
465 TEBS.TRec: result += ' '+mEBSTypeName;
466 TEBS.TEnum: result += ' enum '+mEBSTypeName;
467 TEBS.TBitSet: begin result += ' bitset '; if mBitSetUnique then result += 'unique '; result += mEBSTypeName; end;
468 end;
469 if mHasDefault then
470 begin
471 if (mType = TType.TChar) or (mType = TType.TString) then result += ' default '+TTextParser.quote(mDefSVal)
472 else if (Length(mDefSVal) > 0) then result += ' default '+mDefSVal;
474 else
475 begin
476 if (mType = TType.TBool) then
477 begin
478 result += ' default ';
479 if (mDefIVal <> 0) then result += 'true' else result += 'false';
480 end
481 else
482 begin
483 result += Format(' default %d', [mDefIVal]);
484 end;
485 end;
487 end;
488 if mSepPosSize then
489 begin
490 if (mType = TType.TPoint) then begin if (mAsT) then result += ' as txy' else result += ' as xy'; end
491 else if (mType = TType.TSize) then begin if (mAsT) then result += ' as twh' else result += ' as wh'; end;
492 end;
493 if mOmitDef then result += ' omitdefault';
494 if mInternal then result += ' internal';
495 end;
498 procedure TDynField.parseDef (pr: TTextParser);
499 var
500 fldname: AnsiString;
501 fldtype: AnsiString;
502 fldofs: Integer;
503 fldrecname: AnsiString;
504 fldpasname: AnsiString;
505 asxy, aswh, ast: Boolean;
506 ainternal: Boolean;
507 omitdef: Boolean;
508 defstr: AnsiString;
509 defint: Integer;
510 hasdefStr: Boolean;
511 hasdefInt: Boolean;
512 hasdefId: Boolean;
513 lmaxdim: Integer;
514 lebs: TDynField.TEBS;
515 unique: Boolean;
516 begin
517 fldpasname := '';
518 fldname := '';
519 fldtype := '';
520 fldofs := -1;
521 fldrecname := '';
522 asxy := false;
523 aswh := false;
524 ast := false;
525 ainternal := false;
526 omitdef := false;
527 defstr := '';
528 defint := 0;
529 hasdefStr := false;
530 hasdefInt := false;
531 hasdefId := false;
532 unique := false;
533 lmaxdim := -1;
534 lebs := TDynField.TEBS.TNone;
536 fldpasname := pr.expectId(); // pascal field name
537 // field name
538 pr.expectId('is');
539 fldname := pr.expectStr();
540 // field type
541 pr.expectId('type');
542 fldtype := pr.expectId();
544 // fixed-size array?
545 if pr.eatDelim('[') then
546 begin
547 lmaxdim := pr.expectInt();
548 if (lmaxdim < 1) then raise Exception.Create(Format('invali field ''%s'' array size', [fldname]));
549 pr.expectDelim(']');
550 end;
552 while (pr.tokType <> pr.TTSemi) do
553 begin
554 if pr.eatId('offset') then
555 begin
556 if (fldofs >= 0) then raise Exception.Create(Format('duplicate field ''%s'' offset', [fldname]));
557 fldofs := pr.expectInt();
558 if (fldofs < 0) then raise Exception.Create(Format('invalid field ''%s'' offset', [fldname]));
559 continue;
560 end;
562 if pr.eatId('as') then
563 begin
564 if pr.eatId('xy') then asxy := true
565 else if pr.eatId('wh') then aswh := true
566 else if pr.eatId('txy') then begin asxy := true; ast := true; end
567 else if pr.eatId('twh') then begin aswh := true; ast := true; end
568 else raise Exception.Create(Format('invalid field ''%s'' as what?', [fldname]));
569 continue;
570 end;
572 if pr.eatId('enum') then
573 begin
574 lebs := TDynField.TEBS.TEnum;
575 if (Length(fldrecname) <> 0) then raise Exception.Create(Format('field ''%s'' already typed as ''%s''', [fldname, fldrecname]));
576 fldrecname := pr.expectId();
577 continue;
578 end;
580 if pr.eatId('bitset') then
581 begin
582 lebs := TDynField.TEBS.TBitSet;
583 if (Length(fldrecname) <> 0) then raise Exception.Create(Format('field ''%s'' already typed as ''%s''', [fldname, fldrecname]));
584 unique := pr.eatId('unique');
585 fldrecname := pr.expectId();
586 continue;
587 end;
589 if pr.eatId('default') then
590 begin
591 if hasdefStr or hasdefInt or hasdefId then raise Exception.Create(Format('field ''%s'' has duplicate default', [fldname]));
592 case pr.tokType of
593 pr.TTStr:
594 begin
595 hasdefStr := true;
596 defstr := pr.expectStr(true); // allow empty strings
597 end;
598 pr.TTId:
599 begin
600 hasdefId := true;
601 defstr := pr.expectId();
602 end;
603 pr.TTInt:
604 begin
605 hasdefInt := true;
606 defint := pr.expectInt();
607 end;
608 else
609 raise Exception.Create(Format('field ''%s'' has invalid default', [fldname]));
610 end;
611 continue;
612 end;
614 if pr.eatId('omitdefault') then
615 begin
616 omitdef := true;
617 continue;
618 end;
620 if pr.eatId('internal') then
621 begin
622 ainternal := true;
623 continue;
624 end;
626 if (pr.tokType <> pr.TTId) then raise Exception.Create(Format('field ''%s'' has something unexpected in definition', [fldname]));
628 if (Length(fldrecname) <> 0) then raise Exception.Create(Format('field ''%s'' already typed as ''%s''', [fldname, fldrecname]));
629 fldrecname := pr.expectId();
630 lebs := TDynField.TEBS.TRec;
631 end;
633 pr.expectTT(pr.TTSemi);
635 // create field
636 mName := fldname;
637 if (fldtype = 'bool') then mType := TType.TBool
638 else if (fldtype = 'negbool') then begin mType := TType.TBool; mNegBool := true; end
639 else if (fldtype = 'char') then mType := TType.TChar
640 else if (fldtype = 'byte') then mType := TType.TByte
641 else if (fldtype = 'ubyte') then mType := TType.TUByte
642 else if (fldtype = 'short') then mType := TType.TShort
643 else if (fldtype = 'ushort') then mType := TType.TUShort
644 else if (fldtype = 'int') then mType := TType.TInt
645 else if (fldtype = 'uint') then mType := TType.TUInt
646 else if (fldtype = 'string') then mType := TType.TString
647 else if (fldtype = 'point') then mType := TType.TPoint
648 else if (fldtype = 'size') then mType := TType.TSize
649 else if (fldtype = 'trigdata') then mType := TType.TTrigData
650 else raise Exception.Create(Format('field ''%s'' has invalid type ''%s''', [fldname, fldtype]));
652 {if hasdefId and (self.baseType = self.TType.TBool) then
653 begin
654 if (defstr = 'true') or (defstr = 'tan') or (defstr = 'yes') then self.mDefIVal := 1
655 else if (defstr = 'false') or (defstr = 'ona') or (defstr = 'no') then self.mDefIVal := 0
656 else raise Exception.Create(Format('field ''%s'' has invalid boolean default ''%s''', [fldname, defstr]));
657 end
658 else}
659 begin
660 if hasdefStr then self.mDefSVal := defstr
661 else if hasdefInt then self.mDefSVal := Format('%d', [defint])
662 else if hasdefId then self.mDefSVal := defstr;
663 end;
665 self.mHasDefault := (hasdefStr or hasdefId or hasdefInt);
666 self.mPasName := fldpasname;
667 self.mEBS := lebs;
668 self.mEBSTypeName := fldrecname;
669 self.mBitSetUnique := unique;
670 self.mMaxDim := lmaxdim;
671 self.mBinOfs := fldofs;
672 self.mRecOfs := fldofs;
673 self.mSepPosSize := (asxy or aswh);
674 self.mAsT := ast;
675 self.mOmitDef := omitdef;
676 self.mInternal := ainternal;
677 end;
680 procedure TDynField.writeBinTo (st: TStream; curheader: TDynRecord);
681 var
682 s: AnsiString;
683 f: Integer;
684 maxv: Integer;
685 buf: PByte;
686 ws: TStream = nil;
687 begin
688 case mEBS of
689 TEBS.TNone: begin end;
690 TEBS.TRec:
691 begin
692 // this must be byte/word/int
693 if (mMaxDim >= 0) then
694 begin
695 // this must be triggerdata
696 if (CompareText(mEBSTypeName, 'triggerdata') <> 0) then
697 begin
698 raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
699 end;
700 // write triggerdata
701 case mType of
702 TType.TChar, TType.TByte, TType.TUByte: begin end;
703 else raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
704 end;
705 //writeln('trigdata size: ', mMaxDim);
706 GetMem(buf, mMaxDim);
707 if (buf = nil) then raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
708 try
709 FillChar(buf^, mMaxDim, 0);
710 if (mRecRef <> nil) then
711 begin
712 ws := TSFSMemoryChunkStream.Create(buf, mMaxDim);
713 mRecRef.writeBinTo(ws, curheader, mMaxDim); // as trigdata
714 end;
715 st.WriteBuffer(buf^, mMaxDim);
716 finally
717 ws.Free();
718 if (buf <> nil) then FreeMem(buf);
719 end;
720 exit;
721 end;
722 if (mRecRef = nil) then
723 begin
724 // no ref, write -1
725 case mType of
726 TType.TByte, TType.TUByte: writeInt(st, Byte(-1));
727 TType.TShort, TType.TUShort: writeInt(st, SmallInt(-1));
728 TType.TInt, TType.TUInt: writeInt(st, Integer(-1));
729 else raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
730 end;
731 exit;
732 end;
733 case mType of
734 TType.TByte: maxv := 127;
735 TType.TUByte: maxv := 254;
736 TType.TShort: maxv := 32767;
737 TType.TUShort: maxv := 65534;
738 TType.TInt: maxv := $7fffffff;
739 TType.TUInt: maxv := $7fffffff;
740 else raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
741 end;
742 // find record number
743 f := mOwner.mOwner.findRecordNumByType(mEBSTypeName, mRecRef, curheader);
744 if (f < 0) then raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' not found in record list', [mEBSTypeName, mName]));
745 if (f > maxv) then raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' has too big index', [mEBSTypeName, mName]));
746 case mType of
747 TType.TByte, TType.TUByte: writeInt(st, Byte(f));
748 TType.TShort, TType.TUShort: writeInt(st, SmallInt(f));
749 TType.TInt, TType.TUInt: writeInt(st, Integer(f));
750 else raise Exception.Create(Format('record reference type ''%s'' in field ''%s'' cannot be written', [mEBSTypeName, mName]));
751 end;
752 exit;
753 end;
754 TEBS.TEnum: begin end;
755 TEBS.TBitSet: begin end;
756 else raise Exception.Create('ketmar forgot to handle some EBS type');
757 end;
759 case mType of
760 TType.TBool:
761 begin
762 if (mIVal <> 0) then writeInt(st, Byte(1)) else writeInt(st, Byte(0));
763 exit;
764 end;
765 TType.TChar:
766 begin
767 if (mMaxDim = 0) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
768 if (mMaxDim < 0) then
769 begin
770 if (Length(mSVal) <> 1) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
771 writeInt(st, Byte(mSVal[1]));
772 end
773 else
774 begin
775 if (Length(mSVal) > mMaxDim) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
776 //FillChar(s[0], sizeof(s), 0);
777 s := utfTo1251(mSVal);
778 //writeln('writing char[', mMaxDim, '] <', mName, '>: ', TTextParser.quote(s));
779 if (Length(s) > 0) then st.WriteBuffer(PChar(s)^, Length(s));
780 for f := Length(s) to mMaxDim do writeInt(st, Byte(0));
781 end;
782 exit;
783 end;
784 TType.TByte,
785 TType.TUByte:
786 begin
787 // either array, and this should be triggerdata, or byte
788 if (mMaxDim < 0) then
789 begin
790 // byte
791 writeInt(st, Byte(mIVal));
792 end
793 else
794 begin
795 // array
796 raise Exception.Create(Format('byte array in field ''%s'' cannot be written', [mName]));
797 end;
798 exit;
799 end;
800 TType.TShort,
801 TType.TUShort:
802 begin
803 if (mMaxDim > 0) then raise Exception.Create(Format('short array in field ''%s'' cannot be written', [mName]));
804 writeInt(st, Word(mIVal));
805 exit;
806 end;
807 TType.TInt,
808 TType.TUInt:
809 begin
810 if (mMaxDim > 0) then raise Exception.Create(Format('int array in field ''%s'' cannot be written', [mName]));
811 writeInt(st, LongWord(mIVal));
812 exit;
813 end;
814 TType.TString:
815 begin
816 raise Exception.Create(Format('cannot write string field ''%s''', [mName]));
817 end;
818 TType.TPoint,
819 TType.TSize:
820 begin
821 if (mMaxDim > 0) then raise Exception.Create(Format('pos/size array in field ''%s'' cannot be written', [mName]));
822 writeInt(st, Word(mIVal));
823 writeInt(st, Word(mIVal2));
824 exit;
825 end;
826 TType.TList:
827 begin
828 assert(false);
829 exit;
830 end;
831 TType.TTrigData:
832 begin
833 assert(false);
834 exit;
835 end;
836 else raise Exception.Create('ketmar forgot to handle some field type');
837 end;
838 end;
841 procedure TDynField.writeTo (wr: TTextWriter);
842 var
843 def: TDynMapDef;
844 es: TDynEBS = nil;
845 f, mask: Integer;
846 first, found: Boolean;
847 begin
848 wr.put(mName);
849 wr.put(' ');
850 // if this field should contain struct, convert type and parse struct
851 case mEBS of
852 TEBS.TNone: begin end;
853 TEBS.TRec:
854 begin
855 if (mRecRef = nil) then
856 begin
857 wr.put('null;'#10);
858 end
859 else if (Length(mRecRef.mId) = 0) then
860 begin
861 mRecRef.writeTo(wr, false); // only data, no header
862 end
863 else
864 begin
865 wr.put(mRecRef.mId);
866 wr.put(';'#10);
867 end;
868 exit;
869 end;
870 TEBS.TEnum:
871 begin
872 def := mOwner.mOwner;
873 es := def.findEBSType(mEBSTypeName);
874 if (es = nil) then raise Exception.Create(Format('record enum type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
875 for f := 0 to High(es.mVals) do
876 begin
877 if (es.mVals[f] = mIVal) then
878 begin
879 wr.put(es.mIds[f]);
880 wr.put(';'#10);
881 exit;
882 end;
883 end;
884 raise Exception.Create(Format('value %d in record enum type ''%s'' for field ''%s'' not found', [mIVal, mEBSTypeName, mName]));
885 end;
886 TEBS.TBitSet:
887 begin
888 def := mOwner.mOwner;
889 es := def.findEBSType(mEBSTypeName);
890 if (es = nil) then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
891 // none?
892 if (mIVal = 0) then
893 begin
894 for f := 0 to High(es.mVals) do
895 begin
896 if (es.mVals[f] = 0) then
897 begin
898 wr.put(es.mIds[f]);
899 wr.put(';'#10);
900 exit;
901 end;
902 end;
903 raise Exception.Create(Format('value %d in record bitset type ''%s'' for field ''%s'' not found', [0, mEBSTypeName, mName]));
904 end;
905 // not none
906 mask := 1;
907 first := true;
908 while (mask <> 0) do
909 begin
910 if ((mIVal and mask) <> 0) then
911 begin
912 found := false;
913 for f := 0 to High(es.mVals) do
914 begin
915 if (es.mVals[f] = mask) then
916 begin
917 if not first then wr.put('+') else first := false;
918 wr.put(es.mIds[f]);
919 found := true;
920 break;
921 end;
922 end;
923 if not found then raise Exception.Create(Format('value %d in record bitset type ''%s'' for field ''%s'' not found', [mask, mEBSTypeName, mName]));
924 end;
925 mask := mask shl 1;
926 end;
927 wr.put(';'#10);
928 exit;
929 end;
930 else raise Exception.Create('ketmar forgot to handle some EBS type');
931 end;
933 case mType of
934 TType.TBool:
935 begin
936 if (mIVal = 0) then wr.put('false;'#10) else wr.put('true;'#10);
937 exit;
938 end;
939 TType.TChar:
940 begin
941 if (mMaxDim = 0) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
942 wr.put(TTextParser.quote(mSVal));
943 wr.put(';'#10);
944 exit;
945 end;
946 TType.TByte,
947 TType.TUByte,
948 TType.TShort,
949 TType.TUShort,
950 TType.TInt,
951 TType.TUInt:
952 begin
953 wr.put('%d;'#10, [mIVal]);
954 exit;
955 end;
956 TType.TString:
957 begin
958 wr.put(TTextParser.quote(mSVal));
959 wr.put(';'#10);
960 exit;
961 end;
962 TType.TPoint,
963 TType.TSize:
964 begin
965 wr.put('(%d %d);'#10, [mIVal, mIVal2]);
966 exit;
967 end;
968 TType.TList:
969 begin
970 assert(false);
971 exit;
972 end;
973 TType.TTrigData:
974 begin
975 assert(false);
976 exit;
977 end;
978 else raise Exception.Create('ketmar forgot to handle some field type');
979 end;
980 raise Exception.Create(Format('cannot parse field ''%s'' yet', [mName]));
981 end;
984 procedure TDynField.parseValue (pr: TTextParser; curheader: TDynRecord);
986 procedure parseInt (min, max: Integer);
987 begin
988 mIVal := pr.expectInt();
989 if (mIVal < min) or (mIVal > max) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
990 mDefined := true;
991 end;
993 var
994 rec, rc: TDynRecord;
995 def: TDynMapDef;
996 es: TDynEBS = nil;
997 tfld: TDynField;
998 tk: AnsiString;
999 begin
1000 // if this field should contain struct, convert type and parse struct
1001 case mEBS of
1002 TEBS.TNone: begin end;
1003 TEBS.TRec:
1004 begin
1005 def := mOwner.mOwner;
1006 // ugly hack. sorry.
1007 if (CompareText(mEBSTypeName, 'triggerdata') = 0) then
1008 begin
1009 rec := mOwner;
1010 // find trigger definition
1011 tfld := rec.field['type'];
1012 if (tfld = nil) then raise Exception.Create(Format('triggerdata value for field ''%s'' in record ''%s'' without ''type'' field', [mName, rec.mName]));
1013 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]));
1014 rc := def.findTrigFor(tfld.mSVal);
1015 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]));
1016 rc := rc.clone();
1017 rc.parseValue(pr, curheader);
1018 mRecRef := rc;
1019 mDefined := true;
1020 exit;
1021 end;
1022 // other record types
1023 if (pr.tokType = pr.TTId) then
1024 begin
1025 rec := def.findRecordByTypeId(mEBSTypeName, pr.tokStr, curheader);
1026 if (rec = nil) then raise Exception.Create(Format('record ''%s'' (%s) value for field ''%s'' not found', [pr.tokStr, mEBSTypeName, mName]));
1027 pr.expectId();
1028 mRecRef := rec;
1029 mDefined := true;
1030 pr.expectTT(pr.TTSemi);
1031 exit;
1032 end
1033 else if (pr.tokType = pr.TTBegin) then
1034 begin
1035 rec := def.findRecType(mEBSTypeName);
1036 if (rec = nil) then raise Exception.Create(Format('record type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1037 rc := rec.clone();
1038 rc.parseValue(pr, curheader);
1039 mRecRef := rc;
1040 mDefined := true;
1041 mOwner.mOwner.addRecordByType(mEBSTypeName, rc, curheader);
1042 exit;
1043 end;
1044 pr.expectTT(pr.TTBegin);
1045 end;
1046 TEBS.TEnum:
1047 begin
1048 def := mOwner.mOwner;
1049 es := def.findEBSType(mEBSTypeName);
1050 if (es = nil) then raise Exception.Create(Format('record enum type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1051 tk := pr.expectId();
1052 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]));
1053 mIVal := es.field[tk];
1054 mSVal := tk;
1055 //writeln('ENUM ', mEBSName, '; element <', mSVal, '> with value ', mIVal);
1056 mDefined := true;
1057 pr.expectTT(pr.TTSemi);
1058 exit;
1059 end;
1060 TEBS.TBitSet:
1061 begin
1062 def := mOwner.mOwner;
1063 es := def.findEBSType(mEBSTypeName);
1064 if (es = nil) then raise Exception.Create(Format('record bitset type ''%s'' for field ''%s'' not found', [mEBSTypeName, mName]));
1065 mIVal := 0;
1066 while true do
1067 begin
1068 tk := pr.expectId();
1069 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]));
1070 mIVal := mIVal or es.field[tk];
1071 mSVal := tk;
1072 if (pr.tokType <> pr.TTDelim) or ((pr.tokChar <> '|') and (pr.tokChar <> '+')) then break;
1073 if mBitSetUnique then raise Exception.Create(Format('record bitset of type ''%s'' for field ''%s'' expects only one value', [tk, mEBSTypeName, mName]));
1074 //pr.expectDelim('|');
1075 pr.skipToken(); // plus or pipe
1076 end;
1077 mDefined := true;
1078 pr.expectTT(pr.TTSemi);
1079 exit;
1080 end;
1081 else raise Exception.Create('ketmar forgot to handle some EBS type');
1082 end;
1084 case mType of
1085 TType.TBool:
1086 begin
1087 if pr.eatId('true') or pr.eatId('tan') or pr.eatId('yes') then mIVal := 1
1088 else if pr.eatId('false') or pr.eatId('ona') or pr.eatId('no') then mIVal := 0
1089 else raise Exception.Create(Format('invalid bool value for field ''%s''', [mName]));
1090 mDefined := true;
1091 pr.expectTT(pr.TTSemi);
1092 exit;
1093 end;
1094 TType.TChar:
1095 begin
1096 if (mMaxDim = 0) then raise Exception.Create(Format('invalid string size definition for field ''%s''', [mName]));
1097 mSVal := pr.expectStr(true);
1098 if (mMaxDim < 0) then
1099 begin
1100 // single char
1101 if (Length(mSVal) <> 1) then raise Exception.Create(Format('invalid string size for field ''%s''', [mName]));
1102 mIVal := Integer(mSVal[1]);
1103 mSVal := '';
1104 end
1105 else
1106 begin
1107 // string
1108 if (Length(mSVal) > mMaxDim) then raise Exception.Create(Format('invalid string size for field ''%s''', [mName]));
1109 end;
1110 mDefined := true;
1111 pr.expectTT(pr.TTSemi);
1112 exit;
1113 end;
1114 TType.TByte:
1115 begin
1116 parseInt(-128, 127);
1117 pr.expectTT(pr.TTSemi);
1118 exit;
1119 end;
1120 TType.TUByte:
1121 begin
1122 parseInt(0, 255);
1123 pr.expectTT(pr.TTSemi);
1124 exit;
1125 end;
1126 TType.TShort:
1127 begin
1128 parseInt(-32768, 32768);
1129 pr.expectTT(pr.TTSemi);
1130 exit;
1131 end;
1132 TType.TUShort:
1133 begin
1134 parseInt(0, 65535);
1135 pr.expectTT(pr.TTSemi);
1136 exit;
1137 end;
1138 TType.TInt:
1139 begin
1140 parseInt(Integer($80000000), $7fffffff);
1141 pr.expectTT(pr.TTSemi);
1142 exit;
1143 end;
1144 TType.TUInt:
1145 begin
1146 parseInt(0, $7fffffff); //FIXME
1147 pr.expectTT(pr.TTSemi);
1148 exit;
1149 end;
1150 TType.TString:
1151 begin
1152 mSVal := pr.expectStr(true);
1153 mDefined := true;
1154 pr.expectTT(pr.TTSemi);
1155 exit;
1156 end;
1157 TType.TPoint,
1158 TType.TSize:
1159 begin
1160 pr.expectDelim('(');
1161 mIVal := pr.expectInt();
1162 if (mType = TType.TPoint) then
1163 begin
1164 if (mIVal < -32768) or (mIVal > 32767) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
1165 end
1166 else
1167 begin
1168 if (mIVal < 0) or (mIVal > 32767) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
1169 end;
1170 mIVal2 := pr.expectInt();
1171 if (mType = TType.TPoint) then
1172 begin
1173 if (mIVal2 < -32768) or (mIVal2 > 32767) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
1174 end
1175 else
1176 begin
1177 if (mIVal2 < 0) or (mIVal2 > 32767) then raise Exception.Create(Format('invalid %s value for field ''%s''', [getTypeName(mType), mName]));
1178 end;
1179 mDefined := true;
1180 pr.expectDelim(')');
1181 pr.expectTT(pr.TTSemi);
1182 exit;
1183 end;
1184 TType.TList:
1185 begin
1186 assert(false);
1187 exit;
1188 end;
1189 TType.TTrigData:
1190 begin
1191 assert(false);
1192 exit;
1193 end;
1194 else raise Exception.Create('ketmar forgot to handle some field type');
1195 end;
1196 raise Exception.Create(Format('cannot parse field ''%s'' yet', [mName]));
1197 end;
1200 procedure TDynField.parseBinValue (st: TStream);
1201 begin
1202 end;
1205 // ////////////////////////////////////////////////////////////////////////// //
1206 constructor TDynRecord.Create (pr: TTextParser);
1207 begin
1208 if (pr = nil) then raise Exception.Create('cannot create record type without type definition');
1209 mId := '';
1210 mName := '';
1211 mSize := 0;
1212 mFields := nil;
1213 mTrigTypes := nil;
1214 mHeader := false;
1215 mBinBlock := -1;
1216 parseDef(pr);
1217 end;
1220 constructor TDynRecord.Create ();
1221 begin
1222 mName := '';
1223 mSize := 0;
1224 mFields := nil;
1225 mTrigTypes := nil;
1226 mHeader := false;
1227 end;
1230 destructor TDynRecord.Destroy ();
1231 begin
1232 mName := '';
1233 mFields := nil;
1234 mTrigTypes := nil;
1235 inherited;
1236 end;
1239 function TDynRecord.findByName (const aname: AnsiString): Integer; inline;
1240 begin
1241 result := 0;
1242 while (result < Length(mFields)) do
1243 begin
1244 if (CompareText(aname, mFields[result].mName) = 0) then exit;
1245 Inc(result);
1246 end;
1247 result := -1;
1248 end;
1251 function TDynRecord.hasByName (const aname: AnsiString): Boolean; inline;
1252 begin
1253 result := (findByName(aname) >= 0);
1254 end;
1257 function TDynRecord.getFieldByName (const aname: AnsiString): TDynField; inline;
1258 var
1259 f: Integer;
1260 begin
1261 f := findByName(aname);
1262 if (f >= 0) then result := mFields[f] else result := nil;
1263 end;
1266 function TDynRecord.getIsTrigData (): Boolean; inline;
1267 begin
1268 result := (Length(mTrigTypes) > 0);
1269 end;
1272 function TDynRecord.getIsForTrig (const aname: AnsiString): Boolean; inline;
1273 var
1274 f: Integer;
1275 begin
1276 result := true;
1277 for f := 0 to High(mTrigTypes) do if (CompareText(mTrigTypes[f], aname) = 0) then exit;
1278 result := false;
1279 end;
1282 function TDynRecord.clone (): TDynRecord;
1283 var
1284 f: Integer;
1285 begin
1286 result := TDynRecord.Create();
1287 result.mOwner := mOwner;
1288 result.mId := mId;
1289 result.mPasName := mPasName;
1290 result.mName := mName;
1291 result.mSize := mSize;
1292 result.mHeader := mHeader;
1293 result.mBinBlock := mBinBlock;
1294 SetLength(result.mFields, Length(mFields));
1295 for f := 0 to High(mFields) do
1296 begin
1297 result.mFields[f] := mFields[f].clone();
1298 result.mFields[f].mOwner := result;
1299 end;
1300 SetLength(result.mTrigTypes, Length(mTrigTypes));
1301 for f := 0 to High(mTrigTypes) do result.mTrigTypes[f] := mTrigTypes[f];
1302 end;
1305 procedure TDynRecord.parseDef (pr: TTextParser);
1306 var
1307 fld: TDynField;
1308 tdn: AnsiString;
1309 begin
1310 if pr.eatId('TriggerData') then
1311 begin
1312 pr.expectId('for');
1313 if pr.eatDelim('(') then
1314 begin
1315 while true do
1316 begin
1317 while pr.eatTT(pr.TTComma) do begin end;
1318 if pr.eatDelim(')') then break;
1319 tdn := pr.expectId();
1320 if isForTrig[tdn] then raise Exception.Create(Format('duplicate trigdata ''%s'' trigtype ''%s''', [mName, tdn]));
1321 SetLength(mTrigTypes, Length(mTrigTypes)+1);
1322 mTrigTypes[High(mTrigTypes)] := tdn;
1323 end;
1324 end
1325 else
1326 begin
1327 tdn := pr.expectId();
1328 SetLength(mTrigTypes, 1);
1329 mTrigTypes[0] := tdn;
1330 end;
1331 end
1332 else
1333 begin
1334 mPasName := pr.expectId(); // pascal record name
1335 pr.expectId('is');
1336 mName := pr.expectStr();
1337 while (pr.tokType <> pr.TTBegin) do
1338 begin
1339 if pr.eatId('header') then begin mHeader := true; continue; end;
1340 if pr.eatId('size') then
1341 begin
1342 if (mSize > 0) then raise Exception.Create(Format('duplicate `size` in record ''%s''', [mName]));
1343 mSize := pr.expectInt();
1344 if (mSize < 1) then raise Exception.Create(Format('invalid record ''%s'' size: %d', [mName, mSize]));
1345 pr.expectId('bytes');
1346 continue;
1347 end;
1348 if pr.eatId('binblock') then
1349 begin
1350 if (mBinBlock >= 0) then raise Exception.Create(Format('duplicate `binblock` in record ''%s''', [mName]));
1351 mBinBlock := pr.expectInt();
1352 if (mBinBlock < 1) then raise Exception.Create(Format('invalid record ''%s'' binblock: %d', [mName, mBinBlock]));
1353 continue;
1354 end;
1355 end;
1356 end;
1358 pr.expectTT(pr.TTBegin);
1359 // load fields
1360 while (pr.tokType <> pr.TTEnd) do
1361 begin
1362 fld := TDynField.Create(pr);
1363 if hasByName(fld.name) then begin fld.Free(); raise Exception.Create(Format('duplicate field ''%s''', [fld.name])); end;
1364 // append
1365 fld.mOwner := self;
1366 SetLength(mFields, Length(mFields)+1);
1367 mFields[High(mFields)] := fld;
1368 // done with field
1369 //writeln('DEF: ', fld.definition);
1370 end;
1371 pr.expectTT(pr.TTEnd);
1372 end;
1375 function TDynRecord.definition (): AnsiString;
1376 var
1377 f: Integer;
1378 begin
1379 if isTrigData then
1380 begin
1381 // trigger data
1382 result := 'TriggerData for ';
1383 if (Length(mTrigTypes) > 1) then
1384 begin
1385 result += '(';
1386 for f := 0 to High(mTrigTypes) do
1387 begin
1388 if (f <> 0) then result += ', ';
1389 result += mTrigTypes[f];
1390 end;
1391 result += ')';
1392 end
1393 else
1394 begin
1395 result += mTrigTypes[0];
1396 end;
1397 end
1398 else
1399 begin
1400 // record
1401 result := mPasName+' is '+TTextParser.quote(mName);
1402 if (mSize >= 0) then result += Format(' size %d bytes', [mSize]);
1403 if mHeader then result += ' header';
1404 end;
1405 result += ' {'#10;
1406 for f := 0 to High(mFields) do
1407 begin
1408 result += ' ';
1409 result += mFields[f].definition;
1410 result += ';'#10;
1411 end;
1412 result += '}';
1413 end;
1416 procedure TDynRecord.writeBinTo (st: TStream; curheader: TDynRecord; trigbufsz: Integer=-1);
1417 var
1418 fld: TDynField;
1419 rec: TDynRecord;
1420 buf: PByte = nil;
1421 ws: TStream = nil;
1422 blk, blkmax: Integer;
1423 f, c: Integer;
1424 bufsz: Integer = 0;
1425 begin
1426 if (curheader = nil) and mHeader then curheader := self;
1427 if (trigbufsz < 0) then
1428 begin
1429 if (mBinBlock < 1) then raise Exception.Create('cannot write binary record without block number');
1430 if (mSize < 1) then raise Exception.Create('cannot write binary record without size');
1431 bufsz := mSize;
1432 end
1433 else
1434 begin
1435 bufsz := trigbufsz;
1436 end;
1437 try
1438 GetMem(buf, bufsz);
1439 FillChar(buf^, bufsz, 0);
1440 ws := TSFSMemoryChunkStream.Create(buf, bufsz);
1442 // write normal fields
1443 for f := 0 to High(mFields) do
1444 begin
1445 fld := mFields[f];
1446 // record list?
1447 if (fld.mType = fld.TType.TList) then continue; // later
1448 if fld.mInternal then continue;
1449 if (fld.mBinOfs < 0) then continue;
1450 if (fld.mBinOfs >= bufsz) then raise Exception.Create('binary value offset is outside of the buffer');
1451 TSFSMemoryChunkStream(ws).setup(buf+fld.mBinOfs, bufsz-fld.mBinOfs);
1452 writeln('writing field <', fld.mName, '>');
1453 fld.writeBinTo(ws, curheader);
1454 end;
1456 // write block with normal fields
1457 if mHeader then
1458 begin
1459 writeln('writing header...');
1460 // signature and version
1461 writeIntBE(st, LongWord($4D415001));
1462 writeInt(st, Byte(mBinBlock)); // type
1463 writeInt(st, LongWord(0)); // reserved
1464 writeInt(st, LongWord(bufsz)); // size
1465 end;
1466 st.WriteBuffer(buf^, bufsz);
1468 ws.Free(); ws := nil;
1469 FreeMem(buf); buf := nil;
1471 // write other blocks, if any
1472 if mHeader then
1473 begin
1474 // calculate blkmax
1475 blkmax := 0;
1476 for f := 0 to High(mFields) do
1477 begin
1478 fld := mFields[f];
1479 // record list?
1480 if (fld.mType = fld.TType.TList) then
1481 begin
1482 if (Length(fld.mRVal) = 0) then continue;
1483 rec := mOwner.findRecType(fld.mName);
1484 if (rec = nil) then continue;
1485 if (rec.mBinBlock <= 0) then continue;
1486 if (blkmax < rec.mBinBlock) then blkmax := rec.mBinBlock;
1487 end;
1488 end;
1489 // write blocks
1490 for blk := 1 to blkmax do
1491 begin
1492 if (blk = mBinBlock) then continue;
1493 ws := nil;
1494 for f := 0 to High(mFields) do
1495 begin
1496 fld := mFields[f];
1497 // record list?
1498 if (fld.mType = fld.TType.TList) then
1499 begin
1500 if (Length(fld.mRVal) = 0) then continue;
1501 rec := mOwner.findRecType(fld.mName);
1502 if (rec = nil) then continue;
1503 if (rec.mBinBlock <> blk) then continue;
1504 if (ws = nil) then ws := TMemoryStream.Create();
1505 //rec.writeBinTo(ws);
1506 for c := 0 to High(fld.mRVal) do fld.mRVal[c].writeBinTo(ws, curheader);
1507 end;
1508 end;
1509 // flush block
1510 if (ws <> nil) then
1511 begin
1512 ws.position := 0;
1513 writeInt(st, Byte(blk)); // type
1514 writeInt(st, LongWord(0)); // reserved
1515 writeInt(st, LongWord(ws.size)); // size
1516 st.CopyFrom(ws, ws.size);
1517 ws.Free();
1518 ws := nil;
1519 end;
1520 end;
1521 end;
1522 finally
1523 ws.Free();
1524 if (buf <> nil) then FreeMem(buf);
1525 end;
1526 end;
1529 procedure TDynRecord.writeTo (wr: TTextWriter; putHeader: Boolean=true);
1530 var
1531 f, c: Integer;
1532 fld: TDynField;
1533 begin
1534 if putHeader then
1535 begin
1536 wr.put(mName);
1537 if (Length(mId) > 0) then begin wr.put(' '); wr.put(mId); end;
1538 wr.put(' ');
1539 end;
1540 wr.put('{'#10);
1541 wr.indent();
1542 try
1543 for f := 0 to High(mFields) do
1544 begin
1545 fld := mFields[f];
1546 // record list?
1547 if (fld.mType = fld.TType.TList) then
1548 begin
1549 if not mHeader then raise Exception.Create('record list in non-header record');
1550 for c := 0 to High(fld.mRVal) do
1551 begin
1552 wr.putIndent();
1553 fld.mRVal[c].writeTo(wr, true);
1554 end;
1555 continue;
1556 end;
1557 if fld.mInternal then continue;
1558 if fld.mOmitDef and fld.isDefaultValue then continue;
1559 wr.putIndent();
1560 fld.writeTo(wr);
1561 end;
1562 finally
1563 wr.unindent();
1564 end;
1565 wr.putIndent();
1566 wr.put('}'#10);
1567 end;
1570 procedure TDynRecord.parseValue (pr: TTextParser; curheader: TDynRecord);
1571 var
1572 f, c: Integer;
1573 fld: TDynField;
1574 rec, trc: TDynRecord;
1575 //success: Boolean;
1576 begin
1577 if (mOwner = nil) then raise Exception.Create(Format('can''t parse record ''%s'' value without owner', [mName]));
1579 // not a header?
1580 if (curheader <> self) then
1581 begin
1582 // id?
1583 if (pr.tokType = pr.TTId) then mId := pr.expectId();
1584 end;
1586 writeln('parsing record <', mName, '>');
1587 pr.expectTT(pr.TTBegin);
1588 while (pr.tokType <> pr.TTEnd) do
1589 begin
1590 if (pr.tokType <> pr.TTId) then raise Exception.Create('identifier expected');
1591 //writeln('<', pr.tokStr, ':', asheader, '>');
1593 // records
1594 if (curheader = self) then
1595 begin
1596 // add records with this type (if any)
1597 trc := mOwner.findRecType(pr.tokStr);
1598 if (trc <> nil) then
1599 begin
1600 rec := trc.clone();
1601 try
1602 pr.skipToken();
1603 rec.parseValue(pr, curheader);
1604 if (Length(rec.mId) > 0) then
1605 begin
1606 fld := field[pr.tokStr];
1607 if (fld <> nil) then
1608 begin
1609 for c := 0 to High(fld.mRVal) do
1610 begin
1611 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]));
1612 end;
1613 end;
1614 end;
1615 mOwner.addRecordByType(rec.mName, rec, curheader);
1616 rec := nil;
1617 finally
1618 rec.Free();
1619 end;
1620 continue;
1621 end;
1622 end;
1624 // fields
1625 fld := field[pr.tokStr];
1626 if (fld <> nil) then
1627 begin
1628 if fld.defined then raise Exception.Create(Format('duplicate field ''%s'' in record ''%s''', [fld.mName, mName]));
1629 if fld.internal then raise Exception.Create(Format('internal field ''%s'' in record ''%s''', [fld.mName, mName]));
1630 pr.skipToken();
1631 fld.parseValue(pr, curheader);
1632 continue;
1633 end;
1635 // something is wrong
1636 raise Exception.Create(Format('unknown field ''%s'' in record ''%s''', [pr.tokStr, mName]));
1637 end;
1638 pr.expectTT(pr.TTEnd);
1639 // fix field defaults
1640 for f := 0 to High(mFields) do mFields[f].fixDefaultValue();
1641 writeln('done parsing record <', mName, '>');
1642 end;
1645 procedure TDynRecord.parseBinValue (st: TStream);
1646 begin
1647 end;
1650 // ////////////////////////////////////////////////////////////////////////// //
1651 constructor TDynEBS.Create (pr: TTextParser);
1652 begin
1653 cleanup();
1654 parseDef(pr);
1655 end;
1658 destructor TDynEBS.Destroy ();
1659 begin
1660 cleanup();
1661 inherited;
1662 end;
1665 procedure TDynEBS.cleanup ();
1666 begin
1667 mIsEnum := false;
1668 mName := '';
1669 mIds := nil;
1670 mVals := nil;
1671 mMaxName := '';
1672 mMaxVal := 0;
1673 end;
1676 function TDynEBS.findByName (const aname: AnsiString): Integer;
1677 begin
1678 result := 0;
1679 while (result < Length(mIds)) do
1680 begin
1681 if (CompareText(aname, mIds[result]) = 0) then exit;
1682 Inc(result);
1683 end;
1684 result := -1;
1685 end;
1688 function TDynEBS.hasByName (const aname: AnsiString): Boolean; inline;
1689 begin
1690 result := (findByName(aname) >= 0);
1691 end;
1694 function TDynEBS.getFieldByName (const aname: AnsiString): Integer; inline;
1695 var
1696 f: Integer;
1697 begin
1698 f := findByName(aname);
1699 if (f >= 0) then result := mVals[f] else result := 0;
1700 end;
1703 function TDynEBS.definition (): AnsiString;
1704 var
1705 f, cv: Integer;
1706 begin
1707 if mIsEnum then result :='enum ' else result := 'bitset ';
1708 result += mName;
1709 result += ' {'#10;
1710 // fields
1711 if mIsEnum then cv := 0 else cv := 1;
1712 for f := 0 to High(mIds) do
1713 begin
1714 if (mIds[f] = mMaxName) then continue;
1715 result += ' '+mIds[f];
1716 if (mVals[f] <> cv) then
1717 begin
1718 result += Format(' = %d', [mVals[f]]);
1719 if mIsEnum then cv := mVals[f];
1720 result += ','#10;
1721 end
1722 else
1723 begin
1724 result += Format(', // %d'#10, [mVals[f]]);
1725 end;
1726 if mIsEnum then Inc(cv) else if (mVals[f] = cv) then cv := cv shl 1;
1727 end;
1728 // max field
1729 if (Length(mMaxName) > 0) then result += ' '+mMaxName+' = MAX,'#10;
1730 result += '}';
1731 end;
1734 procedure TDynEBS.parseDef (pr: TTextParser);
1735 var
1736 idname: AnsiString;
1737 cv, v: Integer;
1738 f: Integer;
1739 skipAdd: Boolean;
1740 hasV: Boolean;
1741 begin
1742 if pr.eatId('enum') then mIsEnum := true
1743 else if pr.eatId('bitset') then mIsEnum := false
1744 else pr.expectId('enum');
1745 mName := pr.expectId();
1746 mMaxVal := Integer($80000000);
1747 if mIsEnum then cv := 0 else cv := 1;
1748 pr.expectTT(pr.TTBegin);
1749 while (pr.tokType <> pr.TTEnd) do
1750 begin
1751 idname := pr.expectId();
1752 for f := 0 to High(mIds) do
1753 begin
1754 if (CompareText(mIds[f], idname) = 0) then raise Exception.Create(Format('duplicate field ''%s'' in enum/bitset ''%s''', [idname, mName]));
1755 end;
1756 if (CompareText(mMaxName, idname) = 0) then raise Exception.Create(Format('duplicate field ''%s'' in enum/bitset ''%s''', [idname, mName]));
1757 skipAdd := false;
1758 hasV := false;
1759 v := cv;
1760 // has value?
1761 if pr.eatDelim('=') then
1762 begin
1763 if pr.eatId('MAX') then
1764 begin
1765 if (Length(mMaxName) > 0) then raise Exception.Create(Format('duplicate max field ''%s'' in enum/bitset ''%s''', [idname, mName]));
1766 mMaxName := idname;
1767 skipAdd := true;
1768 end
1769 else
1770 begin
1771 v := pr.expectInt();
1772 if mIsEnum then cv := v;
1773 hasV := true;
1774 end;
1775 end;
1776 // append it?
1777 if not skipAdd then
1778 begin
1779 // fix maxvalue
1780 if mIsEnum or (not hasV) then
1781 begin
1782 if (mMaxVal < v) then mMaxVal := v;
1783 end;
1784 SetLength(mIds, Length(mIds)+1);
1785 mIds[High(mIds)] := idname;
1786 SetLength(mVals, Length(mIds));
1787 mVals[High(mVals)] := v;
1788 // next cv
1789 if mIsEnum or (not hasV) then
1790 begin
1791 if mIsEnum then Inc(cv) else cv := cv shl 1;
1792 end;
1793 end;
1794 if (pr.tokType = pr.TTEnd) then break;
1795 pr.expectTT(pr.TTComma);
1796 while pr.eatTT(pr.TTComma) do begin end;
1797 end;
1798 pr.expectTT(pr.TTEnd);
1799 // add max field
1800 if (Length(mMaxName) > 0) then
1801 begin
1802 SetLength(mIds, Length(mIds)+1);
1803 mIds[High(mIds)] := mMaxName;
1804 SetLength(mVals, Length(mIds));
1805 mVals[High(mVals)] := mMaxVal;
1806 end;
1807 end;
1810 // ////////////////////////////////////////////////////////////////////////// //
1811 constructor TDynMapDef.Create (pr: TTextParser);
1812 begin
1813 recTypes := nil;
1814 trigTypes := nil;
1815 ebsTypes := nil;
1816 parseDef(pr);
1817 end;
1820 destructor TDynMapDef.Destroy ();
1821 var
1822 f: Integer;
1823 begin
1824 for f := 0 to High(recTypes) do recTypes[f].Free();
1825 for f := 0 to High(trigTypes) do trigTypes[f].Free();
1826 for f := 0 to High(ebsTypes) do ebsTypes[f].Free();
1827 recTypes := nil;
1828 trigTypes := nil;
1829 ebsTypes := nil;
1830 inherited;
1831 end;
1834 function TDynMapDef.getHeaderRecType (): TDynRecord; inline;
1835 begin
1836 if (Length(recTypes) = 0) then raise Exception.Create('no header in empty mapdef');
1837 result := recTypes[0];
1838 end;
1841 function TDynMapDef.findRecType (const aname: AnsiString): TDynRecord;
1842 var
1843 f: Integer;
1844 begin
1845 for f := 0 to High(recTypes) do
1846 begin
1847 if (CompareText(recTypes[f].name, aname) = 0) then begin result := recTypes[f]; exit; end;
1848 end;
1849 result := nil;
1850 end;
1853 function TDynMapDef.findTrigFor (const aname: AnsiString): TDynRecord;
1854 var
1855 f: Integer;
1856 begin
1857 for f := 0 to High(trigTypes) do
1858 begin
1859 if (trigTypes[f].isForTrig[aname]) then begin result := trigTypes[f]; exit; end;
1860 end;
1861 result := nil;
1862 end;
1865 function TDynMapDef.findEBSType (const aname: AnsiString): TDynEBS;
1866 var
1867 f: Integer;
1868 begin
1869 for f := 0 to High(ebsTypes) do
1870 begin
1871 if (CompareText(ebsTypes[f].name, aname) = 0) then begin result := ebsTypes[f]; exit; end;
1872 end;
1873 result := nil;
1874 end;
1877 function TDynMapDef.findRecordByTypeId (const atypename, aid: AnsiString; curheader: TDynRecord): TDynRecord;
1878 var
1879 rec: TDynRecord;
1880 fld: TDynField;
1881 f: Integer;
1882 begin
1883 result := nil;
1884 // find record type
1885 if (curheader = nil) then exit;
1886 //writeln('searching for type <', atypename, '>');
1887 rec := findRecType(atypename);
1888 if (rec = nil) then exit;
1889 // find record data
1890 //writeln('searching for data of type <', atypename, '>');
1891 fld := curheader.field[atypename];
1892 if (fld = nil) then exit;
1893 if (fld.mType <> fld.TType.TList) then exit;
1894 // find by id
1895 //writeln('searching for data of type <', atypename, '> with id <', aid, '> (', Length(fld.mRVal), ')');
1896 for f := 0 to High(fld.mRVal) do
1897 begin
1898 if (CompareText(fld.mRVal[f].mId, aid) = 0) then
1899 begin
1900 //writeln(' FOUND!');
1901 result := fld.mRVal[f];
1902 exit;
1903 end;
1904 end;
1905 // alas
1906 end;
1909 procedure TDynMapDef.addRecordByType (const atypename: AnsiString; rc: TDynRecord; curheader: TDynRecord);
1910 var
1911 rec: TDynRecord;
1912 fld: TDynField;
1913 begin
1914 assert(curheader <> nil);
1915 // find record type
1916 rec := findRecType(atypename);
1917 assert(rec <> nil);
1918 // find record data
1919 //writeln('searching for data of type <', atypename, '>');
1920 fld := curheader.field[atypename];
1921 if (fld = nil) then
1922 begin
1923 // first record
1924 fld := TDynField.Create(atypename, TDynField.TType.TList);
1925 fld.mOwner := curheader;
1926 SetLength(curheader.mFields, Length(curheader.mFields)+1);
1927 curheader.mFields[High(curheader.mFields)] := fld;
1928 end;
1929 if (fld.mType <> fld.TType.TList) then exit;
1930 // add
1931 SetLength(fld.mRVal, Length(fld.mRVal)+1);
1932 fld.mRVal[High(fld.mRVal)] := rc;
1933 end;
1936 function TDynMapDef.findRecordNumByType (const atypename: AnsiString; rc: TDynRecord; curheader: TDynRecord): Integer;
1937 var
1938 rec: TDynRecord;
1939 fld: TDynField;
1940 f: Integer;
1941 begin
1942 result := -1;
1943 if (curheader = nil) then exit;
1944 // find record type
1945 rec := findRecType(atypename);
1946 if (rec = nil) then exit;
1947 // find record data
1948 fld := curheader.field[atypename];
1949 if (fld = nil) then exit;
1950 if (fld.mType <> fld.TType.TList) then exit;
1951 // find by ref
1952 for f := 0 to High(fld.mRVal) do
1953 begin
1954 if (fld.mRVal[f] = rc) then
1955 begin
1956 result := f;
1957 exit;
1958 end;
1959 end;
1960 // alas
1961 end;
1964 procedure TDynMapDef.parseDef (pr: TTextParser);
1965 var
1966 dr, hdr: TDynRecord;
1967 eb: TDynEBS;
1968 f: Integer;
1969 begin
1970 hdr := nil;
1971 while true do
1972 begin
1973 if not pr.skipBlanks() then break;
1974 if (pr.tokType <> pr.TTId) then raise Exception.Create('identifier expected');
1976 if (pr.tokStr = 'enum') or (pr.tokStr = 'bitset') then
1977 begin
1978 eb := TDynEBS.Create(pr);
1979 if (findEBSType(eb.name) <> nil) then
1980 begin
1981 eb.Free();
1982 raise Exception.Create(Format('duplicate enum/bitset ''%s''', [eb.name]));
1983 end;
1984 eb.mOwner := self;
1985 SetLength(ebsTypes, Length(ebsTypes)+1);
1986 ebsTypes[High(ebsTypes)] := eb;
1987 //writeln(eb.definition); writeln;
1988 continue;
1989 end;
1991 if (pr.tokStr = 'TriggerData') then
1992 begin
1993 dr := TDynRecord.Create(pr);
1994 for f := 0 to High(dr.mTrigTypes) do
1995 begin
1996 if (findTrigFor(dr.mTrigTypes[f]) <> nil) then
1997 begin
1998 dr.Free();
1999 raise Exception.Create(Format('duplicate trigdata ''%s''', [dr.mTrigTypes[f]]));
2000 end;
2001 end;
2002 dr.mOwner := self;
2003 SetLength(trigTypes, Length(trigTypes)+1);
2004 trigTypes[High(trigTypes)] := dr;
2005 //writeln(dr.definition); writeln;
2006 continue;
2007 end;
2009 dr := TDynRecord.Create(pr);
2010 //writeln(dr.definition); writeln;
2011 if (findRecType(dr.name) <> nil) then begin dr.Free(); raise Exception.Create(Format('duplicate record ''%s''', [dr.name])); end;
2012 if (hdr <> nil) and (CompareText(dr.name, hdr.name) = 0) then begin dr.Free(); raise Exception.Create(Format('duplicate record ''%s''', [dr.name])); end;
2013 dr.mOwner := self;
2014 if dr.mHeader then
2015 begin
2016 if (hdr <> nil) then begin dr.Free(); raise Exception.Create(Format('duplicate header record ''%s'' (previous is ''%s'')', [dr.name, hdr.name])); end;
2017 hdr := dr;
2018 end
2019 else
2020 begin
2021 SetLength(recTypes, Length(recTypes)+1);
2022 recTypes[High(recTypes)] := dr;
2023 end;
2024 end;
2026 if (hdr = nil) then raise Exception.Create('header definition not found in mapdef');
2027 SetLength(recTypes, Length(recTypes)+1);
2028 for f := High(recTypes) downto 1 do recTypes[f] := recTypes[f-1];
2029 recTypes[0] := hdr;
2030 end;
2033 // ////////////////////////////////////////////////////////////////////////// //
2034 function TDynMapDef.parseMap (pr: TTextParser): TDynRecord;
2035 var
2036 res: TDynRecord = nil;
2037 begin
2038 result := nil;
2039 try
2040 pr.expectId(headerType.name);
2041 res := headerType.clone();
2042 res.parseValue(pr, res);
2043 result := res;
2044 res := nil;
2045 except on E: Exception do
2046 begin
2047 res.Free();
2048 raise;
2049 end;
2050 end;
2051 end;
2054 function TDynMapDef.parseBinMap (st: TStream): TDynRecord;
2055 begin
2056 result := nil;
2057 end;
2060 end.