Beispiel #1
0
        /// <summary>Checks to see if the data belongs to a Gen3 Box RS save</summary>
        /// <param name="data">Save data of which to determine the type</param>
        /// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
        internal static GameVersion GetIsG3BOXSAV(byte[] data)
        {
            if (data.Length != SIZE_G3BOX && data.Length != SIZE_G3BOXGCI)
            {
                return(Invalid);
            }

            byte[] sav = data;

            // Verify first checksum
            ushort chk = 0; // initial value
            var    ofs = data.Length - SIZE_G3BOX + 0x2000;

            for (int i = 0x4; i < 0x1FFC; i += 2)
            {
                chk += BigEndian.ToUInt16(sav, ofs + i);
            }

            ushort chkA = chk;
            ushort chkB = (ushort)(0xF004 - chkA);

            ushort CHK_A = BigEndian.ToUInt16(sav, ofs + 0);
            ushort CHK_B = BigEndian.ToUInt16(sav, ofs + 2);

            return(CHK_A == chkA && CHK_B == chkB ? RSBOX : Invalid);
        }
Beispiel #2
0
        public override byte[] Write(bool DSV, bool GCI)
        {
            // Set Memo Back
            StrategyMemo.FinalData.CopyTo(Data, Memo);
            ShadowInfo.FinalData.CopyTo(Data, Shadow);
            SetChecksums();

            // Get updated save slot data
            ushort[] keys = new ushort[4];
            for (int i = 0; i < keys.Length; i++)
            {
                keys[i] = BigEndian.ToUInt16(Data, 8 + (i * 2));
            }
            byte[] newSAV = SaveUtil.EncryptGC(Data, 0x10, 0x27FD8, keys);

            // Put save slot back in original save data
            byte[] newFile = MC != null ? MC.SelectedSaveData : (byte[])BAK.Clone();
            Array.Copy(newSAV, 0, newFile, SLOT_START + (SaveIndex * SLOT_SIZE), newSAV.Length);

            // Return the gci if Memory Card is not being exported
            if (!IsMemoryCardSave || GCI)
            {
                return(Header.Concat(newFile).ToArray());
            }

            MC.SelectedSaveData = newFile.ToArray();
            return(MC.Data);
        }
Beispiel #3
0
        public static void SetChecksum(byte[] input, int offset, int len, int checksum_offset)
        {
            uint[] storedChecksums = new uint[16];
            for (int i = 0; i < storedChecksums.Length; i++)
            {
                storedChecksums[i] = BigEndian.ToUInt32(input, checksum_offset + i * 4);
                BitConverter.GetBytes((uint)0).CopyTo(input, checksum_offset + i * 4);
            }

            uint[] checksums = new uint[16];

            for (int i = 0; i < len; i += 2)
            {
                ushort val = BigEndian.ToUInt16(input, offset + i);
                for (int j = 0; j < 16; j++)
                {
                    checksums[j] += (uint)((val >> j) & 1);
                }
            }

            for (int i = 0; i < checksums.Length; i++)
            {
                BigEndian.GetBytes(checksums[i]).CopyTo(input, checksum_offset + i * 4);
            }
        }
Beispiel #4
0
        private static byte[] EncryptPBRSaveData(byte[] input)
        {
            byte[] output = new byte[input.Length];
            for (int base_ofs = 0; base_ofs < SaveUtil.SIZE_G4BR; base_ofs += 0x1C0000)
            {
                Array.Copy(input, base_ofs, output, base_ofs, 8);

                ushort[] keys = new ushort[4];
                for (int i = 0; i < keys.Length; i++)
                {
                    keys[i] = BigEndian.ToUInt16(input, base_ofs + i * 2);
                }

                for (int ofs = base_ofs + 8; ofs < base_ofs + 0x1C0000; ofs += 8)
                {
                    for (int i = 0; i < keys.Length; i++)
                    {
                        ushort val = BigEndian.ToUInt16(input, ofs + i * 2);
                        val += keys[i];
                        BigEndian.GetBytes(val).CopyTo(output, ofs + i * 2);
                    }
                    keys = SaveUtil.AdvanceGCKeys(keys);
                }
            }
            return(output);
        }
Beispiel #5
0
        /// <summary>Calculates the 32bit checksum over an input byte array. Used in GC R/S BOX.</summary>
        /// <param name="data">Input byte array</param>
        /// <returns>Checksum</returns>
        public static uint CheckSum16BigInvert(ReadOnlySpan <byte> data)
        {
            ushort chk = 0; // initial value

            while ((data.Length & ~1) != 0)
            {
                chk += BigEndian.ToUInt16(data);
                data = data[2..];
Beispiel #6
0
        protected override bool GetIsBoxChecksumValid(int i)
        {
            var       boxOfs = GetBoxOffset(i) - ListHeaderSize;
            const int size   = BoxSizeJ - 2;
            var       chk    = Checksums.CheckSum16(Data, boxOfs, size);
            var       actual = BigEndian.ToUInt16(Data, boxOfs + size);

            return(chk == actual);
        }
Beispiel #7
0
        protected override bool GetIsBoxChecksumValid(int box)
        {
            var boxOfs = GetBoxOffset(box) - ListHeaderSizeBox;
            var size   = BoxSize - 2;
            var chk    = Checksums.CheckSum16(new ReadOnlySpan <byte>(Data, boxOfs, size));
            var actual = BigEndian.ToUInt16(Data, boxOfs + size);

            return(chk == actual);
        }
Beispiel #8
0
        protected override ushort CalculateChecksum()
        {
            ushort chk = 0;

            for (int i = 8; i < SIZE_STORED; i += 2)
            {
                chk += BigEndian.ToUInt16(Data, i);
            }
            return(chk);
        }
Beispiel #9
0
        /// <summary>Calculates the 32bit checksum over an input byte array. Used in GC R/S BOX.</summary>
        /// <param name="data">Input byte array</param>
        /// <param name="start">Offset to start checksum at</param>
        /// <param name="end">Exclusive end offset to finish the checksum at</param>
        /// <returns>Checksum</returns>
        public static uint CheckSum16BigInvert(byte[] data, int start, int end)
        {
            ushort chk = 0; // initial value

            for (int i = start; i < end; i += 2)
            {
                chk += BigEndian.ToUInt16(data, i);
            }
            return((uint)(chk << 16 | (ushort)(0xF004 - chk)));
        }
Beispiel #10
0
        private static byte[] SetChecksums(byte[] input, int subOffset0)
        {
            if (input.Length != SLOT_SIZE)
            {
                throw new ArgumentException("Input should be a slot, not the entire save binary.");
            }

            byte[]    data  = (byte[])input.Clone();
            const int start = 0xA8; // 0x88 + 0x20

            // Header Checksum
            int newHC = 0;

            for (int i = 0; i < 8; i++)
            {
                newHC += data[i];
            }

            BigEndian.GetBytes(newHC).CopyTo(data, start + subOffset0 + 0x38);

            // Body Checksum
            data.AsSpan(0x10, 0x10).Fill(0); // Clear old Checksum Data
            uint[] checksum = new uint[4];
            int    dt       = 8;

            for (int i = 0; i < checksum.Length; i++)
            {
                uint val = 0;
                var  end = dt + 0x9FF4;
                for (int j = dt; j < end; j += 2)
                {
                    val += BigEndian.ToUInt16(data, j);
                }
                dt          = end;
                checksum[i] = val;
            }

            ushort[] newchks = new ushort[8];
            for (int i = 0; i < 4; i++)
            {
                newchks[i * 2]       = (ushort)(checksum[i] >> 16);
                newchks[(i * 2) + 1] = (ushort)checksum[i];
            }

            Array.Reverse(newchks);
            for (int i = 0; i < newchks.Length; i++)
            {
                BigEndian.GetBytes(newchks[i]).CopyTo(data, 0x10 + (2 * i));
            }

            return(data);
        }
Beispiel #11
0
        private void InitializeData(out StrategyMemo memo, out ShadowInfoTableXD info)
        {
            // Scan all 3 save slots for the highest counter
            for (int i = 0; i < SLOT_COUNT; i++)
            {
                int slotOffset  = SLOT_START + (i * SLOT_SIZE);
                int SaveCounter = BigEndian.ToInt32(Data, slotOffset + 4);
                if (SaveCounter <= SaveCount)
                {
                    continue;
                }

                SaveCount = SaveCounter;
                SaveIndex = i;
            }

            // Decrypt most recent save slot
            {
                byte[] slot       = new byte[SLOT_SIZE];
                int    slotOffset = SLOT_START + (SaveIndex * SLOT_SIZE);
                Array.Copy(Data, slotOffset, slot, 0, slot.Length);

                ushort[] keys = new ushort[4];
                for (int i = 0; i < keys.Length; i++)
                {
                    keys[i] = BigEndian.ToUInt16(slot, 8 + (i * 2));
                }

                // Decrypt Slot
                Data = GCSaveUtil.Decrypt(slot, 0x00010, 0x27FD8, keys);
            }

            // Get Offset Info
            ushort[] subLength = new ushort[16];
            for (int i = 0; i < 16; i++)
            {
                subLength[i]  = BigEndian.ToUInt16(Data, 0x20 + (2 * i));
                subOffsets[i] = BigEndian.ToUInt16(Data, 0x40 + (4 * i)) | BigEndian.ToUInt16(Data, 0x40 + (4 * i) + 2) << 16;
            }

            // Offsets are displaced by the 0xA8 savedata region
            Trainer1      = subOffsets[1] + 0xA8;
            Party         = Trainer1 + 0x30;
            Box           = subOffsets[2] + 0xA8;
            DaycareOffset = subOffsets[4] + 0xA8;
            Memo          = subOffsets[5] + 0xA8;
            Shadow        = subOffsets[7] + 0xA8;
            // Purifier = subOffsets[14] + 0xA8;

            memo = new StrategyMemo(Data, Memo, xd: true);
            info = new ShadowInfoTableXD(Data.Slice(Shadow, subLength[7]));
        }
Beispiel #12
0
 public void GetPouchBigEndian(ref byte[] Data)
 {
     InventoryItem[] items = new InventoryItem[PouchDataSize];
     for (int i = 0; i < items.Length; i++)
     {
         items[i] = new InventoryItem
         {
             Index = BigEndian.ToUInt16(Data, Offset + i * 4),
             Count = BigEndian.ToUInt16(Data, Offset + i * 4 + 2) ^ (ushort)SecurityKey
         };
     }
     Items = items;
 }
Beispiel #13
0
 public override void GetPouch(byte[] Data)
 {
     InventoryItem[] items = new InventoryItem[PouchDataSize];
     for (int i = 0; i < items.Length; i++)
     {
         items[i] = new InventoryItem
         {
             Index = BigEndian.ToUInt16(Data, Offset + (i * 4)),
             Count = BigEndian.ToUInt16(Data, Offset + (i * 4) + 2)
         };
     }
     Items = items;
 }
Beispiel #14
0
        public string GetTeamName(int team)
        {
            var name = $"{((Stadium2TeamType) (team / TeamCountType)).ToString().Replace('_', ' ')} {(team % 10) + 1}";

            var ofs = GetTeamOffset(team);
            var str = GetString(ofs + 4, 7);

            if (string.IsNullOrWhiteSpace(str))
            {
                return(name);
            }
            var id = BigEndian.ToUInt16(Data, ofs + 2);

            return($"{name} [{id:D5}:{str}]");
        }
Beispiel #15
0
 public static byte[] EncryptGC(byte[] input, int start, int end, ushort[] keys)
 {
     byte[] output = (byte[])input.Clone();
     for (int ofs = start; ofs < end; ofs += 8)
     {
         for (int i = 0; i < keys.Length; i++)
         {
             ushort val = BigEndian.ToUInt16(input, ofs + i * 2);
             val += keys[i];
             BigEndian.GetBytes(val).CopyTo(output, ofs + i * 2);
         }
         keys = AdvanceGCKeys(keys);
     }
     return(output);
 }
Beispiel #16
0
        public static int GetTeamOffset(int team) => 0 + (team * 2 * TeamSizeJ); // backups are after each team

        public string GetTeamName(int team)
        {
            var name = $"Team {team + 1}";

            var ofs = GetTeamOffset(team);
            var str = GetString(ofs + 2, 5);

            if (string.IsNullOrWhiteSpace(str))
            {
                return(name);
            }
            var id = BigEndian.ToUInt16(Data, ofs + 8);

            return($"{name} [{id:D5}:{str}]");
        }
Beispiel #17
0
        private static byte[] SetChecksums(byte[] input, int subOffset0)
        {
            if (input.Length != 0x28000)
            {
                throw new ArgumentException("Input should be a slot, not the entire save binary.");
            }

            byte[]    data  = (byte[])input.Clone();
            const int start = 0xA8; // 0x88 + 0x20

            // Header Checksum
            int newHC = 0;

            for (int i = 0; i < 8; i++)
            {
                newHC += data[i];
            }

            BigEndian.GetBytes(newHC).CopyTo(data, start + subOffset0 + 0x38);

            // Body Checksum
            new byte[16].CopyTo(data, 0x10); // Clear old Checksum Data
            uint[] checksum = new uint[4];
            int    dt       = 8;

            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 0x9FF4; j += 2, dt += 2)
                {
                    checksum[i] += BigEndian.ToUInt16(data, dt);
                }
            }

            ushort[] newchks = new ushort[8];
            for (int i = 0; i < 4; i++)
            {
                newchks[i * 2]       = (ushort)(checksum[i] >> 16);
                newchks[(i * 2) + 1] = (ushort)checksum[i];
            }

            Array.Reverse(newchks);
            for (int i = 0; i < newchks.Length; i++)
            {
                BigEndian.GetBytes(newchks[i]).CopyTo(data, 0x10 + (2 * i));
            }

            return(data);
        }
Beispiel #18
0
        public StrategyMemo(byte[] input, int offset, bool xd)
        {
            XD = xd;
            int count = BigEndian.ToInt16(input, offset);

            if (count > MAX_COUNT)
            {
                count = MAX_COUNT;
            }
            _unk = BigEndian.ToUInt16(input, offset + 2);

            Entries = new List <StrategyMemoEntry>(count);
            for (int i = 0; i < count; i++)
            {
                var entry = Read(input, offset, i);
                Entries.Add(entry);
            }
        }
Beispiel #19
0
        private byte[] GetInnerData()
        {
            // Set Memo Back
            StrategyMemo.Write().CopyTo(Data, Memo);
            ShadowInfo.Write().CopyTo(Data, Shadow);
            SetChecksums();

            // Get updated save slot data
            ushort[] keys = new ushort[4];
            for (int i = 0; i < keys.Length; i++)
            {
                keys[i] = BigEndian.ToUInt16(Data, 8 + (i * 2));
            }
            byte[] newSAV = GCSaveUtil.Encrypt(Data, 0x10, 0x27FD8, keys);

            // Put save slot back in original save data
            byte[] newFile = MC != null ? MC.SelectedSaveData : (byte[])BAK.Clone();
            Array.Copy(newSAV, 0, newFile, SLOT_START + (SaveIndex * SLOT_SIZE), newSAV.Length);
            return(newFile);
        }
Beispiel #20
0
        public override byte[] Write(bool DSV)
        {
            // Set Memo Back
            StrategyMemo.FinalData.CopyTo(Data, Memo);
            ShadowInfo.FinalData.CopyTo(Data, Shadow);
            setChecksums();

            // Get updated save slot data
            ushort[] keys = new ushort[4];
            for (int i = 0; i < keys.Length; i++)
            {
                keys[i] = BigEndian.ToUInt16(Data, 8 + i * 2);
            }
            byte[] newSAV = SaveUtil.EncryptGC(Data, 0x10, 0x27FD8, keys);

            // Put save slot back in original save data
            byte[] newFile = (byte[])OriginalData.Clone();
            Array.Copy(newSAV, 0, newFile, SLOT_START + SaveIndex * SLOT_SIZE, newSAV.Length);
            return(Header.Concat(newFile).ToArray());
        }
Beispiel #21
0
        // Checksums
        private void GetChecksum(int block, int offset, int length, out ushort csum, out ushort inv_csum)
        {
            csum = inv_csum = 0;
            var ofs = block * BLOCK_SIZE + offset;

            for (int i = 0; i < length; i++)
            {
                var val = BigEndian.ToUInt16(Data, ofs + i * 2);
                csum     += val;
                inv_csum += (ushort)(val ^ 0xffff);
            }
            if (csum == 0xffff)
            {
                csum = 0;
            }
            if (inv_csum == 0xffff)
            {
                inv_csum = 0;
            }
        }
Beispiel #22
0
        public SAV3XD(byte[] data = null)
        {
            Data       = data ?? new byte[SaveUtil.SIZE_G3XD];
            BAK        = (byte[])Data.Clone();
            Exportable = !IsRangeEmpty(0, Data.Length);

            if (SaveUtil.GetIsG3XDSAV(Data) != GameVersion.XD)
            {
                return;
            }

            // Scan all 3 save slots for the highest counter
            for (int i = 0; i < SLOT_COUNT; i++)
            {
                int slotOffset  = SLOT_START + (i * SLOT_SIZE);
                int SaveCounter = BigEndian.ToInt32(Data, slotOffset + 4);
                if (SaveCounter <= SaveCount)
                {
                    continue;
                }

                SaveCount = SaveCounter;
                SaveIndex = i;
            }

            // Decrypt most recent save slot
            {
                byte[] slot       = new byte[SLOT_SIZE];
                int    slotOffset = SLOT_START + (SaveIndex * SLOT_SIZE);
                Array.Copy(Data, slotOffset, slot, 0, slot.Length);

                ushort[] keys = new ushort[4];
                for (int i = 0; i < keys.Length; i++)
                {
                    keys[i] = BigEndian.ToUInt16(slot, 8 + (i * 2));
                }

                // Decrypt Slot
                Data = SaveUtil.DecryptGC(slot, 0x00010, 0x27FD8, keys);
            }

            // Get Offset Info
            ushort[] subLength = new ushort[16];
            for (int i = 0; i < 16; i++)
            {
                subLength[i]  = BigEndian.ToUInt16(Data, 0x20 + (2 * i));
                subOffsets[i] = BigEndian.ToUInt16(Data, 0x40 + (4 * i)) | BigEndian.ToUInt16(Data, 0x40 + (4 * i) + 2) << 16;
            }
            // Offsets are displaced by the 0xA8 savedata region
            Trainer1 = subOffsets[1] + 0xA8;
            Party    = Trainer1 + 0x30;
            Box      = subOffsets[2] + 0xA8;
            Daycare  = subOffsets[4] + 0xA8;
            Memo     = subOffsets[5] + 0xA8;
            Shadow   = subOffsets[7] + 0xA8;
            // Purifier = subOffsets[14] + 0xA8;

            StrategyMemo = new StrategyMemo(Data, Memo, xd: true);
            ShadowInfo   = new ShadowInfoTableXD(Data.Skip(Shadow).Take(subLength[7]).ToArray());

            OFS_PouchHeldItem = Trainer1 + 0x4C8;
            OFS_PouchKeyItem  = Trainer1 + 0x540;
            OFS_PouchBalls    = Trainer1 + 0x5EC;
            OFS_PouchTMHM     = Trainer1 + 0x62C;
            OFS_PouchBerry    = Trainer1 + 0x72C;
            OFS_PouchCologne  = Trainer1 + 0x7E4;
            OFS_PouchDisc     = Trainer1 + 0x7F0;

            LegalItems    = Legal.Pouch_Items_XD;
            LegalKeyItems = Legal.Pouch_Key_XD;
            LegalBalls    = Legal.Pouch_Ball_RS;
            LegalTMHMs    = Legal.Pouch_TM_RS; // not HMs
            LegalBerries  = Legal.Pouch_Berries_RS;
            LegalCologne  = Legal.Pouch_Cologne_XD;
            LegalDisc     = Legal.Pouch_Disc_XD;

            Personal  = PersonalTable.RS;
            HeldItems = Legal.HeldItems_XD;

            if (!Exportable)
            {
                ClearBoxes();
            }

            // Since PartyCount is not stored in the save file,
            // Count up how many party slots are active.
            for (int i = 0; i < 6; i++)
            {
                if (GetPartySlot(GetPartyOffset(i)).Species != 0)
                {
                    PartyCount++;
                }
            }
        }
Beispiel #23
0
        public GCMemoryCardState LoadMemoryCardFile(byte[] data)
        {
            Data = data;
            if (!IsMemoryCardSize(Data))
            {
                // Invalid size
                return(GCMemoryCardState.Invalid);
            }

            // Size in megabits, not megabytes
            int m_sizeMb = Data.Length / BLOCK_SIZE / MBIT_TO_BLOCKS;

            if (m_sizeMb != Header_Size) // Memory card file size does not match the header size
            {
                return(GCMemoryCardState.Invalid);
            }

            if (IsCorruptedMemoryCard())
            {
                return(GCMemoryCardState.Invalid);
            }

            // Use the most recent directory block
            DirectoryBlock_Used = DirectoryBAK_UpdateCounter > Directory_UpdateCounter
                ? DirectoryBackup_Block
                : Directory_Block;

            string Empty_DEntry = EncodingType.GetString(RawEmpty_DEntry, 0, 4);

            // Search for pokemon savegames in the directory
            for (int i = 0; i < NumEntries_Directory; i++)
            {
                int    offset   = DirectoryBlock_Used * BLOCK_SIZE + i * DENTRY_SIZE;
                string GameCode = EncodingType.GetString(Data, offset, 4);
                if (GameCode == Empty_DEntry)
                {
                    continue;
                }

                int FirstBlock = BigEndian.ToUInt16(Data, offset + 0x36);
                int BlockCount = BigEndian.ToUInt16(Data, offset + 0x38);

                // Memory card directory contains info for deleted files with boundaries beyond memory card size, ignore
                if (FirstBlock + BlockCount > NumBlocks)
                {
                    continue;
                }

                if (SaveUtil.HEADER_COLO.Contains(GameCode))
                {
                    if (EntryCOLO > -1) // another entry already exists
                    {
                        return(GCMemoryCardState.DuplicateCOLO);
                    }
                    EntryCOLO = i;
                    SaveGameCount++;
                }
                if (SaveUtil.HEADER_XD.Contains(GameCode))
                {
                    if (EntryXD > -1) // another entry already exists
                    {
                        return(GCMemoryCardState.DuplicateXD);
                    }
                    EntryXD = i;
                    SaveGameCount++;
                }
                if (SaveUtil.HEADER_RSBOX.Contains(GameCode))
                {
                    if (EntryRSBOX > -1) // another entry already exists
                    {
                        return(GCMemoryCardState.DuplicateRSBOX);
                    }
                    EntryRSBOX = i;
                    SaveGameCount++;
                }
            }
            if (SaveGameCount == 0)
            {
                return(GCMemoryCardState.NoPkmSaveGame);
            }

            if (SaveGameCount > 1)
            {
                return(GCMemoryCardState.MultipleSaveGame);
            }

            if (EntryCOLO > -1)
            {
                EntrySelected = EntryCOLO;
                return(GCMemoryCardState.SaveGameCOLO);
            }
            if (EntryXD > -1)
            {
                EntrySelected = EntryXD;
                return(GCMemoryCardState.SaveGameXD);
            }
            EntrySelected = EntryRSBOX;
            return(GCMemoryCardState.SaveGameRSBOX);
        }