2using System.Collections.Generic;
 
    5using System.Security.Cryptography;
 
    6using Terraria.Graphics.Shaders;
 
    7using Terraria.ModLoader.Default;
 
    8using Terraria.ModLoader.Engine;
 
   11using Terraria.Utilities;
 
   15    internal static class PlayerIO
 
   17        internal static void WriteVanillaHairDye(
short hairDye, 
BinaryWriter writer) {
 
   18            writer.Write((
byte)(hairDye > EffectsTracker.vanillaHairShaderCount ? 0 : hairDye));
 
   23        internal static void Save(Player player, 
string path, 
bool isCloudSave) {
 
   24            path = Path.ChangeExtension(path, 
".tplr");
 
   25            if (FileUtilities.Exists(path, isCloudSave))
 
   26                FileUtilities.Copy(path, path + 
".bak", isCloudSave);
 
   28            var tag = 
new TagCompound {
 
   29                [
"armor"] = SaveInventory(player.armor),
 
   30                [
"dye"] = SaveInventory(player.dye),
 
   31                [
"inventory"] = SaveInventory(player.inventory),
 
   32                [
"miscEquips"] = SaveInventory(player.miscEquips),
 
   33                [
"miscDyes"] = SaveInventory(player.miscDyes),
 
   34                [
"bank"] = SaveInventory(player.bank.item),
 
   35                [
"bank2"] = SaveInventory(player.bank2.item),
 
   36                [
"bank3"] = SaveInventory(player.bank3.item),
 
   37                [
"hairDye"] = SaveHairDye(player.hairDye),
 
   38                [
"modData"] = SaveModData(player),
 
   39                [
"modBuffs"] = SaveModBuffs(player),
 
   40                [
"usedMods"] = SaveUsedMods(player)
 
   43            using (Stream stream = isCloudSave ? (Stream)
new MemoryStream() : (Stream)new FileStream(path, FileMode.Create)) {
 
   44                TagIO.ToStream(tag, stream);
 
   45                if (isCloudSave && SocialAPI.Cloud != 
null)
 
   46                    SocialAPI.Cloud.Write(path, ((MemoryStream)stream).ToArray());
 
   50        internal static void Load(Player player, 
string path, 
bool isCloudSave) {
 
   51            path = Path.ChangeExtension(path, 
".tplr");
 
   52            if (!FileUtilities.Exists(path, isCloudSave))
 
   55            var buf = FileUtilities.ReadAllBytes(path, isCloudSave);
 
   56            if (buf[0] != 0x1F || buf[1] != 0x8B) {
 
   57                LoadLegacy(player, buf);
 
   61            var tag = TagIO.FromStream(
new MemoryStream(buf));
 
   62            LoadInventory(player.armor, tag.GetList<TagCompound>(
"armor"));
 
   63            LoadInventory(player.dye, tag.GetList<TagCompound>(
"dye"));
 
   64            LoadInventory(player.inventory, tag.GetList<TagCompound>(
"inventory"));
 
   65            LoadInventory(player.miscEquips, tag.GetList<TagCompound>(
"miscEquips"));
 
   66            LoadInventory(player.miscDyes, tag.GetList<TagCompound>(
"miscDyes"));
 
   67            LoadInventory(player.bank.item, tag.GetList<TagCompound>(
"bank"));
 
   68            LoadInventory(player.bank2.item, tag.GetList<TagCompound>(
"bank2"));
 
   69            LoadInventory(player.bank3.item, tag.GetList<TagCompound>(
"bank3"));
 
   70            LoadHairDye(player, tag.GetString(
"hairDye"));
 
   71            LoadModData(player, tag.GetList<TagCompound>(
"modData"));
 
   72            LoadModBuffs(player, tag.GetList<TagCompound>(
"modBuffs"));
 
   73            LoadUsedMods(player, tag.GetList<
string>(
"usedMods"));
 
   76        public static List<TagCompound> SaveInventory(Item[] inv) {
 
   77            var list = 
new List<TagCompound>();
 
   78            for (
int k = 0; k < inv.Length; k++) {
 
   80                    var tag = ItemIO.Save(inv[k]);
 
   81                    tag.Set(
"slot", (
short)k);
 
   85            return list.Count > 0 ? list : 
null;
 
   88        public static void LoadInventory(Item[] inv, IList<TagCompound> list) {
 
   89            foreach (var tag 
in list)
 
   90                inv[tag.GetShort(
"slot")] = ItemIO.Load(tag);
 
   93        public static string SaveHairDye(
short hairDye) {
 
   94            if (hairDye <= EffectsTracker.vanillaHairShaderCount)
 
   97            int itemId = GameShaders.Hair._reverseShaderLookupDictionary[hairDye];
 
   99            return modItem.mod.Name + 
'/' + modItem.
Name;
 
  102        public static void LoadHairDye(Player player, 
string hairDyeItemName) {
 
  103            if (hairDyeItemName == 
"")
 
  110                player.hairDye = (byte)GameShaders.Hair.GetShaderIdFromItemId(modItem.item.type);
 
  113        internal static List<TagCompound> SaveModData(Player player) {
 
  114            var list = 
new List<TagCompound>();
 
  115            foreach (var modPlayer 
in player.modPlayers) {
 
  116                var data = modPlayer.Save();
 
  120                list.Add(
new TagCompound {
 
  121                    [
"mod"] = modPlayer.mod.Name,
 
  122                    [
"name"] = modPlayer.Name,
 
  129        internal static void LoadModData(Player player, IList<TagCompound> list) {
 
  130            foreach (var tag 
in list) {
 
  132                var modPlayer = mod == 
null ? null : player.GetModPlayer(mod, tag.GetString(
"name"));
 
  133                if (modPlayer != 
null) {
 
  135                        if (tag.ContainsKey(
"legacyData"))
 
  136                            modPlayer.LoadLegacy(
new BinaryReader(
new MemoryStream(tag.GetByteArray(
"legacyData"))));
 
  138                            modPlayer.
Load(tag.GetCompound(
"data"));
 
  142                            "Error in reading custom player data for " + mod.Name, e);
 
  146                    player.GetModPlayer<MysteryPlayer>().data.Add(tag);
 
  151        internal static List<TagCompound> SaveModBuffs(Player player) {
 
  152            var list = 
new List<TagCompound>();
 
  153            for (
int k = 0; k < Player.MaxBuffs; k++) {
 
  154                int buff = player.buffType[k];
 
  155                if (buff == 0 || Main.buffNoSave[buff])
 
  160                    list.Add(
new TagCompound {
 
  161                        [
"mod"] = modBuff.mod.Name,
 
  162                        [
"name"] = modBuff.Name,
 
  163                        [
"time"] = player.buffTime[k]
 
  167                    list.Add(
new TagCompound {
 
  168                        [
"mod"] = 
"Terraria",
 
  170                        [
"time"] = player.buffTime[k]
 
  177        internal static void LoadModBuffs(Player player, IList<TagCompound> list) {
 
  179            int buffCount = Player.MaxBuffs;
 
  180            while (buffCount > 0 && player.buffType[buffCount - 1] == 0)
 
  183            if (buffCount == 0) {
 
  185                foreach (var tag 
in list) {
 
  186                    if (buffCount == Player.MaxBuffs)
 
  189                    var modName = tag.GetString(
"mod");
 
  190                    int type = modName == 
"Terraria" ? tag.GetInt(
"id") : 
ModLoader.
GetMod(modName)?.
BuffType(tag.GetString(
"name")) ?? 0;
 
  192                        player.buffType[buffCount] = type;
 
  193                        player.buffTime[buffCount] = tag.GetInt(
"time");
 
  202            foreach (var tag 
in list.Reverse()) {
 
  204                int type = mod?.
BuffType(tag.GetString(
"name")) ?? 0;
 
  208                int index = Math.Min(tag.GetByte(
"index"), buffCount);
 
  209                Array.Copy(player.buffType, index, player.buffType, index + 1, Player.MaxBuffs - index - 1);
 
  210                Array.Copy(player.buffTime, index, player.buffTime, index + 1, Player.MaxBuffs - index - 1);
 
  211                player.buffType[index] = type;
 
  212                player.buffTime[index] = tag.GetInt(
"time");
 
  216        private static void LoadLegacy(Player player, 
byte[] buffer) {
 
  217            const int numFlagBytes = 2;
 
  218            RijndaelManaged rijndaelManaged = 
new RijndaelManaged();
 
  219            rijndaelManaged.Padding = PaddingMode.None;
 
  220            using (MemoryStream stream = 
new MemoryStream(buffer)) {
 
  221                using (CryptoStream cryptoStream = 
new CryptoStream(stream, rijndaelManaged.CreateDecryptor(Player.ENCRYPTION_KEY, Player.ENCRYPTION_KEY), CryptoStreamMode.Read)) {
 
  223                        byte limit = reader.ReadByte();
 
  227                        byte[] flags = reader.ReadBytes(limit);
 
  228                        if (flags.Length < numFlagBytes) {
 
  229                            Array.Resize(ref flags, numFlagBytes);
 
  231                        LoadLegacyModPlayer(player, flags, reader);
 
  237        private static void LoadLegacyModPlayer(Player player, 
byte[] flags, 
BinaryReader reader) {
 
  238            if ((flags[0] & 1) == 1) {
 
  239                ItemIO.LoadLegacyInventory(player.armor, reader);
 
  241            if ((flags[0] & 2) == 2) {
 
  242                ItemIO.LoadLegacyInventory(player.dye, reader);
 
  244            if ((flags[0] & 4) == 4) {
 
  245                ItemIO.LoadLegacyInventory(player.inventory, reader, 
true, 
true);
 
  247            if ((flags[0] & 8) == 8) {
 
  248                ItemIO.LoadLegacyInventory(player.miscEquips, reader);
 
  250            if ((flags[0] & 16) == 16) {
 
  251                ItemIO.LoadLegacyInventory(player.miscDyes, reader);
 
  253            if ((flags[0] & 32) == 32) {
 
  254                ItemIO.LoadLegacyInventory(player.bank.item, reader, 
true);
 
  256            if ((flags[0] & 64) == 64) {
 
  257                ItemIO.LoadLegacyInventory(player.bank2.item, reader, 
true);
 
  259            if ((flags[0] & 128) == 128) {
 
  260                LoadLegacyModData(player, reader);
 
  262            if ((flags[1] & 1) == 1) {
 
  263                LoadLegacyModBuffs(player, reader);
 
  267        private static void LoadLegacyModData(Player player, 
BinaryReader reader) {
 
  268            int count = reader.ReadUInt16();
 
  269            for (
int k = 0; k < count; k++) {
 
  270                string modName = reader.ReadString();
 
  271                string name = reader.ReadString();
 
  272                byte[] data = reader.ReadBytes(reader.ReadUInt16());
 
  274                ModPlayer modPlayer = mod == 
null ? null : player.GetModPlayer(mod, name);
 
  275                if (modPlayer != 
null) {
 
  276                    using (MemoryStream stream = 
new MemoryStream(data)) {
 
  283                                    "Error in reading custom player data for " + mod.
Name, e);
 
  289                    var tag = 
new TagCompound {
 
  292                        [
"legacyData"] = data
 
  294                    player.GetModPlayer<MysteryPlayer>().data.Add(tag);
 
  299        private static void LoadLegacyModBuffs(Player player, 
BinaryReader reader) {
 
  300            int num = reader.ReadByte();
 
  302            for (
int k = 0; k < num; k++) {
 
  303                int index = reader.ReadByte() - minusIndex;
 
  304                string modName = reader.ReadString();
 
  305                string name = reader.ReadString();
 
  306                int time = reader.ReadInt32();
 
  308                int type = mod == 
null ? 0 : mod.
BuffType(name);
 
  310                    for (
int j = Player.MaxBuffs - 1; j > index; j--) {
 
  311                        player.buffType[j] = player.buffType[j - 1];
 
  312                        player.buffTime[j] = player.buffTime[j - 1];
 
  314                    player.buffType[index] = type;
 
  315                    player.buffTime[index] = time;
 
  321            for (
int k = 1; k < Player.MaxBuffs; k++) {
 
  322                if (player.buffType[k] > 0) {
 
  324                    while (player.buffType[j] == 0) {
 
  325                        player.buffType[j] = player.buffType[j + 1];
 
  326                        player.buffTime[j] = player.buffTime[j + 1];
 
  327                        player.buffType[j + 1] = 0;
 
  328                        player.buffTime[j + 1] = 0;
 
  335        internal static void LoadUsedMods(Player player, IList<string> usedMods) {
 
  336            player.usedMods = usedMods;
 
  339        internal static List<string> SaveUsedMods(Player player) {
 
  340            return ModLoader.
Mods.Select(m => m.Name).Except(
new[] { 
"ModLoader" }).ToList();
 
  344        internal static void MoveToCloud(
string localPath, 
string cloudPath) {
 
  345            localPath = Path.ChangeExtension(localPath, 
".tplr");
 
  346            cloudPath = Path.ChangeExtension(cloudPath, 
".tplr");
 
  347            if (File.Exists(localPath)) {
 
  348                FileUtilities.MoveToCloud(localPath, cloudPath);
 
  355        internal static void MoveToLocal(
string cloudPath, 
string localPath) {
 
  356            cloudPath = Path.ChangeExtension(cloudPath, 
".tplr");
 
  357            localPath = Path.ChangeExtension(localPath, 
".tplr");
 
  358            if (FileUtilities.Exists(cloudPath, 
true)) {
 
  359                FileUtilities.MoveToLocal(cloudPath, localPath);
 
  363        internal static void LoadBackup(
string path, 
bool cloudSave) {
 
  364            path = Path.ChangeExtension(path, 
".tplr");
 
  365            if (FileUtilities.Exists(path + 
".bak", cloudSave)) {
 
  366                FileUtilities.Move(path + 
".bak", path, cloudSave, 
true);
 
  371        internal static void ErasePlayer(
string path, 
bool cloudSave) {
 
  372            path = Path.ChangeExtension(path, 
".tplr");
 
  374                FileUtilities.Delete(path, cloudSave);
 
  375                FileUtilities.Delete(path + 
".bak", cloudSave);
 
This serves as the central class from which buff-related functions are supported and carried out.
 
static ModBuff GetBuff(int type)
Gets the ModBuff instance with the given type. If no ModBuff with the given type exists,...
 
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...
 
static void SplitName(string name, out string domain, out string subName)
 
Mod is an abstract class that you will override. It serves as a central place from which the mod's co...
 
int BuffType(string name)
Gets the type of the ModBuff of this mod corresponding to the given name. Returns 0 if no ModBuff wit...
 
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 ...
 
virtual void Load()
Override this method to add most of your content to your mod. Here you will call other methods such a...
 
string Name
The internal name of this ModItem.
 
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.
 
A ModPlayer instance represents an extension of a Player instance. You can store fields in the ModPla...
 
virtual void LoadLegacy(BinaryReader reader)
Allows you to load pre-v0.9 custom data you have saved for this player.