X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fshared%2Fxdynrec.pas;h=c71250ce9ee8bc4697133ee8960465739b57f537;hb=5c0e145428d60023677261d107632740cec9342b;hp=37d27dfca414f09c812edf9af38aa0ed6c9a31be;hpb=a5f29d598139e36e31e5d0f1d0d93f7518c8f6d9;p=d2df-sdl.git diff --git a/src/shared/xdynrec.pas b/src/shared/xdynrec.pas index 37d27df..c71250c 100644 --- a/src/shared/xdynrec.pas +++ b/src/shared/xdynrec.pas @@ -20,7 +20,7 @@ unit xdynrec; interface uses - Classes, + Variants, Classes, xparser, xstreams, utils, hashtable; @@ -63,7 +63,6 @@ type mRecRef: TDynRecord; // for TEBS.TRec mMaxDim: Integer; // for byte and char arrays; <0: not an array; 0: impossible value mBinOfs: Integer; // offset in binary; <0 - none - mRecOfs: Integer; // offset in record; <0 - none mSepPosSize: Boolean; // for points and sizes, use separate fields mAsT: Boolean; // for points and sizes, use separate fields, names starts with `t` mDefined: Boolean; @@ -85,6 +84,10 @@ type // for binary parser mRecRefId: AnsiString; + // for userdata + mTagInt: Integer; + mTagPtr: Pointer; + private procedure cleanup (); @@ -98,6 +101,13 @@ type function getListItem (idx: Integer): TDynRecord; inline; overload; function getListItem (const aname: AnsiString): TDynRecord; inline; overload; + function getRecRefIndex (): Integer; + + procedure setIVal (v: Integer); inline; + + function getVar (): Variant; + procedure setVar (val: Variant); + protected // returns `true` for duplicate record id function addListItem (rec: TDynRecord): Boolean; inline; @@ -105,6 +115,7 @@ type public constructor Create (const aname: AnsiString; atype: TType); constructor Create (pr: TTextParser); + constructor Create (const aname: AnsiString; val: Variant); destructor Destroy (); override; class function getTypeName (t: TType): AnsiString; @@ -125,28 +136,37 @@ type procedure setValue (const s: AnsiString); + function GetEnumerator (): TDynRecList.TEnumerator; inline; + public property pasname: AnsiString read mPasName; property name: AnsiString read mName; property baseType: TType read mType; - property defined: Boolean read mDefined write mDefined; + property negbool: Boolean read mNegBool; + property defined: Boolean read mDefined; property internal: Boolean read mInternal write mInternal; - property ival: Integer read mIVal; + property hasTPrefix: Boolean read mAsT; + property separatePasFields: Boolean read mSepPosSize; + property binOfs: Integer read mBinOfs; + property ival: Integer read mIVal write setIVal; + property ival2: Integer read mIVal2; property sval: AnsiString read mSVal; property hasDefault: Boolean read mHasDefault; property defsval: AnsiString read mDefSVal; property ebs: TEBS read mEBS; property ebstype: TObject read mEBSType; property ebstypename: AnsiString read mEBSTypeName; // enum/bitset name + property recref: TDynRecord read mRecRef; + property recrefIndex: Integer read getRecRefIndex; // search for this record in header; -1: not found // for lists property count: Integer read getListCount; property item[idx: Integer]: TDynRecord read getListItem; property items[const aname: AnsiString]: TDynRecord read getListItem; default; // alas, FPC 3+ lost property overloading feature - - property x: Integer read mIVal; - property w: Integer read mIVal; - property y: Integer read mIVal2; - property h: Integer read mIVal2; + // userdata + property tagInt: Integer read mTagInt write mTagInt; + property tagPtr: Pointer read mTagPtr write mTagPtr; + // + property varvalue: Variant read getVar write setVar; end; @@ -167,16 +187,25 @@ type mBinBlock: Integer; // -1: none mHeaderRec: TDynRecord; // for "value" records this is header record with data, for "type" records this is header type record + // for userdata + mTagInt: Integer; + mTagPtr: Pointer; + private procedure parseDef (pr: TTextParser); // parse definition function findByName (const aname: AnsiString): Integer; inline; function hasByName (const aname: AnsiString): Boolean; inline; function getFieldByName (const aname: AnsiString): TDynField; inline; + function getFieldAt (idx: Integer): TDynField; inline; + function getCount (): Integer; inline; function getIsTrigData (): Boolean; inline; function getIsForTrig (const aname: AnsiString): Boolean; inline; + function getForTrigCount (): Integer; inline; + function getForTrigAt (idx: Integer): AnsiString; inline; + protected function findRecordByTypeId (const atypename, aid: AnsiString): TDynRecord; function findRecordNumByType (const atypename: AnsiString; rc: TDynRecord): Integer; @@ -209,6 +238,13 @@ type // number of records of the given instance function instanceCount (const typename: AnsiString): Integer; + //procedure setUserField (const fldname: AnsiString; v: LongInt); + //procedure setUserField (const fldname: AnsiString; v: AnsiString); + //procedure setUserField (const fldname: AnsiString; v: Boolean); + + function getUserVar (const aname: AnsiString): Variant; + procedure setUserVar (const aname: AnsiString; val: Variant); + public property id: AnsiString read mId; // for map parser property pasname: AnsiString read mPasName; @@ -216,11 +252,20 @@ type property size: Integer read mSize; // size in bytes //property fields: TDynFieldList read mFields; property has[const aname: AnsiString]: Boolean read hasByName; - property field[const aname: AnsiString]: TDynField read getFieldByName; + property count: Integer read getCount; + property field[const aname: AnsiString]: TDynField read getFieldByName; default; + property fieldAt[idx: Integer]: TDynField read getFieldAt; property isTrigData: Boolean read getIsTrigData; property isForTrig[const aname: AnsiString]: Boolean read getIsForTrig; - property headerType: TDynRecord read mHeaderRec; + property forTrigCount: Integer read getForTrigCount; + property forTrigAt[idx: Integer]: AnsiString read getForTrigAt; + property headerRec: TDynRecord read mHeaderRec; property isHeader: Boolean read mHeader; + // userdata + property tagInt: Integer read mTagInt write mTagInt; + property tagPtr: Pointer read mTagPtr write mTagPtr; + // userfields + property user[const aname: AnsiString]: Variant read getUserVar write setUserVar; end; TDynEBS = class @@ -271,6 +316,9 @@ type function getHeaderRecType (): TDynRecord; inline; + function getTrigTypeCount (): Integer; inline; + function getTrigTypeAt (idx: Integer): TDynRecord; inline; + public constructor Create (pr: TTextParser); // parses data definition destructor Destroy (); override; @@ -280,6 +328,7 @@ type function findEBSType (const aname: AnsiString): TDynEBS; function pasdef (): AnsiString; + function pasdefconst (): AnsiString; // creates new header record function parseMap (pr: TTextParser): TDynRecord; @@ -289,6 +338,8 @@ type public property headerType: TDynRecord read getHeaderRecType; + property trigTypeCount: Integer read getTrigTypeCount; + property trigType[idx: Integer]: TDynRecord read getTrigTypeAt; end; @@ -308,6 +359,14 @@ uses function StrEqu (const a, b: AnsiString): Boolean; inline; begin result := (a = b); end; +// ////////////////////////////////////////////////////////////////////////// // +function TDynField.GetEnumerator (): TDynRecList.TEnumerator; inline; +begin + //result := TListEnumerator.Create(mRVal); + if (mRVal <> nil) then result := mRVal.GetEnumerator else result := TDynRecList.TEnumerator.Create(nil, 0); +end; + + // ////////////////////////////////////////////////////////////////////////// // constructor TDynField.Create (const aname: AnsiString; atype: TType); begin @@ -332,6 +391,72 @@ begin end; +constructor TDynField.Create (const aname: AnsiString; val: Variant); + procedure setInt32 (v: LongInt); + begin + case mType of + TType.TBool: + if (v = 0) then mIVal := 0 + else if (v = 1) then mIVal := 1 + else raise Exception.Create('cannot convert shortint variant to field value'); + TType.TByte: + if (v >= -128) and (v <= 127) then mIVal := v + else raise Exception.Create('cannot convert shortint variant to field value'); + TType.TUByte: + if (v >= 0) and (v <= 255) then mIVal := v + else raise Exception.Create('cannot convert shortint variant to field value'); + TType.TShort: + if (v >= -32768) and (v <= 32767) then mIVal := v + else raise Exception.Create('cannot convert shortint variant to field value'); + TType.TUShort: + if (v >= 0) and (v <= 65535) then mIVal := v + else raise Exception.Create('cannot convert shortint variant to field value'); + TType.TInt: + mIVal := v; + TType.TUInt: + mIVal := v; + TType.TString: + mSVal := formatstrf('%s', [v]); + else + raise Exception.Create('cannot convert integral variant to field value'); + end; + end; +begin + mRVal := nil; + mRecRef := nil; + mRHash := nil; + cleanup(); + mName := aname; + case varType(val) of + varEmpty: raise Exception.Create('cannot convert empty variant to field value'); + varNull: raise Exception.Create('cannot convert null variant to field value'); + varSingle: raise Exception.Create('cannot convert single variant to field value'); + varDouble: raise Exception.Create('cannot convert double variant to field value'); + varDecimal: raise Exception.Create('cannot convert decimal variant to field value'); + varCurrency: raise Exception.Create('cannot convert currency variant to field value'); + varDate: raise Exception.Create('cannot convert date variant to field value'); + varOleStr: raise Exception.Create('cannot convert olestr variant to field value'); + varStrArg: raise Exception.Create('cannot convert stdarg variant to field value'); + varString: mType := TType.TString; + varDispatch: raise Exception.Create('cannot convert dispatch variant to field value'); + varBoolean: mType := TType.TBool; + varVariant: raise Exception.Create('cannot convert variant variant to field value'); + varUnknown: raise Exception.Create('cannot convert unknown variant to field value'); + varByte: mType := TType.TUByte; + varWord: mType := TType.TUShort; + varShortInt: mType := TType.TByte; + varSmallint: mType := TType.TShort; + varInteger: mType := TType.TInt; + varInt64: raise Exception.Create('cannot convert int64 variant to field value'); + varLongWord: raise Exception.Create('cannot convert longword variant to field value'); + varQWord: raise Exception.Create('cannot convert uint64 variant to field value'); + varError: raise Exception.Create('cannot convert error variant to field value'); + else raise Exception.Create('cannot convert undetermined variant to field value'); + end; + varvalue := val; +end; + + destructor TDynField.Destroy (); begin cleanup(); @@ -353,7 +478,6 @@ begin mRecRef := nil; mMaxDim := -1; mBinOfs := -1; - mRecOfs := -1; mSepPosSize := false; mAsT := false; mHasDefault := false; @@ -372,6 +496,8 @@ begin mAsMonsterId := false; mNegBool := false; mRecRefId := ''; + mTagInt := 0; + mTagPtr := nil; end; @@ -397,7 +523,6 @@ begin result.mRecRef := mRecRef; result.mMaxDim := mMaxDim; result.mBinOfs := mBinOfs; - result.mRecOfs := mRecOfs; result.mSepPosSize := mSepPosSize; result.mAsT := mAsT; result.mDefined := mDefined; @@ -416,6 +541,128 @@ begin result.mEBSTypeName := mEBSTypeName; result.mEBSType := mEBSType; result.mRecRefId := mRecRefId; + result.mTagInt := mTagInt; + result.mTagPtr := mTagPtr; +end; + + +procedure TDynField.setIVal (v: Integer); inline; +begin + //FIXME: check type + mIVal := v; + mDefined := true; +end; + + +function TDynField.getVar (): Variant; +begin + if (mEBS = TEBS.TRec) then begin result := LongInt(getRecRefIndex); exit; end; + case mType of + TType.TBool: result := (mIVal <> 0); + TType.TChar: result := mSVal; + TType.TByte: result := ShortInt(mIVal); + TType.TUByte: result := Byte(mIVal); + TType.TShort: result := SmallInt(mIVal); + TType.TUShort: result := Word(mIVal); + TType.TInt: result := LongInt(mIVal); + TType.TUInt: result := LongWord(mIVal); + TType.TString: result := mSVal; + TType.TPoint: raise Exception.Create('cannot convert point field to variant'); + TType.TSize: raise Exception.Create('cannot convert size field to variant'); + TType.TList: raise Exception.Create('cannot convert list field to variant'); + TType.TTrigData: raise Exception.Create('cannot convert trigdata field to variant'); + else result := Unassigned; raise Exception.Create('ketmar forgot to handle some field type'); + end; +end; + + +procedure TDynField.setVar (val: Variant); + procedure setInt32 (v: LongInt); + begin + case mType of + TType.TBool: + if (v = 0) then mIVal := 0 + else if (v = 1) then mIVal := 1 + else raise Exception.Create('cannot convert shortint variant to field value'); + TType.TByte: + if (v >= -128) and (v <= 127) then mIVal := v + else raise Exception.Create('cannot convert shortint variant to field value'); + TType.TUByte: + if (v >= 0) and (v <= 255) then mIVal := v + else raise Exception.Create('cannot convert shortint variant to field value'); + TType.TShort: + if (v >= -32768) and (v <= 32767) then mIVal := v + else raise Exception.Create('cannot convert shortint variant to field value'); + TType.TUShort: + if (v >= 0) and (v <= 65535) then mIVal := v + else raise Exception.Create('cannot convert shortint variant to field value'); + TType.TInt: + mIVal := v; + TType.TUInt: + mIVal := v; + TType.TString: + mSVal := formatstrf('%s', [v]); + else + raise Exception.Create('cannot convert integral variant to field value'); + end; + end; +begin + case varType(val) of + varEmpty: raise Exception.Create('cannot convert empty variant to field value'); + varNull: raise Exception.Create('cannot convert null variant to field value'); + varSingle: raise Exception.Create('cannot convert single variant to field value'); + varDouble: raise Exception.Create('cannot convert double variant to field value'); + varDecimal: raise Exception.Create('cannot convert decimal variant to field value'); + varCurrency: raise Exception.Create('cannot convert currency variant to field value'); + varDate: raise Exception.Create('cannot convert date variant to field value'); + varOleStr: raise Exception.Create('cannot convert olestr variant to field value'); + varStrArg: raise Exception.Create('cannot convert stdarg variant to field value'); + varString: + if (mType = TType.TChar) or (mType = TType.TString) then + begin + mSVal := val; + end + else + begin + raise Exception.Create('cannot convert string variant to field value'); + end; + varDispatch: raise Exception.Create('cannot convert dispatch variant to field value'); + varBoolean: + case mType of + TType.TBool, + TType.TByte, + TType.TUByte, + TType.TShort, + TType.TUShort, + TType.TInt, + TType.TUInt: + if val then mIVal := 1 else mIVal := 0; + TType.TString: + if val then mSVal := 'true' else mSVal := 'false'; + else + raise Exception.Create('cannot convert boolean variant to field value'); + end; + varVariant: raise Exception.Create('cannot convert variant variant to field value'); + varUnknown: raise Exception.Create('cannot convert unknown variant to field value'); + varByte, + varWord, + varShortInt, + varSmallint, + varInteger: + setInt32(val); + varInt64: + if (val < Int64(LongInt($80000000))) or (val > LongInt($7FFFFFFF)) then + raise Exception.Create('cannot convert boolean variant to field value') + else + mIVal := LongInt(val); + varLongWord: + if (val > LongWord($7FFFFFFF)) then raise Exception.Create('cannot convert longword variant to field value') + else setInt32(Integer(val)); + varQWord: raise Exception.Create('cannot convert uint64 variant to field value'); + varError: raise Exception.Create('cannot convert error variant to field value'); + else raise Exception.Create('cannot convert undetermined variant to field value'); + end; + mDefined := true; end; @@ -590,7 +837,7 @@ begin result := mPasName+' is '+quoteStr(mName)+' type '; result += getTypeName(mType); if (mMaxDim >= 0) then result += Format('[%d]', [mMaxDim]); - if (mRecOfs >= 0) then result += Format(' offset %d', [mRecOfs]); + if (mBinOfs >= 0) then result += Format(' offset %d', [mBinOfs]); case mEBS of TEBS.TNone: begin end; TEBS.TRec: result += ' '+mEBSTypeName; @@ -648,7 +895,7 @@ var ainternal: Boolean; omitdef: Boolean; defstr: AnsiString; - defint: Integer; + defint, defint2: Integer; hasdefStr: Boolean; hasdefInt: Boolean; hasdefId: Boolean; @@ -656,6 +903,7 @@ var lebs: TDynField.TEBS; unique: Boolean; asmonid: Boolean; + defech: AnsiChar; begin fldpasname := ''; fldname := ''; @@ -669,6 +917,7 @@ begin omitdef := false; defstr := ''; defint := 0; + defint2 := 0; hasdefStr := false; hasdefInt := false; hasdefId := false; @@ -750,6 +999,14 @@ begin hasdefInt := true; defint := pr.expectInt(); end; + pr.TTDelim: + begin + hasdefInt := true; + if pr.eatDelim('[') then defech := ']' else begin pr.expectDelim('('); defech := ')'; end; + defint := pr.expectInt(); + defint2 := pr.expectInt(); + pr.expectDelim(defech); + end; else raise Exception.Create(Format('field ''%s'' has invalid default', [fldname])); end; @@ -803,8 +1060,13 @@ begin end; if hasdefStr then self.mDefUnparsed := quoteStr(defstr) - else if hasdefInt then self.mDefUnparsed := Format('%d', [defint]) - else if hasdefId then self.mDefUnparsed := defstr; + else if hasdefId then self.mDefUnparsed := defstr + else if hasdefInt then + begin + if (mType = TType.TPoint) then self.mDefUnparsed := Format('(%d %d)', [defint, defint2]) + else if (mType = TType.TSize) then self.mDefUnparsed := Format('[%d %d]', [defint, defint2]) + else self.mDefUnparsed := Format('%d', [defint]); + end; self.mHasDefault := (hasdefStr or hasdefId or hasdefInt); self.mPasName := fldpasname; @@ -814,7 +1076,6 @@ begin self.mAsMonsterId := asmonid; self.mMaxDim := lmaxdim; self.mBinOfs := fldofs; - self.mRecOfs := fldofs; self.mSepPosSize := (asxy or aswh); self.mAsT := ast; self.mOmitDef := omitdef; @@ -822,6 +1083,13 @@ begin end; +function TDynField.getRecRefIndex (): Integer; +begin + if (mRecRef = nil) then begin result := -1; exit; end; + result := mOwner.findRecordNumByType(mEBSTypeName, mRecRef); +end; + + procedure TDynField.writeBinTo (st: TStream); var s: AnsiString; @@ -1122,6 +1390,7 @@ begin raise Exception.Create(Format('cannot parse field ''%s'' yet', [mName])); end; + procedure TDynField.parseBinValue (st: TStream); var rec, rc: TDynRecord; @@ -1564,6 +1833,8 @@ begin mHeader := false; mHeaderRec := nil; mBinBlock := -1; + mTagInt := 0; + mTagPtr := nil; parseDef(pr); end; @@ -1579,6 +1850,8 @@ begin mTrigTypes := nil; mHeader := false; mHeaderRec := nil; + mTagInt := 0; + mTagPtr := nil; end; @@ -1593,6 +1866,8 @@ begin {$ENDIF} mTrigTypes := nil; mHeaderRec := nil; + mTagInt := 0; + mTagPtr := nil; inherited; end; @@ -1652,6 +1927,18 @@ begin end; +function TDynRecord.getFieldAt (idx: Integer): TDynField; inline; +begin + if (idx >= 0) and (idx < mFields.count) then result := mFields[idx] else result := nil; +end; + + +function TDynRecord.getCount (): Integer; inline; +begin + result := mFields.count; +end; + + function TDynRecord.getIsTrigData (): Boolean; inline; begin result := (Length(mTrigTypes) > 0); @@ -1668,6 +1955,18 @@ begin end; +function TDynRecord.getForTrigCount (): Integer; inline; +begin + result := Length(mTrigTypes); +end; + + +function TDynRecord.getForTrigAt (idx: Integer): AnsiString; inline; +begin + if (idx >= 0) and (idx < Length(mTrigTypes)) then result := mTrigTypes[idx] else result := ''; +end; + + function TDynRecord.clone (): TDynRecord; var fld: TDynField; @@ -1689,6 +1988,8 @@ begin result.mHeader := mHeader; result.mBinBlock := mBinBlock; result.mHeaderRec := mHeaderRec; + result.mTagInt := mTagInt; + result.mTagPtr := mTagPtr; end; @@ -1802,6 +2103,112 @@ begin end; +function TDynRecord.getUserVar (const aname: AnsiString): Variant; +var + fld: TDynField; +begin + fld := getFieldByName(aname); + if (fld = nil) then result := Unassigned else result := fld.varvalue; +end; + + +procedure TDynRecord.setUserVar (const aname: AnsiString; val: Variant); +var + fld: TDynField; +begin + fld := getFieldByName(aname); + if (fld = nil) then + begin + if (Length(aname) = 0) then raise Exception.Create('cannot create nameless user field'); + fld := TDynField.Create(aname, val); + fld.mOwner := self; + fld.mInternal := true; + addField(fld); + end + else + begin + fld.varvalue := val; + end; +end; + + +{ +procedure TDynRecord.setUserField (const fldname: AnsiString; v: LongInt); +var + fld: TDynField; +begin + if (Length(fldname) = 0) then exit; + fld := field[fldname]; + if (fld <> nil) then + begin + if (fld.mType <> fld.TType.TInt) or (fld.mEBS <> fld.TEBS.TNone) then + begin + raise Exception.Create(Format('invalid user field ''%s'' type', [fld.name])); + end; + end + else + begin + fld := TDynField.Create(fldname, fld.TType.TInt); + fld.mOwner := self; + fld.mIVal := v; + fld.mInternal := true; + fld.mDefined := true; + addField(fld); + end; +end; + + +procedure TDynRecord.setUserField (const fldname: AnsiString; v: AnsiString); +var + fld: TDynField; +begin + if (Length(fldname) = 0) then exit; + fld := field[fldname]; + if (fld <> nil) then + begin + if (fld.mType <> fld.TType.TString) or (fld.mEBS <> fld.TEBS.TNone) then + begin + raise Exception.Create(Format('invalid user field ''%s'' type', [fld.name])); + end; + end + else + begin + fld := TDynField.Create(fldname, fld.TType.TString); + fld.mOwner := self; + fld.mSVal := v; + fld.mInternal := true; + fld.mDefined := true; + addField(fld); + end; +end; + + +procedure TDynRecord.setUserField (const fldname: AnsiString; v: Boolean); +var + fld: TDynField; +begin + if (Length(fldname) = 0) then exit; + fld := field[fldname]; + if (fld <> nil) then + begin + if (fld.mType <> fld.TType.TBool) or (fld.mEBS <> fld.TEBS.TNone) then + begin + raise Exception.Create(Format('invalid user field ''%s'' type', [fld.name])); + end; + end + else + begin + fld := TDynField.Create(fldname, fld.TType.TBool); + fld.mOwner := self; + fld.mIVal := Integer(v); + fld.mInternal := true; + fld.mDefined := true; + addField(fld); + end; +end; +} + + procedure TDynRecord.parseDef (pr: TTextParser); var fld: TDynField; @@ -2815,4 +3222,19 @@ begin end; +function TDynMapDef.pasdefconst (): AnsiString; +var + ebs: TDynEBS; +begin + result := ''; + result += '// ////////////////////////////////////////////////////////////////////////// //'#10; + result += '// enums and bitsets'#10; + for ebs in ebsTypes do result += #10+ebs.pasdef(); +end; + + +function TDynMapDef.getTrigTypeCount (): Integer; inline; begin result := trigTypes.count; end; +function TDynMapDef.getTrigTypeAt (idx: Integer): TDynRecord; inline; begin if (idx >= 0) and (idx < trigTypes.count) then result := trigTypes[idx] else result := nil; end; + + end.