{
    This file is part of chepersy
    Copyright (c) 2004-2008 by Anton Rzheshevski (chebmaster@mail.ru),

    See the file COPYING.CPS, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}

//  For internal use only. Used for registering the fake types used for conversion:
//  procedure ConvType(StringID: ansistring; I1, I2: PTypeInfo; Proc: TCustomTypeProcessingProc); forward;

{$ifndef fpc}
  var
    FakeQwordInfo, FakeArrayofQwordInfo, Fake2DArrayOfQwordInfo: TTypeInfo;
  //Delpi is definitely an obsolete dialect of Pascal!
  //Sic transit gloria mundi.
{$endif}

  function IsRegistered(C: CManagedObject): boolean;
  begin
//addlog('-- Index %0, Name %1, ttpi %2', [C.ClassIndex, String(C.ClassName), TManagedObject.classindex]);
  {$ifdef fpc}
    Result:=(C.ClassType = TManagedObject) or (C.ClassIndex <> 0);
  {$else}
    //E2076 This form of method call only allowed for class methods
    // Stupid Delphi :(
    Result:= (TypeNameSpace.Ind(C.ClassName, -1) >=0)
  {$endif}
  end;
  
  type
    CpsUnknownRecord = record a: dword end;
    CpsArrayOfUnknownRecord = array of CpsUnknownRecord;
    Cps2dArrayOfUnknownRecord = array of CpsArrayOfUnknownRecord;

{$ifndef fpc}
  var
    CTP_FakeInfo: TTypeInfo;
    CTP_FakeData: TTypeData;
{$endif}

  //added in 0.8.97
  procedure RegTheOnlyMetaclass; //it's called only once
  var
    T: TTypeRecord;
    i: integer;
    StringID: ansistring;
  begin
    FillChar(T, Sizeof(T), 0);
    StringID:= 'CMANAGEDOBJECT';
    {$ifdef fpc}
      T.Info:= TypeInfo(CManagedObject);
      T.Data:=GetTypeData(T.Info);
      //AddLog('***CManagedObject = %0', [GetEnumName(TypeInfo(TTypeKind), Ord(T.Info^.Kind))]);
    {$else}
      //*****ng Turbo Delphi! I had enough with its crap! >_<#
      FillChar(CTP_FakeInfo, sizeof(CTP_FakeInfo), 0);
      FillChar(CTP_FakeData, sizeof(CTP_FakeData), 0);
      CTP_FakeInfo.Name:= 'CMANAGEDOBJECT';
      CTP_FakeInfo.Kind:= tkUnknown;
      T.Info:= @CTP_Fakeinfo;
      T.data:= @CTP_FakeData;
    {$endif}
    T.name:=NameSpace.Add(StringId);
    TypeNameSpace.Add(StringId);
    T.Kind:=fk_metaclass;
    T.proc:= @RP_Metaclass;
    T.Size:= SizeOf(pointer);
    Types.Add(T);
    if MotherState^.VerboseLog then AddLog(RuEn('  Тип: %0 (%1)','  Type: %0 (%1)')
      ,[NameSpace[T.name], FieldKindToStr(T.Kind)]);
  end;

  procedure ClassesRegistrationStart;//constructor Create;
  begin
    CpsValid:= true;
    GarbageCollector:= TGarbageCollector.Create;
    CpsParserWarnings:= TStringList.Create;
    CpsLog:= TStringList.Create;
    CpsError:= TStringList.Create;

    if MotherState^.VerboseLog then AddLog(RuEn('Начата регистрация классов','Classes registration start'));

    CheckCompilerCompatibility;

    CpsCriticalSection:= TCriticalSection.Create;

    Fields:= TFieldInfos.Create;
    SaveableFields:= TFieldInfos.Create;
    Records:= TAOI.Create;
    ClassAncestors:= T2DAOI.Create;
    NameSpace:=TNameSpace.Create;
    // Delphi/FPC compatibility:
    NameSpace.Add('STRING', 'ANSISTRING');
    NameSpace.Add('INTEGER', 'LONGINT');
    NameSpace.Add('CARDINAL', 'DWORD');
    NameSpace.Add('LONGWORD', 'DWORD');

    // For keeping compatibility with the old data files, prior to types renaming:
    NameSpace.Add('T1DARRAYOFDWORD', 'TARRAYOFDWORD');
    NameSpace.Add('TTRULYPERSISTENT', 'TMANAGEDOBJECT');
    NameSpace.Add('CTRULYPERSISTENT', 'CMANAGEDOBJECT');
    NameSpace.Add('TARRAYOFTRULYPERSISTENT','TARRAYOFMANAGEDOBJECTS');
    NameSpace.Add('T2DARRAYOFTRULYPERSISTENT','T2DARRAYOFMANAGEDOBJECTS');
    NameSpace.Add('TAOTP','TAOMO');

    TypeNameSpace:=TNameSpace.Create;
    ClassNameSpace:=TNameSpace.Create;
    Types:=TTypeRegistry.Create;
    Enums:=T2DAOI.Create;
    CurrentObject:= TAOMO.Create;
    CurrentObjectInd:= TAOI.Create;

   {$ifdef oldhacky}
    InitializeVmtxxxConsts;
   {$else}
    InitializeIndexArrays;
   {$endif}
    
      //since the class field containing the index is in fact
      //  a virtual method pointer, it does initially contain
      //  some value - so we need to store it for future reference,
      //  to discern non-registered classes from registered ones.
      //we don't need to worry about it matching with a real class index,
      //  because this value will be very big (positive or negative),
      //  out of any reasonable scope of class indices.
   {$ifdef oldhacky}
    InvalidClassIndex:=TManagedObject.ClassIndex;
    if VerboseLog then AddLog(RuEn('  Индекс незарегистрированных классов = %0','  InvalidClassIndex = %0'), [InvalidClassIndex]);
   {$endif}

  {$ifdef fpc}
    RegTypeEnum(TypeInfo(pointer)); // *MUST* be #0
  {$else}
   //Delphi: E2134 Type 'Pointer' has no type info
    RegTypeEnum(PTypeInfo(nil)); //a hackaround (see RegType below)
  {$endif}
//    RegClass(TManagedObject);  // *MUST* be #1
//    _RegTypeBin(TypeInfo(dword), sizeof(dword)); // *MUST* be #2
    _RegTypeBin(TypeInfo(dword), sizeof(dword)); // *MUST* be #1
    RegClass(TManagedObject);  // *MUST* be #2
    RegTypeComplex(TypeInfo(TArrayOfDword), @RP_ArrayOfDword); // *MUST* be #3
    RegTypeDynArray1d(TypeInfo(T2DArrayOfDword), TypeInfo(TArrayOfDword)); // *MUST* be #4
    RegTypeComplex (TypeInfo(Chepersy_UnknownSet), @RP_UnknownSet); // *MUST* be #5
  {$ifdef fpc}
    _RegTypeBin(TypeInfo(QWORD), SizeOf(QWORD)); //*MUST* be #6
    RegTypeComplex(TypeInfo(TArrayOfQword), @RP_ArrayOfQword); // *MUST* be #7
  {$else}
   //Did I tell you how much I hate that dumb, obsolete Delphi?..
    if MotherState.VerboseLog then AddLog(RuEn(
      'Замечание: в Delphi нет типа QWORD, регистрируется подложный. ',
      'Note: Delphi doesn''t have a QWORD type, registering a fake.'));
    Move(TypeInfo(int64)^, FakeQwordInfo, Sizeof(TTypeInfo));
    FakeQwordInfo.Name:='QWORD';
    Move(TypeInfo(TArrayOfInt64)^, FakeArrayofQwordInfo, Sizeof(TTypeInfo));
    FakeArrayofQwordInfo.Name:='TARRAYOFQWORD';
    Move(TypeInfo(T2dArrayOfInt64)^, Fake2dArrayofQwordInfo, Sizeof(TTypeInfo));
    Fake2dArrayofQwordInfo.Name:='T2DARRAYOFQWORD';
    _RegTypeBin(PTypeInfo(@FakeQwordInfo), SizeOf(int64));
    RegTypeDynArray1d(PTypeInfo(@FakeArrayOfQwordInfo), PTypeInfo(@FakeQwordInfo));
  {$endif}
    RegTypeDynArray1d(TypeInfo(TArrayOfManagedObjects), TypeInfo(TManagedObject)); // *MUST* be #8
    RegTypeDynArray1d(TypeInfo(Chepersy_ArrayOfUnknownSet), TypeInfo(Chepersy_UnknownSet)); // *MUST* be #9
    RegTypeDynArray1d(TypeInfo(Chepersy_2dArrayOfUnknownSet), TypeInfo(Chepersy_ArrayOfUnknownSet)); // *MUST* be #10
    RegTypeDynArray1d(TypeInfo(T2dArrayOfManagedObjects), TypeInfo(TArrayOfManagedObjects)); // *MUST* be #11
  {$ifdef fpc}
    RegTypeDynArray1d(TypeInfo(T2DArrayofQword), TypeInfo(TArrayOfQword)); // *MUST* be  #12
  {$else}
    RegTypeDynArray1d(PTypeInfo(@Fake2DArrayOfQwordInfo), PTypeInfo(@FakeArrayOfQwordInfo));
  {$endif}
  {$ifdef fpc}
    RegType(TypeInfo(CpsUnknownRecord), SizeOf(CpsUnknownRecord), ['-stub', TypeInfo(dword)]);   //MUST be #13  //the record index MUST be 1
  {$else}
    RegType('*CpsUnknownRecord', SizeOf(CpsUnknownRecord), ['-stub', TypeInfo(dword)]);   //MUST be #13  //the record index MUST be 1
  {$endif}
    RegType(TypeInfo(CpsArrayOfUnknownRecord),
      {$ifdef fpc}TypeInfo(CpsUnknownRecord){$else}'*CpsUnknownRecord'{$endif}); //#14
    RegTypeDynArray1d(TypeInfo(Cps2dArrayOfUnknownRecord), TypeInfo(CpsArrayOfUnknownRecord)); //#15
    RegTheOnlyMetaclass; //#16

    _RegTypeBin(TypeInfo(integer), sizeof(integer));
    _RegTypeBin(TypeInfo(int64), SizeOf(int64));


    _RegTypeBin(TypeInfo(float), sizeof(float));//single, a.k.a glFloat.
    _RegTypeBin(TypeInfo(double), SizeOf(double));
    _RegTypeComplexWZK(TypeInfo(extended), @RP_Extended, 10, fk_float);
    
    _RegTypeBin(TypeInfo(comp), SizeOf(comp));

    _RegTypeComplexWZK(TypeInfo(byte), @RP_byte, 1, fk_integer);
    _RegTypeComplexWZK(TypeInfo(shortint), @RP_shortint, 1, fk_integer);
    _RegTypeComplexWZK(TypeInfo(word), @RP_word, 2, fk_integer);
    _RegTypeComplexWZK(TypeInfo(smallint), @RP_smallint, 2, fk_integer);

    {$ifdef fpc}
      _RegTypeBin(TypeInfo(boolean), SizeOf(boolean));
    {$else}
      RegTypeComplex(TypeInfo(boolean), @RP_LONGBOOL);
    {$endif}
    {$ifdef cge}
      _RegTypeBin(TypeInfo(fixed32), SizeOf(fixed32));
    {$endif}

    RegTypeComplex(TypeInfo(AnsiString), @RP_AnsiString);
    RegTypeComplex(TypeInfo(WideString), @RP_WideString);
    
    RegisterBuiltInConverters;

    RegTypeComplex(TypeInfo(TAOI), @RP_TAOI);
    RegTypeComplex(TypeInfo(T2DAOI), @RP_T2DAOI);
    RegTypeComplex(TypeInfo(TAOC), @RP_TAOC);
    RegTypeComplex(TypeInfo(T2DAOC), @RP_T2DAOC);
    RegTypeComplex(TypeInfo(TAOF), @RP_TAOF);
    RegTypeComplex(TypeInfo(T2DAOF), @RP_T2DAOF);
    RegTypeComplex(TypeInfo(TAOS), @RP_TAOS);
    RegTypeComplex(TypeInfo(T2DAOS), @RP_T2DAOS);
    RegTypeComplex(TypeInfo(TAOW), @RP_TAOW);
    RegTypeComplex(TypeInfo(T2DAOW), @RP_T2DAOW);
    
    RegTypeComplex(TypeInfo(TArrayOfDyna), @RP_TArrayOfDyna);
    RegTypeComplex(TypeInfo(TAOMO), @RP_TAOMO);
  end;
  
  procedure ClassesRegistrationEnd;
  var
    i: integer;
  begin
    SetLength(TypeToRecInd, Types.Length);
    FillChar(TypeToRecInd[0], Sizeof(TypeToRecInd[0]) * Types.Length, $ff);
    For i:=0 to Records.High do
      TypeToRecInd[Records[i]]:= i;
    SetLength(tScenarioIndex, Records.Length);
    SetLength(clScenarioIndex, Records.Length);
    SetLength(Scenario, 0);
    SetLength(CloningScenario, 0);
    For i:=0 to Records.High do begin
      CpsBuildSavingScenario(i);
      CpsBuildCloningScenario(i);
    end;

    SetLength(CpsDatabaseHeaders, 1);
    DH:= TChepersyDatabaseHeader.CreateDefaultInstance;
    CpsDatabaseHeaders[0]:= DH;


    if MotherState^.VerboseLog then AddLog(RuEn('Закончена регистрация классов','Classes registration end'));
    CpsFinalized:= true;
  end;
  

 var
   FakeTypeInfoForPointer: TTypeInfo = ( Kind: tkUnknown; Name: 'POINTER');
    type
      TArrayOf1000Pointers = packed Array[0..1000] of pointer;
      PArrayOf1000Pointers = ^TArrayOf1000Pointers;

    function FindOffset(K: TManagedObject; s: string): integer;
    var
      p: PArrayOf1000Pointers;
      p2: pointer;
      i: integer;
    begin
      p:=pointer(Cardinal(K.ClassType) );//- vmtSelfPtr);
      p2:=K.MethodAddress(s);
      For i:=0 to 1000 do
        if p^[i] = p2 then begin
          Result:=i * SizeOf(pointer);
          if MotherState^.VerboseLog then AddLog('  %0 : VMT + %1',[s, Result]);
          Exit;
        end;
      Die(RuEn('Не удалось разместить свои поля в VMT класса!','Unable to place the custom fields in the class VMT!'))
    end;

{$ifdef oldhacky}
    procedure InitializeVmtxxxConsts;
    var
      K: TManagedObject;
    begin
      K:=TManagedObject.Generate;
      vmtMyClassIndex:= FindOffset(K, '_MyClassIndex');
      vmtMyScenarioIndex:= FindOffset(K, '_MyScenarioIndex');
      vmtMyFieldsList:= FindOffset(K, '_MyFieldsList');
      K.TechnicalDestroy;
    end;
{$endif}


  function ClassIndex(C: TClass): integer;
  var i: integer;
  begin
    Result:= -1;
    For i:=0 to Records.High do
      if Types[Records[i]]._class = C then begin
        Result:=i;
        Break;
      end;
  end;

  procedure DieTypeFailed(T: TTypeRecord; reason: string);
  begin
    Die(MI_ERROR_PROGRAMMER_NO_BAKA,
      [RuEn('Тип "','Type "') + NameSpace[T.name] + RuEn('" рода "','" of kind "')
       + GetEnumName(TypeInfo(TTypeKind), ord(T.Info^.Kind))
       + RuEn('" не прошёл регистрацию','" failed its registration.') + #10#13 + reason]);
  end;


{   This bunny registers binary types (i.e. ones that could be
      dumped into a stream directly, without caring about their structure).
      ...I wish TypeInfo or TypeData structures had a size field
      so that pointing it manually would be unnecessary. Dreams, dreams... :(  }
  procedure _RegTypeBin(Info: PTypeInfo; Size: Longint);  //overload;
  var
    T: TTypeRecord;
    var tn: string;
  begin
    FillChar(T, Sizeof(T), 0);
    T.size:=Size;
    T.Info:=Info;
    T.Data:=GetTypeData(T.Info);

    tn:=UpperCase(AnsiString(Info^.Name));

    if TypeNameSpace.Ind(tn, 0) >=0 then begin
      //T.name:=NameSpace.Ind(Info^.Name); //needed for the error message
//      if VerboseLog then AddLog('HINT: Type "%0" is alraedy registered! (index %1)', [tn, NameSpace.Ind(tn, 0)]);
      EXIT;
    end;
    T.name:=NameSpace.Add(tn);
    TypeNameSpace.Add(tn);
    T.proc:=nil;

    if (Size mod 4) <> 0 then DieTypeFailed(T, RuEn('Размер не делится на 4.','Size not divisible by 4.'));
    
    case Info^.Kind of
      tkFloat: begin
          T.Kind:=fk_Float;
          //One of the reasons why float and integer aren't just marked
          //  as binary fields is a chance (like that of a snow flake
          //  in hell) that the support of 64-bit platforms will be added,
          //  where (maybe?) the glUint and glFloat will be not the
          //  DWord and Single, but QWord and Double, respectively,
          //  and we'll have to convert them "on the fly" while reading
          //  from the basket.
        end;
      tkInteger, tkInt64{$ifdef fpc}, tkQWord{$endif}: begin
          T.Kind:=fk_Integer;
        end;
    else
      T.Kind:=fk_Integer;
     // DieTypeFailed(T, RuEn('Вызван неподобающий вариант RegTypeXxx() ','Wrong version of RegTypeXxx() was called for this type.'))
    end;
    Types.Add(T);
    if MotherState^.VerboseLog then AddLog(RuEn('  Тип: %0 (%1 / %2)','  Type: %0 (%1 / %2)')
      ,[NameSpace[T.name], T.size, FieldKindToStr(T.Kind)]);
  end;


  {   This bunny registers array types.
      Since the lack of RTTI it goes via string IDs.}
  {DEPECATED} procedure ObsoleteRegTypeStaticArray(
    StringID: ansistring; BaseType: PTypeInfo; Size: Integer);
  //do not use. Left for backward compatibility with v0.6 and lower.
  var
    T: TTypeRecord;
    i: integer;

  begin
    FillChar(T, Sizeof(T), 0);
    StringID:=UpperCase(StringID);
    if TypeNameSpace.Ind(StringID, -1) >=0 then begin
      //T.name:=NameSpace.Ind(Info^.Name); //needed for the error message
//      if VerboseLog then AddLog('HINT: Type "%0" is alraedy registered! (index %1)', [stringId, NameSpace.Ind(Info^.Name, -1)]);
      EXIT;
    end;
    i:=TypeNameSpace.Ind(AnsiString(BaseType^.Name), -1);
    if i < 0 then
      DieTypeFailed(T, RuEn('Базовый тип ','The base type ')+BaseType^.Name+RuEn(' ещё не зарегистрирован.',' is not registered.'));

    T.BaseTypeInd:= i;
    T.size:=Size;
    if (Size mod Types[i].Size) <> 0 then
      DieTypeFailed(T, format(
        RuEn(
          'Несовпадение размера/выравнивания. Размер=%d, Размер элемента(%s)=%d',
          'Size/alignment mismatch. Size=%d, ElementSize(%s)=%d'),
        [Size, AnsiString(BaseType^.Name), Types[i].Size]));
    T.Length:= Size div Types[i].Size;
    T.Info:=New(PTypeInfo);
    T.Info^.Kind:=tkArray;
    T.Info^.Name:=StringID;
    T.StaticLow:= 0;//other low values not implemented yet
    T.Data:=nil;

    T.name:=NameSpace.Add(StringId);
    TypeNameSpace.Add(StringId);
    T.proc:=nil;
    T.Kind:=fk_binary;

    Types.Add(T);
    if MotherState^.VerboseLog then AddLog(RuEn('  Тип: %0 (%1 / %2)','  Type: %0 (%1 / %2)')
      ,[NameSpace[T.name], T.size, FieldKindToStr(T.Kind)]);
  end;
  
  {   This bunny registers array types.
      Since the lack of RTTI it goes via string IDs.
   0.9: tweaked to accept th High value instead of Size.
      Made unavailable to the general public.  }
  procedure TwkRegTypeStaticArray(StringID: ansistring; BaseTind: integer; BaseType: PTypeInfo; MeHigh: Integer);//Size: Integer);
  var
    T: TTypeRecord;
    i: integer;

  begin
    FillChar(T, Sizeof(T), 0);
    StringID:=UpperCase(StringID);
    if TypeNameSpace.Ind(StringID, -1) >=0 then begin
      //T.name:=NameSpace.Ind(Info^.Name); //needed for the error message
//      if VerboseLog then AddLog('HINT: Type "%0" is alraedy registered! (index %1)', [stringId, NameSpace.Ind(Info^.Name, -1)]);
      EXIT;
    end;
    if BaseTind < 0 then begin
      i:=TypeNameSpace.Ind(AnsiString(BaseType^.Name), -1);
      if i < 0 then
        DieTypeFailed(T, RuEn('Базовый тип ','The base type ')+BaseType^.Name+RuEn(' ещё не зарегистрирован.',' is not registered.'));
    end
    else
      i:= BaseTind;

    T.BaseTypeInd:= i;
    T.Info:=New(PTypeInfo);
    T.Info^.Kind:=tkArray;
    T.Info^.Name:=StringID;
    T.Data:=nil;
    T.size:=(MeHigh + 1) * Types[i].Size; //Size;
{    if (Size mod Types[i].Size) <> 0 then
      DieTypeFailed(T, format(
        RuEn(
          'Несовпадение размера/выравнивания. Размер=%d, Размер элемента(%s)=%d',
          'Size/alignment mismatch. Size=%d, ElementSize(%s)=%d'),
        [Size, AnsiString(BaseType^.Name), Types[i].Size]));
}
    T.Length:= MeHigh + 1;//Size div Types[i].Size;
    T.StaticLow:= 0;
    T.name:=NameSpace.Add(StringId);
    TypeNameSpace.Add(StringId);
    T.Kind:=fk_static_array;
    
    if BaseTind < 0 then begin
      case Types[T.BaseTypeInd].Kind of
        fk_integer, fk_float, fk_enum: begin
          if Assigned(Types[T.BaseTypeInd].proc) then T.proc:=@RP_GenericStaticArray
          else
            if (Types[T.BaseTypeInd].Size = 4)
            or (Types[T.BaseTypeInd].Size = 8)
              then T.Proc:=@RP_BinaryStaticArray
              else DieTypeFailed(T,
                 RuEn('Базовый тип ','The base type ')+BaseType^.Name
                +RuEn(' должен иметь размер 4 или 8 байт, или же иметь обрабатывающую процедуру.',
                      ' must either have size 4 or 8 bytes or have a processing procedure.'));
        end;
        fk_set, // ******** TODO: LIFT THIS LIMITATION

        fk_notsupported: //, fk_enumInd_Array, fk_record, fk_packed_record, fk_array:
          DieTypeFailed(T,
           RuEn('Недопустимый базовый тип ','Invalid base type ')
             +BaseType^.Name+' ('+FieldKindToStr(Types[T.BaseTypeInd].Kind)
          +RuEn(
           '). Статическе типы из подобных типов не поддерживаются.',
           '). Static arrays of such types are not supported.'));
      else
        if Assigned(Types[T.BaseTypeInd].proc) then T.proc:=@RP_GenericStaticArray
        else DieTypeFailed(T,
          RuEn(
            'Базовый тип ',
            'The base type ')
            +BaseType^.Name+ RuEn(
            ' не имеет обрабатывающей процедуры.',
            ' does not have a processing procedure.'));
      end;
    end
    else
      T.proc:=@RP_GenericStaticArray;
    

    Types.Add(T);
    if MotherState^.VerboseLog then AddLog(
       RuEn('  Тип: %0 (%1 / %2)','  Type: %0 (%1 / %2)')
      , [NameSpace[T.name]
      , T.size
      , FieldKindToStr(T.Kind)]);
  end;

{new in v 0.8. Registers n-dimensional dynamic arrys without the need
  to register the intermediary types. Uses fake intermediary types.)
}

  procedure RegTypeDynArray2d(ArrayInfo, BaseType: PTypeInfo);
  begin
    RegTypeDynArrayNd(2, ArrayInfo, BaseType);
  end;
  
  procedure RegTypeDynArray3d(ArrayInfo, BaseType: PTypeInfo);
  begin
    RegTypeDynArrayNd(3, ArrayInfo, BaseType);
  end;


  function _RegTypeArrayStart(var T: TTypeRecord; ArrayInfo, BaseType: PTypeInfo): string;
  var
    i: integer;
    StringID: ansistring;
  begin
    FillChar(T, Sizeof(T), 0);
    StringID:=UpperCase(ArrayInfo^.Name);
    if TypeNameSpace.Ind(StringID, 0) >=0 then begin
      Result:='';
      Exit;
    end;
//    TypeNameSpace.Add(StringId);
    Result:= StringID;
    T.Kind:=fk_dynamic_array;
    T.Info:=ArrayInfo;
    T.Data:=GetTypeData(ArrayInfo);
    T.name:=NameSpace.Add(StringId);

    i:= TypeNameSpace.Ind(AnsiString(BaseType^.Name), 0);
    if i < 0 then
      DieTypeFailed(T, RuEn('Базовый тип ','The base type ')+AnsiString(BaseType^.Name)
                        + RuEn(' ещё не зарегистрирован.'
                              ,' is not registered.'));
    T.BaseTypeInd:=i;
    T.size:=4;
  end;

  procedure _ChooseArrayType(var T: TTypeRecord; BaseType: PTypeInfo);
  begin
    T.Proc:=nil;
    if Types[T.BaseTypeInd].Kind = fk_dynamic_array
      then begin
        T.DynArrayLevel:=Types[T.BaseTypeInd].DynArrayLevel + 1;
        T.DynArrayBaseInd:=Types[T.BaseTypeInd].DynArrayBaseInd;
      end
      else begin
        T.DynArrayLevel:=1;
        T.DynArrayBaseInd:=T.BaseTypeInd;
      end;
    case Types[T.BaseTypeInd].Kind of
      fk_integer, fk_float: begin
        if Assigned(Types[T.BaseTypeInd].proc) then T.proc:=@RP_GenericDynArray
        else
          if Types[T.BaseTypeInd].Size = 4 then T.Proc:=@RP_ArrayOfDWORD
            else if Types[T.BaseTypeInd].Size = 8 then T.Proc:=@RP_ArrayOfQWORD
              else DieTypeFailed(T,
                 RuEn('Базовый тип ','The base type ')+BaseType^.Name
                +RuEn(' должен иметь размер 4 или 8 байт, или же иметь обрабатывающую процедуру.',
                      ' must either have size 4 or 8 bytes or have a processing procedure.'));
      end;
      fk_enum: T.Proc:=@RP_ArrayOfEnum;
      fk_notsupported:
         DieTypeFailed(T,
           RuEn('Недопустимый базовый тип ','Invalid base type ')
           +BaseType^.Name+' ('+FieldKindToStr(Types[T.BaseTypeInd].Kind)
         + RuEn('). Динамические массивы из подобных типов не поддерживаются.'
               ,'). Dynamic arrays of such types are not supported.'));
    else
      if Assigned(Types[T.BaseTypeInd].proc) then T.proc:=@RP_GenericDynArray
      else DieTypeFailed(T,
        RuEn('Базовый тип "','The base type "')
        +BaseType^.Name
        +RuEn('" не имеет обрабатывающей процедуры.', '" does not have a processing procedure.'));
    end;
  end;


  procedure RegTypeDynArrayNd(Dimensions: integer; ArrayInfo, BaseType: PTypeInfo);
  var
    T, FT: TTypeRecord;
    j, i, ii: integer;
    FakeInfo: PTypeInfo;
    nm, newname: string;
  begin
    nm:= _RegTypeArrayStart(T, ArrayInfo, BaseType);
    if nm='' then Exit;
    FT:=T;
    FakeInfo:=baseType;
    For j:=1 to Dimensions - 1 do begin
      //adding the fake types...
      newname:=NameSpace[T.name] + '@' + IntToStr(j);
      FT.name:=NameSpace.Add(newname);
      if j > 1 then FT.BaseTypeInd:= i;
      _ChooseArrayType(FT, FakeInfo);
      New(FakeInfo);
      FakeInfo^.Name:=newname;
      FT.Info:=FakeInfo;
      FT.Data:=New(PTypeData);
//      FT.name:=NameSpace.Add(newname);
      ii:=TypeNameSpace.Add(newname);
      i:= Types.Add(FT);
      if MotherState^.VerboseLog then AddLog(RuEn('  Подложный тип:','  Fake type:') + ' %0 = array of %1' ,[NameSpace[FT.name], NameSpace[Types[FT.BaseTypeInd].Name]]);
//addlog('-%0 %1 %2',[i, ii,  TypeNameSpace[ii]]);
    end;
    T.BaseTypeInd:=i;
    _ChooseArrayType(T, FakeInfo);
    Types.Add(T);
    TypeNameSpace.Add(nm);
    if MotherState^.VerboseLog then AddLog(RuEn('  Тип:','  Type:') +' %0 = array of %1' ,[NameSpace[T.name], NameSpace[Types[T.BaseTypeInd].Name]]);
  end;

{   (nev in v0.7 This bunny registers dynamic arrays of any type.
}
  procedure RegTypeDynArray1d(ArrayInfo, BaseType: PTypeInfo);
  var
    T: TTypeRecord;
    i: integer;
    StringID: ansistring;
  begin
    StringID:= _RegTypeArrayStart(T, ArrayInfo, BaseType);
    if StringID = '' then Exit;
    _ChooseArrayType(T, BaseType);
    Types.Add(T);
    TypeNameSpace.Add(StringID);
    if MotherState^.VerboseLog then AddLog(RuEn('  Тип:','  Type:') +' %0 = array of %1' ,[NameSpace[T.name], NameSpace[Types[T.BaseTypeInd].Name]]);
  end;
  
  
{   (nev in v0.7 This bunny registers dynamic arrays of any type.
}
  procedure RegTypeSet(Info, BaseType: PTypeInfo);
  var
    T: TTypeRecord;
    i: integer;
    StringID: ansistring;
  begin
    FillChar(T, Sizeof(T), 0);
    StringID:=UpperCase(Info^.Name);
    if TypeNameSpace.Ind(StringID, 0) >=0 then begin
      //T.name:=NameSpace.Ind(Info^.Name); //needed for the error message
//      if VerboseLog then AddLog('HINT: Type "%0" is alraedy registered! (index %1)', [stringId, NameSpace.Ind(Info^.Name, 0)]);
      EXIT;
    end;
    i:=TypeNameSpace.Ind(AnsiString(BaseType^.Name), 0);
    if i < 0 then
      DieTypeFailed(T, 'The base type '+BaseType^.Name+' is not registered.');
    if Types[i].Kind <> fk_enum then
      DieTypeFailed(T, 'The base type '+BaseType^.Name+' is not an enumerated type.');

    T.BaseTypeInd:=i;
    T.Info:=Info;
    T.Data:=GetTypeData(Info);
    T.name:=NameSpace.Add(StringId);
    TypeNameSpace.Add(StringId);
    T.SetLen:=Enums[i].Length;
    T.Kind:=fk_set;
    T.proc:=@RP_Set;
   {$ifdef fpc}
     //In FreePascal sets are either 4 or 32 bytes in size
    if T.SetLen > 32 then T.Size:=32 else T.Size:=4;
   {$else}
     //In Delphi they take no more bytes than necessary
    T.Size:=1 + (T.SetLen - 1) div 8;
   {$endif}
    Types.Add(T);
    if MotherState^.VerboseLog then AddLog(RuEn('  Тип: %0 (%1)','  Type: %0 (%1)')
      ,[NameSpace[T.name], FieldKindToStr(T.Kind)]);
  end;


  function VarIsStr(V: TVarRec): boolean;
  begin
    Result:= V.Vtype in [vtAnsiString, vtChar];
  end;
  
  function VarToStr(V:TVarRec): string;
  begin
    if V.Vtype=vtAnsiString
      then Result:=AnsiString(V.VAnsiString)
      else Result:=V.VChar;
  end;
  
  procedure RegTypeEnum(Info: PTypeInfo);  //overload;
  var
    T: TTypeRecord;
  begin
    FillChar(T, Sizeof(T), 0);
    T.size:=4;
    {$ifndef fpc}
    if not Assigned(Info) then begin
      T.Info:=@FakeTypeInfoForPointer;
      T.Data:=nil;
    end
    else
    {$endif}
    begin
      T.Info:=Info;
      T.Data:=GetTypeData(T.Info);
    end;
    T.Proc:=nil;
    if TypeNameSpace.Ind(String(T.Info^.Name), 0) >=0 then begin
      //T.name:=NameSpace.Ind(Info^.Name); //needed for the error message
//      if VerboseLog then AddLog('HINT: Type "%0" is alraedy registered! (index %1)', [string(Info^.Name), NameSpace.Ind(Info^.Name, 0)]);
      EXIT;
    end;
    T.name:=NameSpace.Add(AnsiString(T.Info^.Name));
    TypeNameSpace.Add(AnsiString(T.Info^.Name));

    case T.Info^.Kind of
      tkFloat: begin
        if T.Data^.FloatType = ftSingle then begin
          T.Kind:=fk_Float;
        end
        else
          DieTypeFailed(T, RuEn('Вызван неподобающий вариант RegTypeXxx()','Wrong version of RegTypeXxx() was called for this type.'));
      end;
      tkEnumeration, tkInteger: begin
        T.Kind:=fk_Enum;
        T.Proc:=@RP_Enum;
        Enums.Length:=Types.Length + 1;
        _RegEnum(T);
      end;
    else
      if (UpperCase(T.Info^.Name) = 'POINTER') and (Types.Length = 0)
        then T.Kind:=fk_notsupported
        else DieTypeFailed(T, RuEn('Вызван неподобающий вариант RegTypeXxx()','Wrong version of RegTypeXxx() was called for this type.'));
    end;
    Types.Add(T);
    if MotherState^.VerboseLog then AddLog(RuEn('  Тип: %0 (%1 / %2)','  Type: %0 (%1 / %2)')
      ,[NameSpace[T.name], T.size, FieldKindToStr(T.Kind)]);
  end;


{ For internal use only}
  procedure _RegTypeComplexWZK(Info: PTypeInfo; Proc: TCustomTypeProcessingProc;
    S: integer; K: TFieldKind);
  var
    T: TTypeRecord;
  begin
    FillChar(T, Sizeof(T), 0);
    T.size:=S;
    T.Info:=Info;
    T.Data:=GetTypeData(T.Info);
    if TypeNameSpace.Ind(Info^.Name, 0) >=0 then begin
      //T.name:=NameSpace.Ind(Info^.Name); //needed for the error message
//      if VerboseLog then AddLog('HINT: Type "%0" is alraedy registered! (index %1)', [string(Info^.Name), NameSpace.Ind(Info^.Name, 0)]);
      EXIT;
    end;
    T.name:=NameSpace.Add(AnsiString(Info^.Name));
    TypeNameSpace.Add(AnsiString(Info^.Name));
    T.proc:=Proc;
    T.Kind:=K;
    if (K = fk_notsupported) then
      DieTypeFailed(T, RuEn('Вызван неподобающий вариант RegTypeXxx()','Wrong version of RegTypeXxx() was called for this type.'));
    Types.Add(T);
    if MotherState^.VerboseLog then AddLog(RuEn('  Тип: %0 (%3 / %1 / %2)','  Type: %0 (%3 / %1 / %2)')
      ,[NameSpace[T.name], FieldKindToStr(T.Kind), @T.Proc, S]);
  end;

  procedure RegTypeComplex(Info: PTypeInfo; Proc: TCustomTypeProcessingProc);
  var
    K: TFieldKind;
  begin
   case Info^.Kind of
      TkClass: begin
        //FUCK. I have no other words to describe it.
        //Why does the fucking InheritsFrom() ALWAYS RETURN FALSE?!!!
        K:=fk_CustomClass;
      end;
      WstringType:
        K:=fk_String;
      {$ifdef fpc}tkAString{$else}tkLstring{$endif}:
        K:=fk_8bit_string;
//0.8.98 (wtf?)  tkArray, tkRecord, tkInteger, tkDynArray, tkSet:     K:=fk_Complex;
      tkRecord:
        K:= fk_record;
      tkInteger:
        K:=fk_integer;
      tkArray:
        K:=fk_array;
      tkDynArray:
        K:=fk_dynamic_array;
      tkSet:
        K:=fk_Set;
      tkFloat:
        K:=fk_float;
    else
      if UpperCase(Info^.Name) = 'LONGBOOL'
        then K:=fk_integer
      else
        K:=fk_notsupported;
    end;
    _RegTypeComplexWZK(Info, Proc, 4, K);
  end;


procedure RegTypeClass(C: CManagedObject);
var
  T: TTypeRecord;
begin
  FillChar(T, Sizeof(T), 0);
  T.size:=Sizeof(pointer);
  New(T.Info);
  T.Info^.Name:=C.ClassName;
  T.Info^.Kind:=tkClass;
  T.Data:=GetTypeData(T.Info);
  if TypeNameSpace.Ind(T.Info^.Name, 0) >=0 then begin
    //T.name:=NameSpace.Ind(Info^.Name); //needed for the error message
//    if VerboseLog then AddLog('HINT: Type "%0" is alraedy registered! (index %1)', [string(T.Info^.Name), NameSpace.Ind(T.Info^.Name, 0)]);
    EXIT;
  end;
  T.name:=NameSpace.Add(AnsiString(T.Info^.Name));
  TypeNameSpace.Add(AnsiString(T.Info^.Name));
  T.proc:=@RP_Persistent;
  T.Kind:=fk_Class;
  T._class:=C;
  T.ClassName:=ClassNameSpace.Add(AnsiString(C.ClassName));
  Types.Add(T);
  if MotherState^.VerboseLog then AddLog(RuEn('  Тип: %0 (%1 / %2 /%3)','  Type: %0 (%1 / %2 / %3)')
    ,[NameSpace[T.name], T.size, FieldKindToStr(T.Kind), @T.Proc]);
end;



(* Removed in 0.8.96
procedure RegCustomClass(C: TClass; proc: TCustomClassProc);
var
  T: TTypeRecord;
begin
  CpsValidate(C.ClassName);
  if GetClassIndex(C) > 0 then Exit; // registered already
{ bitch >_<#
  if C is TManagedObject then Die(RuEn(
    'Класс %0 нельзя зарегистрировать как класс пользователя:'#10#13'  это потомок TManagedObject',
    'Class %0 cannot be registered as a custom class:'#10#13'  it''s a TManagedObject descendant'),
    [string(C.ClassName)]);
}
  if TypeNameSpace.Ind(C.ClassName, 0) > 0 then Exit;
  FillChar(T, Sizeof(T), 0);
  T.size:=Sizeof(pointer);
  T.name:=NameSpace.Add(C.ClassName);
  Records.Add(TypeNameSpace.Add(C.ClassName));
  SetClassIndex(C, Records.High);
//writeln(' ---- records.high = ',records.high);
//writeln(' ---- records.last = ',records.last);
  T.proc:=RP_CustomClass;
  T.CustomClassProc:= proc;
  T.Kind:=fk_09_custom_class;
  T.ClassName:=ClassNameSpace.Add(AnsiString(C.ClassName));
  ClassAncestors.Length:= Records.Length;
  Fields.Increment;
  Types.Add(T);
//writeln(' ---- types.high = ',types.high);
  if MotherState.VerboseLog then AddLog(RuEn('  Тип: %0 (%1 / %2 /%3)','  Type: %0 (%1 / %2 / %3)')
    ,[NameSpace[T.name], T.size, FieldKindToStr(T.Kind), @T.CustomClassProc]);
end;
*)

var
  FieldsRegistrationInProgress: boolean = No;

procedure ResetPrevRec;
var
  pi: PTypeInfo;
begin
  {$ifdef fpc}
  prevOffset:=@(CurrentObject.Last.CpsIndex) - pointer(CurrentObject.Last);
  {$else}
  prevOffset:=cardinal(@(CurrentObject.Last.CpsIndex)) - cardinal(CurrentObject.Last);
  {$endif}
  prevSize:=SizeOf(CurrentObject.Last.CpsIndex);
  prevName:=NameSpace.Add('CpsIndex');
  pi:=TypeInfo(longint);
  prevType:=NameSpace.Add(pi^.Name);
end;

function IsCTPDescendant(C: TClass): boolean;
begin
  Result:= false;
  while C <> TManagedObject do begin
    if C = TObject then Exit;
    C:= C.ClassParent();
  end;
  result:= true;
end;


procedure BuildAncestorsList(C: TClass; N: integer);
var
  A: TClass;
  i: integer;
begin
  A:=C.ClassParent();
  while Assigned(A) and (A <> TManagedObject) and (A <> TObject) do begin
    {$ifdef delphiworkaround}
      i:=GetClassIndex(A);
    {$else}
      i:=CManagedObject(A).ClassIndex;
    {$endif}
    if not IsRegistered(CManagedObject(A)) then begin
      if MotherState^.VerboseLog then
        AddLog('Hint: ' + RuEn(
          '%1, предок класса %0, ещё не зарегистрирован. Выполняется автоматом.',
          'Class'' %0 ancestor, %1, is not registered yet. Performing auto-registration.')
         , [string(C.ClassName()), string(A.ClassName())]);
      RegClass(CManagedObject(A));
      {$ifdef delphiworkaround}
        i:=GetClassIndex(A);
      {$else}
        i:=CManagedObject(A).ClassIndex;
      {$endif}
    end;
    ClassAncestors[N].Add(Types[Records[i]].Name);//name
    A:=A.ClassParent();
  end;
end;

function GetAlignedNextOffset(A: cardinal): cardinal;
begin
  Result:=(((A - 1) div AlignGranularity) + 1) * AlignGranularity;
end;

procedure RegBegin(C: TObject);
begin
  if MotherState^.VerboseLog then AddLog(RuEn('Начало регистрации класса "%0".','Class registration start: %0'),[AnsiString(C.ClassName)]);
  if FieldsRegistrationInProgress then Die(MI_ERROR_PROGRAMMER_NO_BAKA, [RuEn(
    'Классы в методе RegisterFields() должны идти *ДО* любых полей!',
    'Classes in RegisterFields() should be registered BEFORE any fields!')]);
  CurrentObject.Add (C as TManagedObject);
  Records.Add(TypeNameSpace.Ind(C.ClassName, 0));
  ClassAncestors.Length:=Records.Length;
  Fields.Increment;
  SaveableFields.Increment;
  CurrentObjectInd.Add(Fields.High);
  {$ifdef delphiworkaround}
    SetClassIndex(CurrentObject.Last.ClassType(), Records.High);
  {$else}
    CurrentObject.Last._RegisterMyIndex(Records.High);
  {$endif}
  ResetPrevRec;
  BuildAncestorsList(C.ClassType, Records.High)
end;


procedure RegEnd;
var i: integer;
begin
 //Проверка, нет ли "хвоста" за последним полем
  if Fields[CurrentObjectInd.Last].Length > 0 then //aha! there it was, the AV snake nest!
    with Fields[CurrentObjectInd.Last].Last do
      if CurrentObject.Last.InstanceSize <> GetAlignedNextOffset(offset + Types[tind].Size)
        then Die(MI_ERROR_PROGRAMMER_NO_BAKA,
          [PervertedFormat(RuEn(
            'В хвосте класса %0 дырка %4 байт'#10#13'  (размер %3, посл. поле %1:%2)',
            'Class %0 has a %4-byte hole in its tail'#10#13'  (InstanceSize %3, last field %1:%2)'),
            [string(CurrentObject.Last.ClassName),
             NameSpace[name],
             NameSpace[Types[tind].Name],
             CurrentObject.Last.InstanceSize,
             CurrentObject.Last.InstanceSize - GetAlignedNextOffset(offset + Types[tind].Size)])]);
 {$ifdef oldhacky}
  CurrentObject.Last._SetMyFieldsList(Fields[CurrentObjectInd.Last]);
  if VerboseLog then AddLog(RuEn('Завершена регистрация класса "%0" (индекс %1).','Class registration end: %0 (ind %1)'),
    [AnsiString(CurrentObject.Last.ClassName), CurrentObject.Last.ClassIndex]);
 {$else}
  {$ifdef fpc}
   CurrentObject.Last._SetMyFieldsList(Fields[CurrentObjectInd.Last]);
  {$else}
    //F***ng Delphi! can it do *anything* right?!..
   DumbDelphiSetMyFieldsList(CurrentObject.Last.ClassType(), Fields[CurrentObjectInd.Last]);
  {$endif}
  if MotherState^.VerboseLog then begin
    AddLog(RuEn('Завершена регистрация класса "%0" (индекс %1, хеш %2).','Class registration end: %0 (ind %1, hash %2)'),
    [AnsiString(CurrentObject.Last.ClassName),
     {$ifdef delphiworkaround}
      GetClassIndex(CurrentObject.Last.ClassType())
     {$else}
      CurrentObject.Last.ClassIndex
     {$endif}
      , CHash(CurrentObject.Last.ClassType())]);
    AddLog(RuEn('  Список полей:','  Fields list:'));
      for i:=0 to Fields[CurrentObject.Last.ClassIndex].High do
        with Fields[CurrentObject.Last.ClassIndex][i] do
          if Skip then
            AddLog(RuEn('    поле','    field') +' "%0:%3", %1 / %2, '+RuEn('опускаемое.','skipped.'),
              [NameSpace[name],offset,Types[Tind].size, NameSpace[Types[tind].Name]])
          else
            AddLog(RuEn('    поле','    field') +' "%0:%3", %1 / %2.',
              [NameSpace[name],offset,Types[Tind].size, NameSpace[Types[Tind].Name]]);
  end;
 {$endif}
  CurrentObject.Decrement;
  CurrentObjectInd.Decrement;
  FieldsRegistrationInProgress:=No;

  if CurrentObject.Length > 0 then ResetPrevRec; //added support for nested registration.
end;


procedure RegClass(C: CManagedObject);
var
  R: TManagedObject;
begin
  CpsValidate(C.ClassName);

  if IsRegistered(C) and (Records.Length > 0)
 (*
  {$ifdef fpc}
  if ClassIndex(C.ClassType) >= 0
  //E2076 This form of method call only allowed for class methods
  // Dumb Delphi :(  
  {$else}
  if TypeNameSpace.Ind(C.ClassName, -1) >=0
  {$endif}
*)
  then begin
    if MotherState^.VerboseLog then
      AddLog(RuEn(
        'Hint: Класс %0 уже зарегистрирован (№%1).',
        'Hint: Class %0 is alraedy registered (#%1).'), [string(C.ClassName)
      , {$ifdef delphiworkaround}GetClassIndex(C){$else}C.ClassIndex{$endif}]);
    Exit;
  end;
  pointer(R):= nil;
  try
    RegTypeClass(C);
    R:=C.Register();   //constructor call
  except
    Die(RuEn('Провалена регистрация класса %0',
             'Failed to register class %0'),[string(C.ClassName)])
  end;
  if Assigned(R) then R.TechnicalDestroy;
end;

(*
procedure __RegisterField(name: string; Pf: pointer; tnu: integer; SkipIt, IsAligned: boolean);
var
  i, j, a, b: integer;
  f: TFieldInfo;
  PI: PTypeInfo;
begin
  FieldsRegistrationInProgress:=Yes;
  a:=NameSpace.ind(TypeNameSpace[tnu], 0);
  PI:=Types[tnu].Info;
  For i:=0 to Types.High do
    if a = Types[i].Name then begin
      b:=i;
      Break;
    end;
  f.Tind:=b;
  f.name:=NameSpace.Add(name);
  {$ifdef fpc}
  f.offset:=Pf - pointer(CurrentObject.Last);
  {$else}
  f.offset:=cardinal(Pf) - cardinal(CurrentObject.Last);
  {$endif}
  if Assigned(Types[f.Tind].Proc) 
    then f.haspp:=1 
    else f.haspp:=0;
  //duplicates check

  for j:=0 to Fields[CurrentObjectInd.Last].High do
    if f.name = Fields[CurrentObjectInd.Last][j].name
      then Die(MI_ERROR_PROGRAMMER_NO_BAKA,
      [RuEn('Класс ','Class ')+CurrentObject.Last.ClassName + RuEn(' не прошёл регистрацию.',' failed its registration.')+#10#13
      + RuEn('  Попытка зарегистрировать ещё одно поле с именем "','  Attempt to register field named "')
      + NameSpace[f.name] + RuEn('"!','" twice!')]);

  if (IsAligned and (f.offset <> GetAlignedNextOffset(prevOffset + prevSize)))
    then Die (MI_ERROR_PROGRAMMER_NO_BAKA,
      [RuEn('Класс ','Class ')+CurrentObject.Last.ClassName 
     + RuEn(' не прошёл регистрацию.',' failed its registration.')+#10#13
     + '  "' + NameSpace[prevName] + ':' + NameSpace[prevType] 
     + RuEn('", смещение=','", offset=') + IntToStr(prevOffset)
     + RuEn(', размер=',', size=') + IntToStr(prevSize) + #10#13
     + '  "' + NameSpace[f.name] +':' + Types[f.Tind].Info^.Name 
     + RuEn('", смещение=','", offset=') + IntToStr(f.offset)
     + RuEn(', размер=',', size=') + IntToStr(Types[f.Tind].size)+ #10#13
     + RuEn('  Выравнивание=','  Alignment=') + IntToStr(AlignGranularity) +#10#13
     + RuEn('  Размер объекта=','  InstanceSize=') + IntToStr(CurrentObject.Last.InstanceSize) +#10#13
     + RuEn('  Нестыковка=','  Gap=') + IntToStr(f.offset - (GetAlignedNextOffset(prevOffset + prevSize)))
      ]);
  if (not IsAligned and (f.offset <> prevOffset + prevSize))
    then Die (MI_ERROR_PROGRAMMER_NO_BAKA,
      [RuEn('Класс ','Class ')+CurrentObject.Last.ClassName 
     + RuEn(' не прошёл регистрацию.',' failed its registration.')+#10#13
     + '  "' + NameSpace[prevName] + ':' + NameSpace[prevType] 
     + RuEn('", смещение=','", offset=') + IntToStr(prevOffset)
     + RuEn(', размер=',', size=') + IntToStr(prevSize) + #10#13
     + '  "' + NameSpace[f.name] +':' + Types[f.Tind].Info^.Name
     + RuEn('", смещение=','", offset=') + IntToStr(f.offset)
     + RuEn(', размер=',', size=') + IntToStr(Types[f.Tind].size) + #10#13
     + RuEn('  (без выравнивания)','  (non-aligned)') +#10#13
     + RuEn('  Размер объекта=','  InstanceSize=') + IntToStr(CurrentObject.Last.InstanceSize) +#10#13
     + RuEn('  Нестыковка=','  Gap=') + IntToStr(f.offset - prevOffset + prevSize)
      ]);

  prevOffset:=f.offset;
  prevName:=f.name;
  prevSize:=Types[f.Tind].size;
  prevType:=NameSpace.Ind(Types[f.Tind].Info^.Name, 0);
  if (Types[f.Tind].Info^.Kind = tkClass) then begin
    if Types[f.Tind].Kind = fk_Class //._class.InheritsFrom(TManagedObject)
     // or Types[f.Tind]._class.InheritsFrom(TDyna)
      then SaveableFields[CurrentObjectInd.Last].Add(f);
    if SkipIt then Die (MI_ERROR_PROGRAMMER_NO_BAKA,
         [RuEn('Класс ','Class ')+CurrentObject.Last.ClassName
           + RuEn(' не прошёл регистрацию.',' failed its registration.')+#10#13
           + RuEn('  Попытка сделать поле "','  Attempt to skip the field "')
           + NameSpace[prevName]
           + RuEn('" опускаемым.','".') +#10#13
           + RuEn('  Сохранябельные объектные поля не могут быть опускаемыми!'
                 ,'  Saveable object fields cannot be skipped!')]);
    f.Skip:=No;
    Fields[CurrentObjectInd.Last].Add(f);
    if MotherState.VerboseLog then AddLog(RuEn('  поле','  field')+' "%0": %1 / %2.',
      [NameSpace[f.name],f.offset, NameSpace[Types[f.Tind].Name]]);
  end
  else begin
    if (f.Tind = 0) and not SkipIt
      then Die (MI_ERROR_PROGRAMMER_NO_BAKA,
        [RuEn('Класс ','Class ')+CurrentObject.Last.ClassName
        + RuEn(' не прошёл регистрацию.',' failed its registration.')+#10#13
        + RuEn('  Поле "','  Field "')
        + NameSpace[prevName]
        + RuEn('" это указатель.','" is a pointer.') + #10#13
        + RuEn(
          '  Указатели и незарегистрированные классы следует опускать посредством',
          '  Pointers and non-registered classes must be skipped via')
        +' RegSkipPtr()']);
    f.skip:=SkipIt;
    Fields[CurrentObjectInd.Last].Add(f);
    if MotherState.VerboseLog then
      if SkipIt then
        AddLog(RuEn('  поле','  field') +' "%0:%3", %1 / %2, '+RuEn('опускаемое.','skipped.'),
          [NameSpace[f.name],f.offset,Types[f.Tind].size, NameSpace[Types[f.Tind].Name]])
      else
        AddLog(RuEn('  поле','  field') +' "%0:%3", %1 / %2.',
          [NameSpace[f.name],f.offset,Types[f.Tind].size, NameSpace[Types[f.Tind].Name]]);
  end;
end;
*)

(*
procedure _RegisterField(name: string; Pf: pointer; PI: PTypeInfo; SkipIt: boolean);
var
  t, i, j, k, o: integer;
begin
 t:=TypeNameSpace.Ind(PI^.Name, 0);
  if t < 0 then Die(MI_ERROR_PROGRAMMER_NO_BAKA,
     [RuEn('Неизвестный тип "','Unknown type "')
      + UpperCase(PI^.Name)
      +RuEn('", отсутствует в реестре!','", not present in the registry!"')]);
  case Types[t].Kind of
    fk_enumind_array: begin
        j:=Types[t].BaseTypeInd;
        k:=Types[t].IndEnumInd;
        o:=0;
        For i:=0 to Enums[k].High do
          __RegisterField(name + '[' + NameSpace[Enums[k][i]] + ']',
            pointer(cardinal(Pf) + i * Types[j].size), j, SkipIt, i=0);
      end;
    fk_record: begin
        k:=Types[t].IndEnumInd;
        o:=0;
        For i:=0 to Length(Types[t].RecFieldName) -1 do begin
          j:=Types[t].RecFieldTind[i];
          __RegisterField(name + '.' + NameSpace[Types[t].RecFieldName[i]],
            pointer(cardinal(Pf) + o), j, SkipIt, Yes);
          o:=GetAlignedNextOffset(o + Types[j].size);
          //if i = 0 then o:= NFA_A(o, 4, Types[j].size)
          //         else o:= NFA_A(o, Types[Types[t].RecFieldTind[i - 1]].size, Types[j].size)
        end;
      end;
    fk_packed_record: begin
        k:=Types[t].IndEnumInd;
        o:=0;
        For i:=0 to Length(Types[t].RecFieldName) -1 do begin
          j:=Types[t].RecFieldTind[i];
          __RegisterField(name + '.' + NameSpace[Types[t].RecFieldName[i]],
            pointer(cardinal(Pf) + o), j, SkipIt, (i = 0));
          {$ifdef fpc}
          o+= Types[j].size;
          {$else}
          o:=o + Types[j].size; //Delphi suxxxxx!
          {$endif}
          //if i = 0 then o:= NFA_A(o, 4, Types[j].size)
          //         else o:= NFA_A(o, Types[Types[t].RecFieldTind[i - 1]].size, Types[j].size)
        end;
      end
  else
    __RegisterField(name, Pf, t, SkipIt, Yes);
  end;

end;
*)

procedure _RegEnum(T: TTypeRecord);
var
  i, m: integer;
begin
  if T.Data^.MinValue <> 0 then DieTypeFailed(T, 'MinValue <> 0. ' + RuEn(
    'Не забывайте: механизм хранения перечислимых типов не расчитан на ПОДДИАПАЗОННЫЕ типы.',
    'Please note that the mechanism for storing enumerated types was NOT meant for the SUBRANGE types.'));
  m:=T.Data^.MaxValue;
  if m > 255 then DieTypeFailed(T, 'MaxValue > 255. ' + RuEn(
    'Не забывайте: механизм хранения перечислимых типов не расчитан на ПОДДИАПАЗОННЫЕ типы.',
    'Please note that the mechanism for storing enumerated types was NOT meant for the SUBRANGE types.'));
  Enums.Last.Length:=m + 1;
  For i:=0 to m do
begin
    if T.Info^.Kind = tkEnumeration
      then Enums.Last[i]:=NameSpace.Add(GetEnumName(T.Info, i))
      else Enums.Last[i]:=NameSpace.Add(IntToStr(i));
end;
end;

(*procedure RegField(name: string; Pf: pointer; TypeString: string); overload;
var i: integer;
begin
  i:=TypeNameSpace.Ind(UpperCase(TypeString), 0);
  if i < 0 then Die(MI_ERROR_PROGRAMMER_NO_BAKA,
     [RuEn('Неизвестный тип "','Unknown type "')
      + UpperCase(TypeString)
      +RuEn('", отсутствует в реестре!','", not present in the registry!"')]);

  _RegisterField(name, pf, Types[i].Info, No);
end;

procedure RegField(name: string; Pf: pointer; PI: PTypeInfo); overload;
begin
  _RegisterField(name, pf, PI, No);
end;

procedure RegMultipleFields(v: array of const);
var
  c: integer;
  vN: string;
  vP, vT: pointer;
  vS: boolean;
begin
  vS:= No;
  vT:= TypeInfo(integer);
  c:=0;
  While c < Length(v) do begin
    if not VarIsStr(v[c]) then Die(MI_ERROR_PROGRAMMER_NO_BAKA,
      ['RegMultipleFields(), v['+ IntToStr(c)+']. '+RuEn('Ожидалось имя поля (строка)','Expected field name (string)')]);
    vN:=VarToStr(v[c]);
    if c + 1 >= Length(v) then Die(MI_ERROR_PROGRAMMER_NO_BAKA,
        ['RegMultipleFields(), field "'+ vN + '". ' + RuEn(
          'Неожиданный конец массива параметров. Ожидался адрес поля (указатель)',
          'Parameter array is too short. Expected field address (pointer)')]);
    if v[c + 1].Vtype <> vtPointer then Die(MI_ERROR_PROGRAMMER_NO_BAKA,
        ['RegMultipleFields(), field "'+ vN + '", v['+ IntToStr(c + 1)+
         ']. '+RuEn('Ожидался адрес поля (указатель)','Expected field address (pointer)')]);
    vP:=v[c + 1].VPointer;
    inc(c, 2);
    if (c < Length(v)) and (v[c].Vtype = vtPointer) then begin
      vT:= v[c].VPointer;
      inc(c);
    end;
    if (c < Length(v)) and (v[c].Vtype = vtBoolean) then begin
      vS:= v[c].VBoolean;
      inc(c);
    end;
    _RegisterField(vN, vP, vT, vS);
  end;
end;


procedure RegSkip(name: string; Pf: pointer; TypeString: string); overload;
var i: integer;
begin
  i:=TypeNameSpace.Ind(UpperCase(TypeString), 0);
  if i < 0 then Die(MI_ERROR_PROGRAMMER_NO_BAKA,
     [RuEn('Неизвестный тип "','Unknown type "')
      + UpperCase(TypeString)
      +RuEn('", отсутствует в реестре!','", not present in the registry!"')]);

  __RegisterField(name, pf, i, Yes, Yes);
end;

procedure RegSkip(name: string; Pf: pointer; PI: PTypeInfo); overload;
begin
  _RegisterField(name, pf, PI, Yes);
end;

procedure RegSkipPtr(name: string; Pf: pointer);
begin
  FieldsRegistrationInProgress:=Yes;
  _RegisterField(name, pf,
    {$ifdef fpc}
    TypeInfo(pointer)
    {$else}
    @FakeTypeInfoForPointer
    {$endif}
    , Yes);
end;
*)
{$include mo_cps_reg.inc}
