diff --git a/src/shared/xdynrec.pas b/src/shared/xdynrec.pas
index 223dc51518c92d89b11e8ad9cb957c89c57ba1aa..e56266351a060fd6e0dad26351a58c1e6af99ed2 100644 (file)
--- a/src/shared/xdynrec.pas
+++ b/src/shared/xdynrec.pas
private
mOwner: TDynRecord; // owner record
mName: AnsiString; // field name
+ mTip: AnsiString; // short tip
+ mHelp: AnsiString; // long help
mType: TType; // field type
mIVal: Integer; // for all integer types
mIVal2: Integer; // for point and size
// binary parser and writer (DO NOT USE!)
procedure parseBinValue (st: TStream);
- procedure writeBinTo (st: TStream);
+ procedure writeBinTo (var hasLostData: Boolean; st: TStream);
public
// the following functions are here only for 'mapgen'! DO NOT USE!
// field value as Variant
property value: Variant read getVar write setVar;
+ property tip: AnsiString read mTip;
+ property help: AnsiString read mHelp;
+
public
// userdata (you can use these properties as you want to; they won't be written or read to files)
property tagInt: Integer read mTagInt write mTagInt;
property hasTPrefix: Boolean read mAsT;
property separatePasFields: Boolean read mSepPosSize;
property binOfs: Integer read mBinOfs;
+ property equToDefault: Boolean read isDefaultValue;
end;
mOwner: TDynMapDef;
mId: AnsiString;
mTypeName: AnsiString;
+ mTip: AnsiString; // short tip
+ mHelp: AnsiString; // long help
mSize: Integer;
mFields: TDynFieldList;
{$IF DEFINED(XDYNREC_USE_FIELDHASH)}
// binary parser and writer (DO NOT USE!)
procedure parseBinValue (st: TStream; forceData: Boolean=false);
- procedure writeBinTo (st: TStream; trigbufsz: Integer=-1; onlyFields: Boolean=false);
+ procedure writeBinTo (var hasLostData: Boolean; st: TStream; trigbufsz: Integer=-1; onlyFields: Boolean=false);
public
property mapdef: TDynMapDef read mOwner;
property headerRec: TDynRecord read mHeaderRec; // get header record for this one (header contains all other records, enums, bitsets, etc.)
property isHeader: Boolean read mHeader; // is this a header record?
+ property tip: AnsiString read mTip;
+ property help: AnsiString read mHelp;
+
public
// user fields; user can add arbitrary custom fields
// by default, any user field will be marked as "internal"
mOwner: TDynMapDef;
mIsEnum: Boolean;
mTypeName: AnsiString;
+ mTip: AnsiString; // short tip
+ mHelp: AnsiString; // long help
mIds: array of AnsiString;
mVals: array of Integer;
mMaxName: AnsiString; // MAX field
property isEnum: Boolean read mIsEnum; // is this enum? `false` means "bitset"
property has[const aname: AnsiString]: Boolean read hasByName;
property field[const aname: AnsiString]: Integer read getFieldByName;
+
+ property tip: AnsiString read mTip;
+ property help: AnsiString read mHelp;
end;
public
// parse text or binary map, return new header record
// WARNING! stream must be seekable
- function parseMap (st: TStream): TDynRecord;
+ function parseMap (st: TStream; wasBinary: PBoolean=nil): TDynRecord;
// returns `true` if the given stream can be a map file
// stream position is 0 on return
procedure TDynField.cleanup ();
begin
mName := '';
+ mTip := '';
+ mHelp := '';
mType := TType.TInt;
mIVal := 0;
mIVal2 := 0;
result.mOwner := mOwner;
if (newOwner <> nil) then result.mOwner := newOwner else result.mOwner := mOwner;
result.mName := mName;
+ result.mTip := mTip;
+ result.mHelp := mHelp;
result.mType := mType;
result.mIVal := mIVal;
result.mIVal2 := mIVal2;
asmonid: Boolean;
defech: AnsiChar;
xalias: AnsiString;
+ atip, ahelp: AnsiString;
begin
fldname := '';
fldtype := '';
lmaxdim := -1;
lebs := TDynField.TEBS.TNone;
xalias := '';
+ atip := '';
+ ahelp := '';
// field name
fldname := pr.expectStrOrId();
continue;
end;
+ if pr.eatId('tip') then
+ begin
+ if (Length(atip) > 0) then raise TDynParseException.CreateFmt(pr, 'duplicate tip definition for field ''%s''', [fldname]);
+ atip := pr.expectStr(false);
+ continue;
+ end;
+
+ if pr.eatId('help') then
+ begin
+ if (Length(ahelp) > 0) then raise TDynParseException.CreateFmt(pr, 'duplicate tip definition for field ''%s''', [fldname]);
+ ahelp := pr.expectStr(false);
+ continue;
+ end;
+
if pr.eatId('offset') then
begin
if (fldofs >= 0) then raise TDynParseException.CreateFmt(pr, 'duplicate field ''%s'' offset', [fldname]);
self.mWriteDef := writedef;
self.mInternal := ainternal;
self.mAlias := xalias;
+ self.mTip := atip;
+ self.mHelp := ahelp;
end;
end;
-procedure TDynField.writeBinTo (st: TStream);
+procedure TDynField.writeBinTo (var hasLostData: Boolean; st: TStream);
var
s: AnsiString;
f: Integer;
if (mRecRef <> nil) then
begin
ws := TSFSMemoryChunkStream.Create(buf, mMaxDim);
- mRecRef.writeBinTo(ws, mMaxDim); // as trigdata
+ mRecRef.writeBinTo(hasLostData, ws, mMaxDim); // as trigdata
end;
st.WriteBuffer(buf^, mMaxDim);
finally
exit;
end;
TType.TList:
- begin
- assert(false);
- exit;
- end;
+ raise TDynRecException.Create('cannot write lists to binary format');
TType.TTrigData:
- begin
- assert(false);
- exit;
- end;
+ raise TDynRecException.Create('cannot write triggers to binary format (internal error)');
else raise TDynRecException.Create('ketmar forgot to handle some field type');
end;
end;
begin
if (es.mVals[f] = mask) then
begin
- if not first then wr.put('+') else first := false;
+ if not first then wr.put(' | ') else first := false;
wr.put(es.mIds[f]);
found := true;
break;
result.mOwner := mOwner;
result.mId := mId;
result.mTypeName := mTypeName;
+ result.mTip := mTip;
+ result.mHelp := mHelp;
result.mSize := mSize;
result.mHeader := mHeader;
result.mBinBlock := mBinBlock;
if (mBinBlock < 1) then raise TDynParseException.CreateFmt(pr, 'invalid record ''%s'' binblock: %d', [mTypeName, mBinBlock]);
continue;
end;
+ if pr.eatId('tip') then
+ begin
+ if (Length(mTip) > 0) then raise TDynParseException.CreateFmt(pr, 'duplicate tip definition for record ''%s''', [mTypeName]);
+ mTip := pr.expectStr(false);
+ continue;
+ end;
+ if pr.eatId('help') then
+ begin
+ if (Length(mHelp) > 0) then raise TDynParseException.CreateFmt(pr, 'duplicate help definition for record ''%s''', [mTypeName]);
+ mHelp := pr.expectStr(false);
+ continue;
+ end;
end;
end;
end;
-procedure TDynRecord.writeBinTo (st: TStream; trigbufsz: Integer=-1; onlyFields: Boolean=false);
+procedure TDynRecord.writeBinTo (var hasLostData: Boolean; st: TStream; trigbufsz: Integer=-1; onlyFields: Boolean=false);
var
fld: TDynField;
rec, rv: TDynRecord;
buf: PByte = nil;
ws: TStream = nil;
blk, blkmax: Integer;
- //f, c: Integer;
bufsz: Integer = 0;
blksz: Integer;
begin
// record list?
if (fld.mType = fld.TType.TList) then continue; // later
if fld.mInternal then continue;
- if (fld.mBinOfs < 0) then continue;
+ if (fld.mBinOfs < 0) then
+ begin
+ if not fld.equToDefault then hasLostData := true;
+ continue;
+ end;
if (fld.mBinOfs >= bufsz) then raise TDynRecException.Create('binary value offset is outside of the buffer');
TSFSMemoryChunkStream(ws).setup(buf+fld.mBinOfs, bufsz-fld.mBinOfs);
//writeln('writing field <', fld.mName, '>');
- fld.writeBinTo(ws);
+ fld.writeBinTo(hasLostData, ws);
end;
// write block with normal fields
if (rec = nil) then continue;
if (rec.mBinBlock <> blk) then continue;
if (ws = nil) then ws := TMemoryStream.Create();
- for rv in fld.mRVal do rv.writeBinTo(ws);
+ for rv in fld.mRVal do rv.writeBinTo(hasLostData, ws);
end;
end;
// flush block
var
fld: TDynField;
rec: TDynRecord;
+ putTypeComment: Boolean;
+ f: Integer;
begin
if putHeader then
begin
if (fld.mType = fld.TType.TList) then
begin
if not mHeader then raise TDynRecException.Create('record list in non-header record');
- if (fld.mRVal <> nil) then
+ if (fld.mRVal <> nil) and (fld.mRVal.count > 0) then
begin
+ putTypeComment := true;
for rec in fld.mRVal do
begin
- if (Length(rec.mId) = 0) then continue;
+ if (rec = nil) or (Length(rec.mId) = 0) then continue;
+ if putTypeComment then
+ begin
+ wr.put(#10);
+ if (80-wr.curIndent*2 >= 2) then
+ begin
+ wr.putIndent();
+ for f := wr.curIndent to 80-wr.curIndent do wr.put('/');
+ wr.put(#10);
+ end;
+ putTypeComment := false;
+ wr.putIndent();
+ wr.put('// ');
+ wr.put(fld.name);
+ wr.put(#10);
+ end
+ else
+ begin
+ wr.put(#10);
+ end;
wr.putIndent();
rec.writeTo(wr, true);
end;
begin
mIsEnum := false;
mTypeName := '';
+ mTip := '';
+ mHelp := '';
mIds := nil;
mVals := nil;
mMaxName := '';
mTypeName := pr.expectId();
mMaxVal := Integer($80000000);
if mIsEnum then cv := 0 else cv := 1;
+ while (pr.tokType <> pr.TTBegin) do
+ begin
+ if pr.eatId('tip') then
+ begin
+ if (Length(mTip) > 0) then raise TDynParseException.CreateFmt(pr, 'duplicate tip definition for enum/bitset ''%s''', [mTypeName]);
+ mTip := pr.expectStr(false);
+ continue;
+ end;
+ if pr.eatId('help') then
+ begin
+ if (Length(mHelp) > 0) then raise TDynParseException.CreateFmt(pr, 'duplicate help definition for enum/bitset ''%s''', [mTypeName]);
+ mHelp := pr.expectStr(false);
+ continue;
+ end;
+ break;
+ end;
pr.expectTT(pr.TTBegin);
while (pr.tokType <> pr.TTEnd) do
begin
// WARNING! stream must be seekable
-function TDynMapDef.parseMap (st: TStream): TDynRecord;
+function TDynMapDef.parseMap (st: TStream; wasBinary: PBoolean=nil): TDynRecord;
var
sign: packed array[0..3] of AnsiChar;
pr: TTextParser;
begin
+ if (wasBinary <> nil) then wasBinary^ := false;
st.position := 0;
st.ReadBuffer(sign[0], 4);
st.position := 0;
begin
if (sign[3] = #1) then
begin
+ if (wasBinary <> nil) then wasBinary^ := true;
result := parseBinMap(st);
exit;
end;