using System; using System.Collections.Generic; using System.Text; using static System.Buffers.Binary.BinaryPrimitives; namespace NHSE.Core; /// /// Data structures stored for the players Post Box (postbox.dat) /// public sealed class PostBox : EncryptedFilePair { public const string FileName = "postbox"; public readonly PostBoxOffsets Offsets; public PostBox(ISaveFileProvider provider) : base(provider, FileName) => Offsets = PostBoxOffsets.GetOffsets(Info); public readonly int MailLength = Mail.SIZE; public readonly int MailListLength = 0x46E60; public readonly int MaxMailItems = Mail.MaxSlots; public Span Get_s_7b602b39() => Data.Slice(Offsets.s_7b602b39, 4); public Span MailListWholeRegion { get => Data.Slice(Offsets.MailList, MailListLength); set => value.CopyTo(Data[Offsets.MailList..]); } public Span GetIndividualLetter(int index) => Data.Slice(Offsets.MailList + (MailLength * index), MailLength); public void SetIndividualLetter(int index, Span value) { value.CopyTo(Data[(Offsets.MailList + (MailLength * index))..]); } public List GetUsedPostBoxSlots() { var usedSlots = new List(); for (int i = 0; i < MaxMailItems; i++) { var readStatus = MailListWholeRegion[(MailLength * i) + Mail.ReadUnread]; if (readStatus == 0x03 || readStatus == 0x04) usedSlots.Add(i); } return usedSlots; } public int GetLatestUniqueId() { return ReadInt32LittleEndian(Data.Slice(Offsets.LatestUniqueId, 4)); } public void SetLatestUniqueId(int uniqueId) { WriteInt32LittleEndian(Data.Slice(Offsets.LatestUniqueId, 4), uniqueId); } #region Mail public void WipeTxRxSection(int index) { var arr = new byte[0x60]; Span bytes = arr; bytes.CopyTo(Data[(Offsets.MailList + (MailLength * index) + Mail.ReadUnread)..]); } public bool GetMailCheck(int index) { return Data[Offsets.MailList + (MailLength * index) + Mail.ReadUnread] != 0x00; } public bool GetMailReadStatus(int index) { return Data[Offsets.MailList + (MailLength * index) + Mail.ReadUnread] != 0x03; } public void SetMailReadStatus(int index, bool isRead) { Data[Offsets.MailList + (MailLength * index) + Mail.ReadUnread] = isRead ? (byte)0x04 : (byte)0x03; } public bool GetMailFavStatus(int index) { return Data[Offsets.MailList + (MailLength * index) + Mail.LetterFavourited] != 0x00; } public void SetMailFavStatus(int index, bool isFavourite) { Data[Offsets.MailList + (MailLength * index) + Mail.LetterFavourited] = isFavourite ? (byte)0x01 : (byte)0x00; } public uint GetPlayerSenderIslandId(int index) { var offset = Offsets.MailList + (MailLength * index) + Mail.PlayerSenderIslandID; return ReadUInt32LittleEndian(Data.Slice(offset, 4)); } public void SetPlayerSenderIslandId(int index, uint islandId) { var offset = Offsets.MailList + (MailLength * index) + Mail.PlayerSenderIslandID; WriteUInt32LittleEndian(Data.Slice(offset, 4), islandId); } public string GetPlayerSenderIslandName(int index) { return GetString(Offsets.MailList + (MailLength * index) + Mail.PlayerSenderIslandName, 10); } public void SetPlayerSenderIslandName(int index, string islandName) { var nameBytes = GetBytes(islandName, 10); nameBytes.CopyTo(Data[(Offsets.MailList + (MailLength * index) + Mail.PlayerSenderIslandName)..]); } public byte GetVillagerSenderSpeciesId(int index) { return Data[Offsets.MailList + (MailLength * index) + Mail.NPCSenderSpeciesID]; } public void SetVillagerSenderSpeciesId(int index, int speciesId) { Data[Offsets.MailList + (MailLength * index) + Mail.NPCSenderSpeciesID] = (byte)speciesId; } public byte GetVillagerSenderVariantId(int index) { return Data[Offsets.MailList + (MailLength * index) + Mail.NPCSenderVariantID]; } public void SetVillagerSenderVariantId(int index, int variantId) { Data[Offsets.MailList + (MailLength * index) + Mail.NPCSenderVariantID] = (byte)variantId; } public uint GetNPCSenderIslandId(int index) { var offset = Offsets.MailList + (MailLength * index) + Mail.NPCSenderIslandID; return ReadUInt32LittleEndian(Data.Slice(offset, 4)); } public void SetNPCSenderIslandId(int index, uint islandId) { var offset = Offsets.MailList + (MailLength * index) + Mail.NPCSenderIslandID; WriteUInt32LittleEndian(Data.Slice(offset, 4), islandId); } public string GetNPCSenderIslandName(int index) { return GetString(Offsets.MailList + (MailLength * index) + Mail.NPCSenderIslandName, 10); } public void SetNPCSenderIslandName(int index, string islandName) { var nameBytes = GetBytes(islandName, 20); nameBytes.CopyTo(Data[(Offsets.MailList + (MailLength * index) + Mail.NPCSenderIslandName)..]); } public uint GetSenderId(int index) { var offset = Offsets.MailList + (MailLength * index) + Mail.SenderID; return ReadUInt32LittleEndian(Data.Slice(offset, 4)); } public void SetSenderId(int index, uint senderId) { var offset = Offsets.MailList + (MailLength * index) + Mail.SenderID; WriteUInt32LittleEndian(Data.Slice(offset, 4), senderId); } public string GetSenderName(int index) { return GetString(Offsets.MailList + (MailLength * index) + Mail.SenderName, 10); } public void SetSenderName(int index, string senderName) { var nameBytes = GetBytes(senderName, 10); nameBytes.CopyTo(Data[(Offsets.MailList + (MailLength * index) + Mail.SenderName)..]); } public byte GetSenderType(int index) { return Data[Offsets.MailList + (MailLength * index) + Mail.SenderType]; } public void SetSenderType(int index, byte senderType) { Data[Offsets.MailList + (MailLength * index) + Mail.SenderType] = senderType; } public uint GetReceiverIslandId(int index) { var offset = Offsets.MailList + (MailLength * index) + Mail.ReceiverIslandID; return ReadUInt32LittleEndian(Data.Slice(offset, 4)); } public void SetReceiverIslandId(int index, uint islandId) { var offset = Offsets.MailList + (MailLength * index) + Mail.ReceiverIslandID; WriteUInt32LittleEndian(Data.Slice(offset, 4), islandId); } public string GetReceiverIslandName(int index) { return GetString(Offsets.MailList + (MailLength * index) + Mail.ReceiverIslandName, 10); } public void SetReceiverIslandName(int index, string islandName) { var nameBytes = GetBytes(islandName, 10); nameBytes.CopyTo(Data[(Offsets.MailList + (MailLength * index) + Mail.ReceiverIslandName)..]); } public uint GetReceiverPlayerId(int index) { var offset = Offsets.MailList + (MailLength * index) + Mail.ReceiverPlayerID; return ReadUInt32LittleEndian(Data.Slice(offset, 4)); } public void SetReceiverPlayerId(int index, uint playerId) { var offset = Offsets.MailList + (MailLength * index) + Mail.ReceiverPlayerID; WriteUInt32LittleEndian(Data.Slice(offset, 4), playerId); } public string GetReceiverPlayerName(int index) { return GetString(Offsets.MailList + (MailLength * index) + Mail.ReceiverPlayerName, 10); } public void SetReceiverPlayerName(int index, string playerName) { var nameBytes = GetBytes(playerName, 10); nameBytes.CopyTo(Data[(Offsets.MailList + (MailLength * index) + Mail.ReceiverPlayerName)..]); } public GSaveDate GetTimestamp(int index) { return Data.ToStructure(Offsets.MailList + (MailLength * index) + Mail.TimeStamp, GSaveDate.SIZE); } public void SetTimeStamp(int index, GSaveDate timestamp) { timestamp.ToBytes().CopyTo(Data[(Offsets.MailList + (MailLength * index) + Mail.TimeStamp)..]); } public Item GetAttachedItem(int index) { var offset = Offsets.MailList + (MailLength * index) + Mail.AttachedItemID; return Item.GetArray(Data.Slice(offset, Item.SIZE))[0]; } public void SetAttachedItem(int index, Item item) { var offset = Offsets.MailList + (MailLength * index) + Mail.AttachedItemID; var itemBytes = Item.SetArray(new[] { item }); itemBytes.CopyTo(Data[offset..]); } public byte GetItemWrappingType(int index) { return Data[Offsets.MailList + (MailLength * index) + Mail.MailItemWrappingType]; } public void SetItemWrappingType(int index, byte wrappingType) { Data[Offsets.MailList + (MailLength * index) + Mail.MailItemWrappingType] = wrappingType; } public bool GetMailType(int index) { var offset = Offsets.MailList + (MailLength * index); return Data[offset + Mail.MailType01] == 0x00 && Data[offset + Mail.MailType02] == 0x01; // Letter or boxed type } public void SetMailType(int index, bool letter) { var offset = Offsets.MailList + (MailLength * index); if (letter) { Data[offset + Mail.MailType01] = 0x00; Data[offset + Mail.MailType02] = 0x01; } // Letter type else { Data[offset + Mail.MailType01] = 0x01; Data[offset + Mail.MailType02] = 0x00; } // Boxed item type } public string GetLetterToLineText(int index) { return GetString(Offsets.MailList + (MailLength * index) + Mail.LetterToLineText, Mail.LetterToTextLength); } public void SetLetterToLineText(int index, string toLineText) { var textBytes = GetBytes(toLineText, Mail.LetterToTextLength); textBytes.CopyTo(Data[(Offsets.MailList + (MailLength * index) + Mail.LetterToLineText)..]); } public string GetLetterMessageText(int index) { return GetString(Offsets.MailList + (MailLength * index) + Mail.LetterMessageText, Mail.LetterMessageTextLength); } public void SetLetterMessageText(int index, string messageText) { var textBytes = GetBytes(messageText, Mail.LetterMessageTextLength); textBytes.CopyTo(Data[(Offsets.MailList + (MailLength * index) + Mail.LetterMessageText)..]); } public string GetLetterFromLineText(int index) { return GetString(Offsets.MailList + (MailLength * index) + Mail.LetterFromLineText, Mail.LetterFromTextLength); } public void SetLetterFromLineText(int index, string fromLineText) { var textBytes = GetBytes(fromLineText, Mail.LetterFromTextLength); textBytes.CopyTo(Data[(Offsets.MailList + (MailLength * index) + Mail.LetterFromLineText)..]); } public ushort GetMailStationerySkin(int index) { var offset = Offsets.MailList + (MailLength * index) + Mail.MailStationerySkin; return ReadUInt16LittleEndian(Data.Slice(offset, 2)); } public void SetMailStationerySkin(int index, ushort skinCode) { var offset = Offsets.MailList + (MailLength * index) + Mail.MailStationerySkin; WriteUInt16LittleEndian(Data.Slice(offset, 2), skinCode); } public byte GetPostBoxSortIndex(int index) { return Data[Offsets.MailList + (MailLength * index) + Mail.PostBoxSortIndex]; } public void SetPostBoxSortIndex(int index, byte sortIndex) { Data[Offsets.MailList + (MailLength * index) + Mail.PostBoxSortIndex] = sortIndex; } public string GetMailLanguageString(int index) { return Encoding.UTF8.GetString(Data.Slice(Offsets.MailList + (MailLength * index) + Mail.LanguageString, 5)); } /// /// Sets or clears the language string for a mail item at the specified index. /// /// The zero-based index of the mail item in the postbox. /// The IETF BCP 47 language tag string to set (e.g., "en-US", "ja-JP"). /// If true, sets the language string; if false, clears the language data. public void SetMailLanguageString(int index, string languageString, bool player) { var langOffset = Offsets.MailList + (MailLength * index) + Mail.LanguageString; if (player) { GetBytes(languageString, 5).CopyTo(Data[langOffset..]); } else { Data.Slice(langOffset, 5).Clear(); } } #endregion }