2using System.Collections.Generic;
 
    5using Terraria.DataStructures;
 
    6using Terraria.GameContent.Tile_Entities;
 
    8using Terraria.ModLoader.Default;
 
   13    internal static class TileIO
 
   16        internal struct TileTables
 
   18            internal IDictionary<ushort, ushort> tiles;
 
   19            internal IDictionary<ushort, bool> frameImportant;
 
   20            internal IDictionary<ushort, ushort> walls;
 
   21            internal IDictionary<ushort, string> tileModNames;
 
   22            internal IDictionary<ushort, string> tileNames;
 
   24            internal static TileTables Create() {
 
   25                TileTables tables = 
new TileTables {
 
   26                    tiles = 
new Dictionary<ushort, ushort>(),
 
   27                    frameImportant = 
new Dictionary<ushort, bool>(),
 
   28                    walls = 
new Dictionary<ushort, ushort>(),
 
   29                    tileModNames = 
new Dictionary<ushort, string>(),
 
   30                    tileNames = 
new Dictionary<ushort, string>()
 
   36        internal static TagCompound SaveTiles() {
 
   39            using (var ms = 
new MemoryStream())
 
   41                WriteTileData(writer, hasTile, hasWall);
 
   43                var tileList = 
new List<TagCompound>();
 
   44                for (
int type = TileID.Count; type < hasTile.Length; type++) {
 
   49                    tileList.Add(
new TagCompound {
 
   50                        [
"value"] = (short)type,
 
   51                        [
"mod"] = modTile.mod.Name,
 
   52                        [
"name"] = modTile.Name,
 
   53                        [
"framed"] = Main.tileFrameImportant[type],
 
   56                var wallList = 
new List<TagCompound>();
 
   57                for (
int wall = WallID.Count; wall < hasWall.Length; wall++) {
 
   62                    wallList.Add(
new TagCompound {
 
   63                        [
"value"] = (short)wall,
 
   64                        [
"mod"] = modWall.mod.Name,
 
   65                        [
"name"] = modWall.Name,
 
   68                if (tileList.Count == 0 && wallList.Count == 0)
 
   71                return new TagCompound {
 
   72                    [
"tileMap"] = tileList,
 
   73                    [
"wallMap"] = wallList,
 
   74                    [
"data"] = ms.ToArray()
 
   79        internal static void LoadTiles(TagCompound tag) {
 
   80            if (!tag.ContainsKey(
"data"))
 
   83            var tables = TileTables.Create();
 
   84            foreach (var tileTag 
in tag.GetList<TagCompound>(
"tileMap")) {
 
   85                ushort type = (ushort)tileTag.GetShort(
"value");
 
   86                string modName = tileTag.GetString(
"mod");
 
   87                string name = tileTag.GetString(
"name");
 
   89                tables.tiles[type] = mod == 
null ? (ushort)0 : (ushort)mod.
TileType(name);
 
   90                if (tables.tiles[type] == 0) {
 
   91                    tables.tiles[type] = (ushort)
ModContent.GetInstance<ModLoaderMod>().TileType(
"PendingMysteryTile");
 
   92                    tables.tileModNames[type] = modName;
 
   93                    tables.tileNames[type] = name;
 
   95                tables.frameImportant[type] = tileTag.GetBool(
"framed");
 
   97            foreach (var wallTag 
in tag.GetList<TagCompound>(
"wallMap")) {
 
   98                ushort wall = (ushort)wallTag.GetShort(
"value");
 
   99                string modName = wallTag.GetString(
"mod");
 
  100                string name = wallTag.GetString(
"name");
 
  102                tables.walls[wall] = mod == 
null ? (ushort)0 : (ushort)mod.
WallType(name);
 
  104            using (var memoryStream = 
new MemoryStream(tag.GetByteArray(
"data")))
 
  106                ReadTileData(reader, tables);
 
  107            WorldIO.ValidateSigns();
 
  110        internal static void LoadLegacyTiles(
BinaryReader reader) {
 
  111            TileTables tables = TileTables.Create();
 
  112            ushort count = reader.ReadUInt16();
 
  113            for (
int k = 0; k < count; k++) {
 
  114                ushort type = reader.ReadUInt16();
 
  115                string modName = reader.ReadString();
 
  116                string name = reader.ReadString();
 
  118                tables.tiles[type] = mod == 
null ? (ushort)0 : (ushort)mod.
TileType(name);
 
  119                if (tables.tiles[type] == 0) {
 
  120                    tables.tiles[type] = (ushort)
ModContent.GetInstance<ModLoaderMod>().TileType(
"PendingMysteryTile");
 
  121                    tables.tileModNames[type] = modName;
 
  122                    tables.tileNames[type] = name;
 
  124                tables.frameImportant[type] = reader.ReadBoolean();
 
  126            count = reader.ReadUInt16();
 
  127            for (
int k = 0; k < count; k++) {
 
  128                ushort wall = reader.ReadUInt16();
 
  129                string modName = reader.ReadString();
 
  130                string name = reader.ReadString();
 
  132                tables.walls[wall] = mod == 
null ? (ushort)0 : (ushort)mod.
WallType(name);
 
  134            ReadTileData(reader, tables);
 
  137        internal static void WriteTileData(
BinaryWriter writer, 
bool[] hasTile, 
bool[] hasWall) {
 
  139            bool nextModTile = 
false;
 
  143                Tile tile = Main.tile[i, j];
 
  144                if (HasModData(tile)) {
 
  152                    WriteModTile(ref i, ref j, writer, ref nextModTile, hasTile, hasWall);
 
  162            while (NextTile(ref i, ref j));
 
  168        internal static void ReadTileData(
BinaryReader reader, TileTables tables) {
 
  171            bool nextModTile = 
false;
 
  174                    byte skip = reader.ReadByte();
 
  175                    while (skip == 255) {
 
  176                        for (
byte k = 0; k < 255; k++) {
 
  177                            if (!NextTile(ref i, ref j)) {
 
  181                        skip = reader.ReadByte();
 
  183                    for (
byte k = 0; k < skip; k++) {
 
  184                        if (!NextTile(ref i, ref j)) {
 
  192                ReadModTile(ref i, ref j, tables, reader, ref nextModTile);
 
  194            while (NextTile(ref i, ref j));
 
  197        internal static void WriteModTile(ref 
int i, ref 
int j, 
BinaryWriter writer, ref 
bool nextModTile, 
bool[] hasTile, 
bool[] hasWall) {
 
  198            Tile tile = Main.tile[i, j];
 
  200            byte[] data = 
new byte[11];
 
  202            if (tile.active() && tile.type >= TileID.Count) {
 
  203                hasTile[tile.type] = 
true;
 
  205                data[index] = (byte)tile.type;
 
  207                data[index] = (
byte)(tile.type >> 8);
 
  209                if (Main.tileFrameImportant[tile.type]) {
 
  210                    data[index] = (byte)tile.frameX;
 
  212                    if (tile.frameX >= 256) {
 
  214                        data[index] = (byte)(tile.frameX >> 8);
 
  217                    data[index] = (byte)tile.frameY;
 
  219                    if (tile.frameY >= 256) {
 
  221                        data[index] = (byte)(tile.frameY >> 8);
 
  225                if (tile.color() != 0) {
 
  227                    data[index] = tile.color();
 
  231            if (tile.wall >= WallID.Count) {
 
  232                hasWall[tile.wall] = 
true;
 
  234                data[index] = (byte)tile.wall;
 
  236                data[index] = (
byte)(tile.wall >> 8);
 
  238                if (tile.wallColor() != 0) {
 
  240                    data[index] = tile.wallColor();
 
  247            while (NextTile(ref nextI, ref nextJ)) {
 
  248                if (tile.isTheSameAs(Main.tile[nextI, nextJ]) && sameCount < 255) {
 
  253                else if (HasModData(Main.tile[nextI, nextJ])) {
 
  264                data[index] = sameCount;
 
  268            writer.Write(data, 0, index);
 
  271        internal static void ReadModTile(ref 
int i, ref 
int j, TileTables tables, 
BinaryReader reader, ref 
bool nextModTile) {
 
  273            flags = reader.ReadByte();
 
  274            Tile tile = Main.tile[i, j];
 
  275            if ((flags & 1) == 1) {
 
  277                ushort saveType = reader.ReadUInt16();
 
  278                tile.type = tables.tiles[saveType];
 
  279                if (tables.frameImportant[saveType]) {
 
  280                    if ((flags & 2) == 2) {
 
  281                        tile.frameX = reader.ReadInt16();
 
  284                        tile.frameX = reader.ReadByte();
 
  286                    if ((flags & 4) == 4) {
 
  287                        tile.frameY = reader.ReadInt16();
 
  290                        tile.frameY = reader.ReadByte();
 
  297                if (tile.type == 
ModContent.GetInstance<ModLoaderMod>().TileType(
"PendingMysteryTile")
 
  298                    && tables.tileNames.ContainsKey(saveType)) {
 
  299                    MysteryTileInfo info;
 
  300                    if (tables.frameImportant[saveType]) {
 
  301                        info = 
new MysteryTileInfo(tables.tileModNames[saveType], tables.tileNames[saveType],
 
  302                            tile.frameX, tile.frameY);
 
  305                        info = 
new MysteryTileInfo(tables.tileModNames[saveType], tables.tileNames[saveType]);
 
  307                    MysteryTilesWorld modWorld = 
ModContent.GetInstance<MysteryTilesWorld>();
 
  308                    int pendingFrameID = modWorld.pendingInfos.IndexOf(info);
 
  309                    if (pendingFrameID < 0) {
 
  310                        pendingFrameID = modWorld.pendingInfos.Count;
 
  311                        modWorld.pendingInfos.Add(info);
 
  313                    MysteryTileFrame pendingFrame = 
new MysteryTileFrame(pendingFrameID);
 
  314                    tile.frameX = pendingFrame.FrameX;
 
  315                    tile.frameY = pendingFrame.FrameY;
 
  317                if ((flags & 8) == 8) {
 
  318                    tile.color(reader.ReadByte());
 
  320                WorldGen.tileCounts[tile.type] += j <= Main.worldSurface ? 5 : 1;
 
  322            if ((flags & 16) == 16) {
 
  323                tile.wall = tables.walls[reader.ReadUInt16()];
 
  324                if ((flags & 32) == 32) {
 
  325                    tile.wallColor(reader.ReadByte());
 
  328            if ((flags & 64) == 64) {
 
  329                byte sameCount = reader.ReadByte();
 
  330                for (
byte k = 0; k < sameCount; k++) {
 
  331                    NextTile(ref i, ref j);
 
  332                    Main.tile[i, j].CopyFrom(tile);
 
  333                    WorldGen.tileCounts[tile.type] += j <= Main.worldSurface ? 5 : 1;
 
  336            if ((flags & 128) == 128) {
 
  341        private static bool HasModData(Tile tile) {
 
  342            return (tile.active() && tile.type >= TileID.Count) || tile.wall >= WallID.Count;
 
  345        private static bool NextTile(ref 
int i, ref 
int j) {
 
  347            if (j >= Main.maxTilesY) {
 
  350                if (i >= Main.maxTilesX) {
 
  359        internal static void VanillaSaveFrames(Tile tile, ref 
short frameX) {
 
  360            if (tile.type == TileID.Mannequin || tile.type == TileID.Womannequin) {
 
  361                int slot = tile.frameX / 100;
 
  362                int position = tile.frameY / 18;
 
  363                if (HasModArmor(slot, position)) {
 
  369        internal struct ContainerTables
 
  371            internal IDictionary<int, int> headSlots;
 
  372            internal IDictionary<int, int> bodySlots;
 
  373            internal IDictionary<int, int> legSlots;
 
  375            internal static ContainerTables Create() {
 
  376                ContainerTables tables = 
new ContainerTables {
 
  377                    headSlots = 
new Dictionary<int, int>(),
 
  378                    bodySlots = 
new Dictionary<int, int>(),
 
  379                    legSlots = 
new Dictionary<int, int>()
 
  386        internal static TagCompound SaveContainers() {
 
  387            var ms = 
new MemoryStream();
 
  389            byte[] flags = 
new byte[1];
 
  391            ISet<int> headSlots = 
new HashSet<int>();
 
  392            ISet<int> bodySlots = 
new HashSet<int>();
 
  393            ISet<int> legSlots = 
new HashSet<int>();
 
  394            IDictionary<int, int> itemFrames = 
new Dictionary<int, int>();
 
  395            for (
int i = 0; i < Main.maxTilesX; i++) {
 
  396                for (
int j = 0; j < Main.maxTilesY; j++) {
 
  397                    Tile tile = Main.tile[i, j];
 
  398                    if (tile.active() && (tile.type == TileID.Mannequin || tile.type == TileID.Womannequin)) {
 
  399                        int slot = tile.frameX / 100;
 
  400                        int position = tile.frameY / 18;
 
  401                        if (HasModArmor(slot, position)) {
 
  405                            else if (position == 1) {
 
  408                            else if (position == 2) {
 
  418            foreach (KeyValuePair<int, TileEntity> entity 
in TileEntity.ByID) {
 
  419                TEItemFrame itemFrame = entity.Value as TEItemFrame;
 
  421                    itemFrames.Add(itemFrame.ID, tileEntity);
 
  431            writer.Write(numFlags);
 
  432            writer.Write(flags, 0, numFlags);
 
  433            if ((flags[0] & 1) == 1) {
 
  434                writer.Write((ushort)headSlots.Count);
 
  435                foreach (
int slot 
in headSlots) {
 
  436                    writer.Write((ushort)slot);
 
  439                    writer.Write(item.
Name);
 
  441                writer.Write((ushort)bodySlots.Count);
 
  442                foreach (
int slot 
in bodySlots) {
 
  443                    writer.Write((ushort)slot);
 
  446                    writer.Write(item.
Name);
 
  448                writer.Write((ushort)legSlots.Count);
 
  449                foreach (
int slot 
in legSlots) {
 
  450                    writer.Write((ushort)slot);
 
  453                    writer.Write(item.
Name);
 
  455                WriteContainerData(writer);
 
  457            var tag = 
new TagCompound();
 
  458            tag.Set(
"data", ms.ToArray());
 
  460            if (itemFrames.Count > 0) {
 
  461                tag.Set(
"itemFrames", itemFrames.Select(entry =>
 
  463                        [
"id"] = entry.Value,
 
  464                        [
"item"] = ItemIO.Save(((TEItemFrame)TileEntity.ByID[entry.Key]).item)
 
  471        internal static void LoadContainers(TagCompound tag) {
 
  472            if (tag.ContainsKey(
"data"))
 
  473                ReadContainers(
new BinaryReader(
new MemoryStream(tag.GetByteArray(
"data"))));
 
  475            foreach (var frameTag 
in tag.GetList<TagCompound>(
"itemFrames")) {
 
  476                if (
TileEntity.ByID.TryGetValue(frameTag.GetInt(
"id"), out 
TileEntity tileEntity) && tileEntity is TEItemFrame itemFrame)
 
  477                    ItemIO.Load(itemFrame.item, frameTag.GetCompound(
"item"));
 
  479                    Logging.tML.Warn($
"Due to a bug in previous versions of tModLoader, the following ItemFrame data has been lost: {frameTag.ToString()}");
 
  483        internal static void ReadContainers(
BinaryReader reader) {
 
  484            byte[] flags = 
new byte[1];
 
  485            reader.Read(flags, 0, reader.ReadByte());
 
  486            if ((flags[0] & 1) == 1) {
 
  487                ContainerTables tables = ContainerTables.Create();
 
  488                int count = reader.ReadUInt16();
 
  489                for (
int k = 0; k < count; k++) {
 
  490                    int slot = reader.ReadUInt16();
 
  491                    string modName = reader.ReadString();
 
  492                    string name = reader.ReadString();
 
  494                    tables.headSlots[slot] = mod?.
GetItem(name).
item.headSlot ?? 0;
 
  496                count = reader.ReadUInt16();
 
  497                for (
int k = 0; k < count; k++) {
 
  498                    int slot = reader.ReadUInt16();
 
  499                    string modName = reader.ReadString();
 
  500                    string name = reader.ReadString();
 
  502                    tables.bodySlots[slot] = mod?.
GetItem(name).
item.bodySlot ?? 0;
 
  504                count = reader.ReadUInt16();
 
  505                for (
int k = 0; k < count; k++) {
 
  506                    int slot = reader.ReadUInt16();
 
  507                    string modName = reader.ReadString();
 
  508                    string name = reader.ReadString();
 
  510                    tables.legSlots[slot] = mod?.
GetItem(name).
item.legSlot ?? 0;
 
  512                ReadContainerData(reader, tables);
 
  515            if ((flags[0] & 2) == 2) {
 
  516                int count = reader.ReadInt32();
 
  517                for (
int k = 0; k < count; k++) {
 
  518                    int id = reader.ReadInt32();
 
  519                    TEItemFrame itemFrame = 
TileEntity.ByID[id] as TEItemFrame;
 
  520                    ItemIO.LoadLegacy(itemFrame.item, reader, 
true);
 
  525        internal static void WriteContainerData(
BinaryWriter writer) {
 
  526            for (
int i = 0; i < Main.maxTilesX; i++) {
 
  527                for (
int j = 0; j < Main.maxTilesY; j++) {
 
  528                    Tile tile = Main.tile[i, j];
 
  529                    if (tile.active() && (tile.type == TileID.Mannequin || tile.type == TileID.Womannequin)) {
 
  530                        int slot = tile.frameX / 100;
 
  531                        int frameX = tile.frameX % 100;
 
  532                        int position = tile.frameY / 18;
 
  533                        if (HasModArmor(slot, position) && frameX % 36 == 0) {
 
  536                            writer.Write((
byte)position);
 
  537                            writer.Write((ushort)slot);
 
  545        internal static void ReadContainerData(
BinaryReader reader, ContainerTables tables) {
 
  546            int i = reader.ReadInt32();
 
  548                int j = reader.ReadInt32();
 
  549                int position = reader.ReadByte();
 
  550                int slot = reader.ReadUInt16();
 
  551                Tile left = Main.tile[i, j];
 
  552                Tile right = Main.tile[i + 1, j];
 
  553                if (left.active() && right.active() && (left.type == TileID.Mannequin || left.type == TileID.Womannequin)
 
  554                    && left.type == right.type && (left.frameX == 0 || left.frameX == 36) && right.frameX == left.frameX + 18
 
  555                    && left.frameY / 18 == position && left.frameY == right.frameY) {
 
  557                        slot = tables.headSlots[slot];
 
  559                    else if (position == 1) {
 
  560                        slot = tables.bodySlots[slot];
 
  562                    else if (position == 2) {
 
  563                        slot = tables.legSlots[slot];
 
  565                    left.frameX += (short)(100 * slot);
 
  567                i = reader.ReadInt32();
 
  571        private static bool HasModArmor(
int slot, 
int position) {
 
  573                return slot >= Main.numArmorHead;
 
  575            else if (position == 1) {
 
  576                return slot >= Main.numArmorBody;
 
  578            else if (position == 2) {
 
  579                return slot >= Main.numArmorLegs;
 
  584        internal static List<TagCompound> SaveTileEntities() {
 
  585            List<TagCompound> list = 
new List<TagCompound>();
 
  586            foreach (KeyValuePair<int, TileEntity> pair 
in TileEntity.ByID) {
 
  589                    list.Add(
new TagCompound {
 
  591                        [
"name"] = tileEntity.
Name,
 
  592                        [
"X"] = tileEntity.Position.X,
 
  593                        [
"Y"] = tileEntity.Position.Y,
 
  594                        [
"data"] = tileEntity.
Save()
 
  601        internal static void LoadTileEntities(IList<TagCompound> list) {
 
  602            foreach (TagCompound tag 
in list) {
 
  606                if (tileEntity != 
null) {
 
  608                    newEntity.type = (byte)tileEntity.
Type;
 
  609                    newEntity.Position = 
new Point16(tag.GetShort(
"X"), tag.GetShort(
"Y"));
 
  610                    if (tag.ContainsKey(
"data")) {
 
  612                            newEntity.
Load(tag.GetCompound(
"data"));
 
  613                            if (newEntity is MysteryTileEntity) {
 
  614                                ((MysteryTileEntity)newEntity).TryRestore(ref newEntity);
 
  619                            "Error in reading " + tileEntity.
Name + 
" tile entity data for " + mod.
Name, e);
 
  624                    tileEntity = 
ModContent.GetInstance<ModLoaderMod>().GetTileEntity(
"MysteryTileEntity");
 
  626                    newEntity.type = (byte)tileEntity.
Type;
 
  627                    newEntity.Position = 
new Point16(tag.GetShort(
"X"), tag.GetShort(
"Y"));
 
  628                    ((MysteryTileEntity)newEntity).SetData(tag);
 
  630                if (tileEntity.
ValidTile(newEntity.Position.X, newEntity.Position.Y)) {
 
  634                    if (
TileEntity.ByPosition.TryGetValue(newEntity.Position, out other)) {
 
  637                    TileEntity.ByPosition[newEntity.Position] = newEntity;
 
This serves as a central place to store equipment slots and their corresponding textures....
 
This serves as the central class from which item-related functions are carried out....
 
static ModItem GetItem(int type)
Gets the ModItem instance corresponding to the specified type. Returns null if no modded item has the...
 
static bool NeedsModSaving(Item item)
 
Manages content added by mods. Liasons between mod content and Terraria's arrays and oversees the Loa...
 
Mod is an abstract class that you will override. It serves as a central place from which the mod's co...
 
int TileType(string name)
Gets the type of the ModTile of this mod with the given name. Returns 0 if no ModTile with the given ...
 
ModItem GetItem(string name)
Gets the ModItem instance corresponding to the name. Because this method is in the Mod class,...
 
virtual string Name
Stores the name of the mod. This name serves as the mod's identification, and also helps with saving ...
 
int WallType(string name)
Gets the type of the ModWall of this mod with the given name. Returns 0 if no ModWall with the given ...
 
ModTileEntity GetTileEntity(string name)
Gets the ModTileEntity of this mod corresponding to the given name. Returns null if no ModTileEntity ...
 
This class serves as a place for you to place all your properties and hooks for each item....
 
string Name
The internal name of this ModItem.
 
Item item
The item object that this ModItem controls.
 
This serves as the central class which loads mods. It contains many static fields and methods related...
 
static Mod GetMod(string name)
Gets the instance of the Mod with the specified name.
 
Tile Entities are Entities tightly coupled with tiles, allowing the possibility of tiles to exhibit c...
 
int Type
The numeric type used to identify this kind of tile entity.
 
static ModTileEntity ConstructFromBase(ModTileEntity tileEntity)
Returns a new ModTileEntity with the same class, mod, name, and type as the parameter....
 
abstract bool ValidTile(int i, int j)
Whether or not this tile entity is allowed to survive at the given coordinates. You should check whet...
 
virtual void Load(TagCompound tag)
Allows you to load the custom data you have saved for this tile entity.
 
virtual TagCompound Save()
Allows you to save custom data for this tile entity.
 
string Name
The internal name of this ModTileEntity.
 
Mod mod
The mod that added this ModTileEntity.
 
This serves as the central class from which tile-related functions are supported and carried out.
 
static ModTile GetTile(int type)
Gets the ModTile instance with the given type. If no ModTile with the given type exists,...
 
This serves as the central class from which wall-related functions are supported and carried out.
 
static ModWall GetWall(int type)
Gets the ModWall instance with the given type. If no ModWall with the given type exists,...
 
EquipType
This is an enum of all the types of equipment that exist. An equipment type is defined as a type or l...