public static void NewLoadFromArea(StringReader sr, AreaData area)
        {
            if (area == null)
            {
                throw new Exception($"Load_resets: no #AREA seen yet.");
            }

            while (true)
            {
                var letter = sr.ReadLetter();
                if (letter != '#')
                {
                    throw new Exception("Load_rooms: # not found.");
                }

                var vnum = sr.ReadVnum();
                if (vnum == Vnum.None)
                {
                    break;
                }

                RoomDef existing;
                if (TryGetRoomDef(vnum, out existing))
                {
                    throw new Exception($"Load_rooms: vnum {vnum} duplicated.");
                }

                var rd = new RoomDef
                {
                    Area = area,
                    Vnum = vnum
                };

                rd.Name        = sr.ReadString();
                rd.Description = sr.ReadString();
                sr.ReadNumber(); // Area number
                rd.RoomFlags  = sr.ReadNumber();
                rd.SectorType = sr.ReadShort();

                while (true)
                {
                    letter = sr.ReadLetter();

                    if (letter == 'S' || letter == 's')
                    {
                        break;
                    }

                    switch (letter)
                    {
                    case 'D':
                        var door = sr.ReadNumber();
                        if (door < 0 || door > 5)
                        {
                            throw new Exception($"Fread_rooms: vnum {vnum} has bad door number.");
                        }

                        var exit = new ExitData();
                        exit.Description = sr.ReadString();
                        exit.Keyword     = sr.ReadString();
                        var locks = sr.ReadNumber();
                        exit.ExitInfo  = locks;
                        exit.RSFlags   = (ExitFlags)locks;
                        exit.Key       = sr.ReadVnum();
                        exit.Vnum      = sr.ReadVnum();
                        rd.Exits[door] = exit;
                        break;

                    case 'E':
                        var ed = new ExtraDescrData();
                        ed.Keyword     = sr.ReadString();
                        ed.Description = sr.ReadString();
                        rd.ExtraDescr.Add(ed);
                        break;

                    case 'T':
                        var rt = new RoomTextData();
                        rt.Input    = sr.ReadString();
                        rt.Output   = sr.ReadString();
                        rt.CHOutput = sr.ReadString();
                        rt.Name     = sr.ReadString();
                        rt.Type     = sr.ReadNumber();
                        rt.Power    = sr.ReadNumber();
                        rt.Mob      = sr.ReadNumber();

                        rd.RoomText.Add(rt);
                        break;

                    default:
                        throw new Exception($"Load_rooms: vnum {vnum} has flag not 'DES'.");
                    }
                }

                // TODO ?????????????????????

                //iHash = vnum % MAX_KEY_HASH;
                //pRoomIndex->next = room_index_hash[iHash];
                //room_index_hash[iHash] = pRoomIndex;
                //top_room++;
                //top_vnum_room = top_vnum_room < vnum ? vnum : top_vnum_room;
                //assign_area_vnum(vnum);
            }
        }
        public static void LoadFromArea(StringReader sr, AreaData area)
        {
            if (area == null)
            {
                throw new Exception("Load_objects: no #AREA seen yet.");
            }

            while (true)
            {
                var letter = sr.ReadLetter();
                if (letter != '#')
                {
                    throw new Exception("Load_objects: # not found.");
                }

                var vnum = sr.ReadVnum();
                if (vnum == Vnum.None)
                {
                    break;
                }

                ObjectDef existing;
                if (!TryGetObjectData(vnum, out existing))
                {
                    throw new Exception($"Load_objects: vnum % {vnum} duplicated.");
                }

                var od = new ObjectDef
                {
                    Vnum = vnum,
                    Area = area
                };

                od.Name = sr.ReadString();
                // TODO upper first char
                od.ShortDescription = sr.ReadString();
                // TODO upper first char
                od.Description = sr.ReadString();
                sr.ReadString(); // Action description

                od.ItemType   = (ItemType)sr.ReadShort();
                od.ExtraFlags = (ExtraFlags)sr.ReadNumber();
                od.WearFlags  = sr.ReadNumber();

                switch (od.ItemType)
                {
                case ItemType.Wand:
                case ItemType.Staff:
                    od.Value[0] = sr.ReadNumber();
                    od.Value[1] = sr.ReadNumber();
                    od.Value[2] = sr.ReadNumber();
                    od.Value[3] = SkillType.Lookup(sr.ReadWord());
                    break;

                case ItemType.Potion:
                case ItemType.Pill:
                case ItemType.Scroll:
                    od.Value[0] = sr.ReadNumber();
                    od.Value[1] = SkillType.Lookup(sr.ReadWord());
                    od.Value[2] = SkillType.Lookup(sr.ReadWord());
                    od.Value[3] = SkillType.Lookup(sr.ReadWord());
                    break;

                default:
                    od.Value[0] = sr.ReadNumber();
                    od.Value[1] = sr.ReadNumber();
                    od.Value[2] = sr.ReadNumber();
                    od.Value[3] = sr.ReadNumber();
                    break;
                }

                od.Weight = sr.ReadShort();
                od.Cost   = sr.ReadNumber();

                sr.ReadNumber(); // Cost per day

                //if (od.ItemType == ItemType.Potion)
                //{
                //    od.ExtraFlags |= ExtraFlags.NoDrop;
                //}

                while (true)
                {
                    bool done = false;
                    switch (sr.PeekLetter())
                    {
                    case 'A':
                        sr.ReadLetter();
                        var aff = new AffectData
                        {
                            Type     = -1,
                            Duration = -1
                        };
                        aff.Location = sr.ReadShort();
                        aff.Modifier = sr.ReadShort();
                        od.Affected.Add(aff);
                        // top_affect++;
                        break;

                    case 'E':
                        sr.ReadLetter();
                        var ed = new ExtraDescrData();
                        ed.Keyword     = sr.ReadString();
                        ed.Description = sr.ReadString();
                        od.ExtraDescr.Add(ed);
                        //top_ed++;
                        break;

                    case 'Q':
                        sr.ReadLetter();
                        od.CHPowerOn      = sr.ReadString();
                        od.CHPowerOff     = sr.ReadString();
                        od.CHPowerUse     = sr.ReadString();
                        od.VictimPowerOn  = sr.ReadString();
                        od.VictimPowerOff = sr.ReadString();
                        od.VictimPowerUse = sr.ReadString();
                        od.SpecType       = sr.ReadNumber();
                        od.SpecPower      = sr.ReadNumber();
                        break;

                    default:
                        done = true;
                        break;
                    }
                    if (done)
                    {
                        break;
                    }
                }

                // ?????
                switch (od.ItemType)
                {
                case ItemType.Pill:
                case ItemType.Potion:
                case ItemType.Scroll:
                case ItemType.Staff:
                case ItemType.Wand:
                    break;
                }

                switch (od.Vnum)
                {
                case (Vnum)3375:
                    // CHAOS = true;
                    break;

                case (Vnum)29515:
                    // VISOR = true;
                    break;

                case (Vnum)29512:
                    // DARKNESS = true;
                    break;

                case (Vnum)29505:
                    // SPEED = true;
                    break;

                case (Vnum)29518:
                    // BRACELET = true;
                    break;

                case (Vnum)29504:
                    // TORC = true;
                    break;

                case (Vnum)29514:
                    // ARMOUR = true;
                    break;

                case (Vnum)29516:
                    // CLAWS = true;
                    break;

                case (Vnum)29555:
                    // ITEMAFFMANTIS = true;
                    break;

                case (Vnum)2654:
                    // ITEMAFFENTROPY = true;
                    break;

                case (Vnum)29598:
                    // ITEMAFFENTROPY = true;
                    break;
                }


                // TODO ???????

                //iHash = vnum % MAX_KEY_HASH;
                //pObjIndex->next = obj_index_hash[iHash];
                //obj_index_hash[iHash] = pObjIndex;
                //top_obj_index++;
                //top_vnum_obj = top_vnum_obj < vnum ? vnum : top_vnum_obj;  /* OLC */
                //assign_area_vnum(vnum);                                  /* OLC */
            }
        }