Example #1
0
        public SAV3Colosseum(byte[] data = null)
        {
            Data       = data == null ? new byte[SaveUtil.SIZE_G3COLO] : (byte[])data.Clone();
            BAK        = (byte[])Data.Clone();
            Exportable = !Data.SequenceEqual(new byte[Data.Length]);

            if (SaveUtil.getIsG3COLOSAV(Data) != GameVersion.COLO)
            {
                return;
            }

            OriginalData = (byte[])Data.Clone();

            // 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);
                byte[] digest = new byte[20];
                Array.Copy(slot, SLOT_SIZE - 20, digest, 0, digest.Length);

                // Decrypt Slot
                Data = DecryptColosseum(slot, digest);
            }

            Trainer1          = 0x00078;
            Party             = 0x000A8;
            OFS_PouchHeldItem = 0x007F8;
            OFS_PouchKeyItem  = 0x00848;
            OFS_PouchBalls    = 0x008F4;
            OFS_PouchTMHM     = 0x00934;
            OFS_PouchBerry    = 0x00A34;
            OFS_PouchCologne  = 0x00AEC; // Cologne

            Box          = 0x00B90;
            Daycare      = 0x08170;
            Memo         = 0x082B0;
            StrategyMemo = new StrategyMemo(Data, Memo, xd: false);

            LegalItems    = Legal.Pouch_Items_COLO;
            LegalKeyItems = Legal.Pouch_Key_COLO;
            LegalBalls    = Legal.Pouch_Ball_RS;
            LegalTMHMs    = Legal.Pouch_TM_RS; // not HMs
            LegalBerries  = Legal.Pouch_Berries_RS;
            LegalCologne  = Legal.Pouch_Cologne_CXD;

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

            if (!Exportable)
            {
                resetBoxes();
            }

            // 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++;
                }
            }
        }
Example #2
0
        public SAV2(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
        {
            Data       = data ?? new byte[SaveUtil.SIZE_G2RAW_U];
            BAK        = (byte[])Data.Clone();
            Exportable = !IsRangeEmpty(0, Data.Length);

            if (data == null)
            {
                Version = GameVersion.C;
            }
            else if (versionOverride != GameVersion.Any)
            {
                Version = versionOverride;
            }
            else
            {
                Version = SaveUtil.GetIsG2SAV(Data);
            }

            if (Version == GameVersion.Invalid)
            {
                return;
            }

            Japanese = SaveUtil.GetIsG2SAVJ(Data) != GameVersion.Invalid;
            if (!Japanese)
            {
                Korean = SaveUtil.GetIsG2SAVK(Data) != GameVersion.Invalid;
            }

            Box = Data.Length;
            Array.Resize(ref Data, Data.Length + SIZE_RESERVED);
            Party = GetPartyOffset(0);

            Personal = Version == GameVersion.GS ? PersonalTable.GS : PersonalTable.C;

            Offsets = new SAV2Offsets(this);

            LegalItems    = Legal.Pouch_Items_GSC;
            LegalBalls    = Legal.Pouch_Ball_GSC;
            LegalKeyItems = Version == GameVersion.C ? Legal.Pouch_Key_C : Legal.Pouch_Key_GS;
            LegalTMHMs    = Legal.Pouch_TMHM_GSC;
            HeldItems     = Legal.HeldItems_GSC;

            // Stash boxes after the save file's end.
            int splitAtIndex = (Japanese ? 6 : 7);
            int stored       = SIZE_STOREDBOX;
            int baseDest     = Data.Length - SIZE_RESERVED;
            var capacity     = Japanese ? PokeListType.StoredJP : PokeListType.Stored;

            for (int i = 0; i < BoxCount; i++)
            {
                int ofs     = GetBoxRawDataOffset(i, splitAtIndex);
                var box     = GetData(ofs, stored);
                var boxDest = baseDest + (i * SIZE_BOX);
                var boxPL   = new PokeList2(box, capacity, Japanese);
                for (int j = 0; j < boxPL.Pokemon.Length; j++)
                {
                    var dest  = boxDest + (j * SIZE_STORED);
                    var pkDat = (j < boxPL.Count)
                        ? new PokeList2(boxPL[j]).Write()
                        : new byte[PokeList2.GetDataLength(PokeListType.Single, Japanese)];
                    pkDat.CopyTo(Data, dest);
                }
            }

            var current  = GetData(Offsets.CurrentBox, stored);
            var curBoxPL = new PokeList2(current, capacity, Japanese);
            var curDest  = baseDest + (CurrentBox * SIZE_BOX);

            for (int i = 0; i < curBoxPL.Pokemon.Length; i++)
            {
                var dest  = curDest + (i * SIZE_STORED);
                var pkDat = i < curBoxPL.Count
                    ? new PokeList2(curBoxPL[i]).Write()
                    : new byte[PokeList2.GetDataLength(PokeListType.Single, Japanese)];
                pkDat.CopyTo(Data, dest);
            }

            var party   = GetData(Offsets.Party, SIZE_STOREDPARTY);
            var partyPL = new PokeList2(party, PokeListType.Party, Japanese);

            for (int i = 0; i < partyPL.Pokemon.Length; i++)
            {
                var dest  = GetPartyOffset(i);
                var pkDat = i < partyPL.Count
                    ? new PokeList2(partyPL[i]).Write()
                    : new byte[PokeList2.GetDataLength(PokeListType.Single, Japanese)];
                pkDat.CopyTo(Data, dest);
            }

            if (Offsets.Daycare >= 0)
            {
                int offset = Offsets.Daycare;

                DaycareFlags[0] = Data[offset]; offset++;
                var pk1      = ReadPKMFromOffset(offset);    // parent 1
                var daycare1 = new PokeList2(pk1);
                offset         += (StringLength * 2) + 0x20; // nick/ot/pkm
                DaycareFlags[1] = Data[offset]; offset++;
                byte steps = Data[offset]; offset++;
                byte BreedMotherOrNonDitto = Data[offset]; offset++;
                var  pk2      = ReadPKMFromOffset(offset);       // parent 2
                var  daycare2 = new PokeList2(pk2);
                offset += (StringLength * 2) + PKX.SIZE_2STORED; // nick/ot/pkm
                var pk3 = ReadPKMFromOffset(offset);             // egg!
                pk3.IsEgg = true;
                var daycare3 = new PokeList2(pk3);

                daycare1.Write().CopyTo(Data, GetPartyOffset(7 + (0 * 2)));
                daycare2.Write().CopyTo(Data, GetPartyOffset(7 + (1 * 2)));
                daycare3.Write().CopyTo(Data, GetPartyOffset(7 + (2 * 2)));
                Daycare = Offsets.Daycare;
            }

            // Enable Pokedex editing
            PokeDex    = 0;
            EventFlag  = Offsets.EventFlag;
            EventConst = Offsets.EventConst;

            if (!Exportable)
            {
                ClearBoxes();
            }
        }
Example #3
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++;
                }
            }
        }
Example #4
0
        public SAV1(byte[] data = null)
        {
            Data       = data == null ? new byte[SaveUtil.SIZE_G1RAW] : (byte[])data.Clone();
            BAK        = (byte[])Data.Clone();
            Exportable = !Data.SequenceEqual(new byte[Data.Length]);

            Version = data == null ? GameVersion.RBY : SaveUtil.getIsG1SAV(Data);
            if (Version == GameVersion.Invalid)
            {
                return;
            }

            Box = Data.Length;
            Array.Resize(ref Data, Data.Length + SIZE_RESERVED);
            Party = getPartyOffset(0);

            Japanese = SaveUtil.getIsG1SAVJ(data);
            Personal = PersonalTable.RBY;

            // Stash boxes after the save file's end.
            byte[] TempBox = new byte[SIZE_STOREDBOX];
            for (int i = 0; i < BoxCount; i++)
            {
                if (i < BoxCount / 2)
                {
                    Array.Copy(Data, 0x4000 + i * TempBox.Length, TempBox, 0, TempBox.Length);
                }
                else
                {
                    Array.Copy(Data, 0x6000 + (i - BoxCount / 2) * TempBox.Length, TempBox, 0, TempBox.Length);
                }
                PokemonList1 PL1 = new PokemonList1(TempBox, Japanese ? PokemonList1.CapacityType.StoredJP : PokemonList1.CapacityType.Stored, Japanese);
                for (int j = 0; j < PL1.Pokemon.Length; j++)
                {
                    if (j < PL1.Count)
                    {
                        byte[] pkDat = new PokemonList1(PL1[j]).GetBytes();
                        pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + i * SIZE_BOX + j * SIZE_STORED);
                    }
                    else
                    {
                        byte[] pkDat = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Single, Japanese)];
                        pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + i * SIZE_BOX + j * SIZE_STORED);
                    }
                }
            }

            Array.Copy(Data, Japanese ? 0x302D : 0x30C0, TempBox, 0, TempBox.Length);
            PokemonList1 curBoxPL = new PokemonList1(TempBox, Japanese ? PokemonList1.CapacityType.StoredJP : PokemonList1.CapacityType.Stored, Japanese);

            for (int i = 0; i < curBoxPL.Pokemon.Length; i++)
            {
                if (i < curBoxPL.Count)
                {
                    byte[] pkDat = new PokemonList1(curBoxPL[i]).GetBytes();
                    pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + CurrentBox * SIZE_BOX + i * SIZE_STORED);
                }
                else
                {
                    byte[] pkDat = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Single, Japanese)];
                    pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + CurrentBox * SIZE_BOX + i * SIZE_STORED);
                }
            }

            byte[] TempParty = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Party, Japanese)];
            Array.Copy(Data, Japanese ? 0x2ED5 : 0x2F2C, TempParty, 0, TempParty.Length);
            PokemonList1 partyList = new PokemonList1(TempParty, PokemonList1.CapacityType.Party, Japanese);

            for (int i = 0; i < partyList.Pokemon.Length; i++)
            {
                if (i < partyList.Count)
                {
                    byte[] pkDat = new PokemonList1(partyList[i]).GetBytes();
                    pkDat.CopyTo(Data, getPartyOffset(i));
                }
                else
                {
                    byte[] pkDat = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Single, Japanese)];
                    pkDat.CopyTo(Data, getPartyOffset(i));
                }
            }

            byte[] rawDC = new byte[0x38];
            Array.Copy(Data, Japanese ? 0x2CA7 : 0x2CF4, rawDC, 0, rawDC.Length);
            byte[] TempDaycare = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Single, Japanese)];
            TempDaycare[0] = rawDC[0];
            Array.Copy(rawDC, 1, TempDaycare, 2 + 1 + PKX.SIZE_1PARTY + StringLength, StringLength);
            Array.Copy(rawDC, 1 + StringLength, TempDaycare, 2 + 1 + PKX.SIZE_1PARTY, StringLength);
            Array.Copy(rawDC, 1 + 2 * StringLength, TempDaycare, 2 + 1, PKX.SIZE_1STORED);
            PokemonList1 daycareList = new PokemonList1(TempDaycare, PokemonList1.CapacityType.Single, Japanese);

            daycareList.GetBytes().CopyTo(Data, getPartyOffset(7));
            Daycare = getPartyOffset(7);

            // Enable Pokedex editing
            PokeDex = 0;

            if (!Exportable)
            {
                resetBoxes();
            }
        }
Example #5
0
        public SAV2(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
        {
            Data       = data == null ? new byte[SaveUtil.SIZE_G2RAW_U] : (byte[])data.Clone();
            BAK        = (byte[])Data.Clone();
            Exportable = !Data.SequenceEqual(new byte[Data.Length]);

            if (data == null)
            {
                Version = GameVersion.C;
            }
            else if (versionOverride != GameVersion.Any)
            {
                Version = versionOverride;
            }
            else
            {
                Version = SaveUtil.getIsG2SAV(Data);
            }

            if (Version == GameVersion.Invalid)
            {
                return;
            }

            Japanese = SaveUtil.getIsG2SAVJ(Data) != GameVersion.Invalid;
            if (Japanese && Data.Length < SaveUtil.SIZE_G2RAW_J)
            {
                Array.Resize(ref Data, SaveUtil.SIZE_G2RAW_J);
            }

            Box = Data.Length;
            Array.Resize(ref Data, Data.Length + SIZE_RESERVED);
            Party = getPartyOffset(0);

            Personal = Version == GameVersion.GS ? PersonalTable.GS : PersonalTable.C;

            getSAVOffsets();

            LegalItems    = Legal.Pouch_Items_GSC;
            LegalBalls    = Legal.Pouch_Ball_GSC;
            LegalKeyItems = Version == GameVersion.C ? Legal.Pouch_Key_C : Legal.Pouch_Key_GS;
            LegalTMHMs    = Legal.Pouch_TMHM_GSC;
            HeldItems     = Legal.HeldItems_GSC;

            // Stash boxes after the save file's end.
            byte[] TempBox = new byte[SIZE_STOREDBOX];
            for (int i = 0; i < BoxCount; i++)
            {
                if (i < (Japanese ? 6 : 7))
                {
                    Array.Copy(Data, 0x4000 + i * (TempBox.Length + 2), TempBox, 0, TempBox.Length);
                }
                else
                {
                    Array.Copy(Data, 0x6000 + (i - (Japanese ? 6 : 7)) * (TempBox.Length + 2), TempBox, 0, TempBox.Length);
                }
                PokemonList2 PL2 = new PokemonList2(TempBox, Japanese ? PokemonList2.CapacityType.StoredJP : PokemonList2.CapacityType.Stored, Japanese);
                for (int j = 0; j < PL2.Pokemon.Length; j++)
                {
                    if (j < PL2.Count)
                    {
                        byte[] pkDat = new PokemonList2(PL2[j]).GetBytes();
                        pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + i * SIZE_BOX + j * SIZE_STORED);
                    }
                    else
                    {
                        byte[] pkDat = new byte[PokemonList2.GetDataLength(PokemonList2.CapacityType.Single, Japanese)];
                        pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + i * SIZE_BOX + j * SIZE_STORED);
                    }
                }
            }

            Array.Copy(Data, CurrentBoxOffset, TempBox, 0, TempBox.Length);
            PokemonList2 curBoxPL = new PokemonList2(TempBox, Japanese ? PokemonList2.CapacityType.StoredJP : PokemonList2.CapacityType.Stored, Japanese);

            for (int i = 0; i < curBoxPL.Pokemon.Length; i++)
            {
                if (i < curBoxPL.Count)
                {
                    byte[] pkDat = new PokemonList2(curBoxPL[i]).GetBytes();
                    pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + CurrentBox * SIZE_BOX + i * SIZE_STORED);
                }
                else
                {
                    byte[] pkDat = new byte[PokemonList2.GetDataLength(PokemonList2.CapacityType.Single, Japanese)];
                    pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + CurrentBox * SIZE_BOX + i * SIZE_STORED);
                }
            }

            byte[] TempParty = new byte[PokemonList2.GetDataLength(PokemonList2.CapacityType.Party, Japanese)];
            Array.Copy(Data, PartyOffset, TempParty, 0, TempParty.Length);
            PokemonList2 partyList = new PokemonList2(TempParty, PokemonList2.CapacityType.Party, Japanese);

            for (int i = 0; i < partyList.Pokemon.Length; i++)
            {
                if (i < partyList.Count)
                {
                    byte[] pkDat = new PokemonList2(partyList[i]).GetBytes();
                    pkDat.CopyTo(Data, getPartyOffset(i));
                }
                else
                {
                    byte[] pkDat = new byte[PokemonList2.GetDataLength(PokemonList2.CapacityType.Single, Japanese)];
                    pkDat.CopyTo(Data, getPartyOffset(i));
                }
            }

            // Daycare currently undocumented for all Gen II games.

            // Enable Pokedex editing
            PokeDex = 0;

            if (!Exportable)
            {
                resetBoxes();
            }
        }
Example #6
0
        public SAV1(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
        {
            Data       = data ?? new byte[SaveUtil.SIZE_G1RAW];
            BAK        = (byte[])Data.Clone();
            Exportable = !IsRangeEmpty(0, Data.Length);

            if (versionOverride != GameVersion.Any)
            {
                Version = versionOverride;
            }
            else if (data == null)
            {
                Version = GameVersion.RBY;
            }
            else
            {
                Version = SaveUtil.GetIsG1SAV(Data);
            }
            if (Version == GameVersion.Invalid)
            {
                return;
            }

            Japanese = SaveUtil.GetIsG1SAVJ(Data);
            Offsets  = Japanese ? SAV1Offsets.JPN : SAV1Offsets.INT;

            // see if RBY can be differentiated
            if (Starter != 0 && versionOverride != GameVersion.Any)
            {
                Version = Yellow ? GameVersion.YW : GameVersion.RB;
            }

            Box = Data.Length;
            Array.Resize(ref Data, Data.Length + SIZE_RESERVED);
            Party = GetPartyOffset(0);

            Personal = Version == GameVersion.Y ? PersonalTable.Y : PersonalTable.RB;

            // Stash boxes after the save file's end.
            int stored   = SIZE_STOREDBOX;
            int baseDest = Data.Length - SIZE_RESERVED;
            var capacity = Japanese ? PokeListType.StoredJP : PokeListType.Stored;

            for (int i = 0; i < BoxCount; i++)
            {
                int ofs     = GetBoxRawDataOffset(i);
                var box     = GetData(ofs, stored);
                var boxDest = baseDest + (i * SIZE_BOX);
                var boxPL   = new PokeList1(box, capacity, Japanese);
                for (int j = 0; j < boxPL.Pokemon.Length; j++)
                {
                    var dest  = boxDest + (j * SIZE_STORED);
                    var pkDat = (j < boxPL.Count)
                        ? new PokeList1(boxPL[j]).Write()
                        : new byte[PokeList1.GetDataLength(PokeListType.Single, Japanese)];
                    pkDat.CopyTo(Data, dest);
                }
            }

            var current  = GetData(Offsets.CurrentBox, SIZE_STOREDBOX);
            var curBoxPL = new PokeList1(current, capacity, Japanese);

            for (int i = 0; i < curBoxPL.Pokemon.Length; i++)
            {
                var dest  = Data.Length - SIZE_RESERVED + (CurrentBox * SIZE_BOX) + (i * SIZE_STORED);
                var pkDat = i < curBoxPL.Count
                    ? new PokeList1(curBoxPL[i]).Write()
                    : new byte[PokeList1.GetDataLength(PokeListType.Single, Japanese)];
                pkDat.CopyTo(Data, dest);
            }

            var party   = GetData(Offsets.Party, SIZE_STOREDPARTY);
            var partyPL = new PokeList1(party, PokeListType.Party, Japanese);

            for (int i = 0; i < partyPL.Pokemon.Length; i++)
            {
                var dest  = GetPartyOffset(i);
                var pkDat = i < partyPL.Count
                    ? new PokeList1(partyPL[i]).Write()
                    : new byte[PokeList1.GetDataLength(PokeListType.Single, Japanese)];
                pkDat.CopyTo(Data, dest);
            }

            byte[] rawDC = new byte[0x38];
            Array.Copy(Data, Offsets.Daycare, rawDC, 0, rawDC.Length);
            byte[] TempDaycare = new byte[PokeList1.GetDataLength(PokeListType.Single, Japanese)];
            TempDaycare[0] = rawDC[0];
            Array.Copy(rawDC, 1, TempDaycare, 2 + 1 + PKX.SIZE_1PARTY + StringLength, StringLength);
            Array.Copy(rawDC, 1 + StringLength, TempDaycare, 2 + 1 + PKX.SIZE_1PARTY, StringLength);
            Array.Copy(rawDC, 1 + (2 * StringLength), TempDaycare, 2 + 1, PKX.SIZE_1STORED);
            PokeList1 daycareList = new PokeList1(TempDaycare, PokeListType.Single, Japanese);

            daycareList.Write().CopyTo(Data, GetPartyOffset(7));
            Daycare = GetPartyOffset(7);

            EventFlag = Offsets.EventFlag;

            // Enable Pokedex editing
            PokeDex = 0;

            if (!Exportable)
            {
                ClearBoxes();
            }
        }
Example #7
0
        public SAV5(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
        {
            Data       = data ?? new byte[SaveUtil.SIZE_G5RAW];
            BAK        = (byte[])Data.Clone();
            Exportable = !IsRangeEmpty(0, Data.Length);

            // Get Version
            if (data == null)
            {
                Version = GameVersion.B2W2;
            }
            else if (versionOverride != GameVersion.Any)
            {
                Version = versionOverride;
            }
            else
            {
                Version = SaveUtil.GetIsG5SAV(Data);
            }
            if (Version == GameVersion.Invalid)
            {
                return;
            }

            // First blocks are always the same position/size
            PCLayout       = 0x0;
            Box            = 0x400;
            Party          = 0x18E00;
            Trainer1       = 0x19400;
            WondercardData = 0x1C800;
            AdventureInfo  = 0x1D900;

            // Different Offsets for later blocks
            switch (Version)
            {
            case GameVersion.BW:
                BattleBox            = 0x20A00;
                Trainer2             = 0x21200;
                EventConst           = 0x20100;
                EventFlag            = EventConst + 0x27C;
                Daycare              = 0x20E00;
                PokeDex              = 0x21600;
                PokeDexLanguageFlags = PokeDex + 0x320;
                BattleSubway         = 0x21D00;
                CGearInfoOffset      = 0x1C000;
                CGearDataOffset      = 0x52000;
                EntreeForestOffset   = 0x22C00;

                // Inventory offsets are the same for each game.
                OFS_PouchHeldItem = 0x18400; // 0x188D7
                OFS_PouchKeyItem  = 0x188D8; // 0x18A23
                OFS_PouchTMHM     = 0x18A24; // 0x18BD7
                OFS_PouchMedicine = 0x18BD8; // 0x18C97
                OFS_PouchBerry    = 0x18C98; // 0x18DBF
                LegalItems        = Legal.Pouch_Items_BW;
                LegalKeyItems     = Legal.Pouch_Key_BW;
                LegalTMHMs        = Legal.Pouch_TMHM_BW;
                LegalMedicine     = Legal.Pouch_Medicine_BW;
                LegalBerries      = Legal.Pouch_Berries_BW;

                Personal = PersonalTable.BW;
                break;

            case GameVersion.B2W2:     // B2W2
                BattleBox            = 0x20900;
                Trainer2             = 0x21100;
                EventConst           = 0x1FF00;
                EventFlag            = EventConst + 0x35E;
                Daycare              = 0x20D00;
                PokeDex              = 0x21400;
                PokeDexLanguageFlags = PokeDex + 0x328;     // forme flags size is + 8 from bw with new formes (therians)
                BattleSubway         = 0x21B00;
                CGearInfoOffset      = 0x1C000;
                CGearDataOffset      = 0x52800;
                EntreeForestOffset   = 0x22A00;

                // Inventory offsets are the same for each game.
                OFS_PouchHeldItem = 0x18400; // 0x188D7
                OFS_PouchKeyItem  = 0x188D8; // 0x18A23
                OFS_PouchTMHM     = 0x18A24; // 0x18BD7
                OFS_PouchMedicine = 0x18BD8; // 0x18C97
                OFS_PouchBerry    = 0x18C98; // 0x18DBF
                LegalItems        = Legal.Pouch_Items_BW;
                LegalKeyItems     = Legal.Pouch_Key_B2W2;
                LegalTMHMs        = Legal.Pouch_TMHM_BW;
                LegalMedicine     = Legal.Pouch_Medicine_BW;
                LegalBerries      = Legal.Pouch_Berries_BW;

                Personal = PersonalTable.B2W2;
                break;
            }
            HeldItems = Legal.HeldItems_BW;
            Blocks    = Version == GameVersion.BW ? BlockInfoNDS.BlocksBW : BlockInfoNDS.BlocksB2W2;

            if (!Exportable)
            {
                ClearBoxes();
            }
        }
Example #8
0
        public SAV2(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
        {
            Data       = data ?? new byte[SaveUtil.SIZE_G2RAW_U];
            BAK        = (byte[])Data.Clone();
            Exportable = !IsRangeEmpty(0, Data.Length);

            if (data == null)
            {
                Version = GameVersion.C;
            }
            else if (versionOverride != GameVersion.Any)
            {
                Version = versionOverride;
            }
            else
            {
                Version = SaveUtil.GetIsG2SAV(Data);
            }

            if (Version == GameVersion.Invalid)
            {
                return;
            }

            Japanese = SaveUtil.GetIsG2SAVJ(Data) != GameVersion.Invalid;
            if (!Japanese)
            {
                Korean = SaveUtil.GetIsG2SAVK(Data) != GameVersion.Invalid;
            }

            Box = Data.Length;
            Array.Resize(ref Data, Data.Length + SIZE_RESERVED);
            Party = GetPartyOffset(0);

            Personal = Version == GameVersion.GS ? PersonalTable.GS : PersonalTable.C;

            Offsets = new SAV2Offsets(this);

            LegalItems    = Legal.Pouch_Items_GSC;
            LegalBalls    = Legal.Pouch_Ball_GSC;
            LegalKeyItems = Version == GameVersion.C ? Legal.Pouch_Key_C : Legal.Pouch_Key_GS;
            LegalTMHMs    = Legal.Pouch_TMHM_GSC;
            HeldItems     = Legal.HeldItems_GSC;

            // Stash boxes after the save file's end.
            byte[] TempBox = new byte[SIZE_STOREDBOX];
            for (int i = 0; i < BoxCount; i++)
            {
                if (i < (Japanese ? 6 : 7))
                {
                    Array.Copy(Data, 0x4000 + i * (TempBox.Length + 2), TempBox, 0, TempBox.Length);
                }
                else
                {
                    Array.Copy(Data, 0x6000 + (i - (Japanese ? 6 : 7)) * (TempBox.Length + 2), TempBox, 0, TempBox.Length);
                }
                PokemonList2 PL2 = new PokemonList2(TempBox, Japanese ? PokemonList2.CapacityType.StoredJP : PokemonList2.CapacityType.Stored, Japanese);
                for (int j = 0; j < PL2.Pokemon.Length; j++)
                {
                    if (j < PL2.Count)
                    {
                        byte[] pkDat = new PokemonList2(PL2[j]).GetBytes();
                        pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + i * SIZE_BOX + j * SIZE_STORED);
                    }
                    else
                    {
                        byte[] pkDat = new byte[PokemonList2.GetDataLength(PokemonList2.CapacityType.Single, Japanese)];
                        pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + i * SIZE_BOX + j * SIZE_STORED);
                    }
                }
            }

            Array.Copy(Data, Offsets.CurrentBox, TempBox, 0, TempBox.Length);
            PokemonList2 curBoxPL = new PokemonList2(TempBox, Japanese ? PokemonList2.CapacityType.StoredJP : PokemonList2.CapacityType.Stored, Japanese);

            for (int i = 0; i < curBoxPL.Pokemon.Length; i++)
            {
                if (i < curBoxPL.Count)
                {
                    byte[] pkDat = new PokemonList2(curBoxPL[i]).GetBytes();
                    pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + CurrentBox * SIZE_BOX + i * SIZE_STORED);
                }
                else
                {
                    byte[] pkDat = new byte[PokemonList2.GetDataLength(PokemonList2.CapacityType.Single, Japanese)];
                    pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + CurrentBox * SIZE_BOX + i * SIZE_STORED);
                }
            }

            byte[] TempParty = new byte[PokemonList2.GetDataLength(PokemonList2.CapacityType.Party, Japanese)];
            Array.Copy(Data, Offsets.Party, TempParty, 0, TempParty.Length);
            PokemonList2 partyList = new PokemonList2(TempParty, PokemonList2.CapacityType.Party, Japanese);

            for (int i = 0; i < partyList.Pokemon.Length; i++)
            {
                if (i < partyList.Count)
                {
                    byte[] pkDat = new PokemonList2(partyList[i]).GetBytes();
                    pkDat.CopyTo(Data, GetPartyOffset(i));
                }
                else
                {
                    byte[] pkDat = new byte[PokemonList2.GetDataLength(PokemonList2.CapacityType.Single, Japanese)];
                    pkDat.CopyTo(Data, GetPartyOffset(i));
                }
            }

            // Daycare currently undocumented for all Gen II games.
            if (Offsets.Daycare >= 0)
            {
                int offset = Offsets.Daycare;

                DaycareFlags[0] = Data[offset]; offset++;
                var pk1      = ReadPKMFromOffset(offset);  // parent 1
                var daycare1 = new PokemonList2(pk1);
                offset         += StringLength * 2 + 0x20; // nick/ot/pkm
                DaycareFlags[1] = Data[offset]; offset++;
                byte steps = Data[offset]; offset++;
                byte BreedMotherOrNonDitto = Data[offset]; offset++;
                var  pk2      = ReadPKMFromOffset(offset);     // parent 2
                var  daycare2 = new PokemonList2(pk2);
                offset += StringLength * 2 + PKX.SIZE_2STORED; // nick/ot/pkm
                var pk3 = ReadPKMFromOffset(offset);           // egg!
                pk3.IsEgg = true;
                var daycare3 = new PokemonList2(pk3);

                daycare1.GetBytes().CopyTo(Data, GetPartyOffset(7 + (0 * 2)));
                daycare2.GetBytes().CopyTo(Data, GetPartyOffset(7 + (1 * 2)));
                daycare3.GetBytes().CopyTo(Data, GetPartyOffset(7 + (2 * 2)));
                Daycare = Offsets.Daycare;
            }

            // Enable Pokedex editing
            PokeDex   = 0;
            EventFlag = Offsets.EventFlag;

            if (!Exportable)
            {
                ClearBoxes();
            }
        }
Example #9
0
        public SAV3(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
        {
            Data       = data == null ? new byte[SaveUtil.SIZE_G3RAW] : (byte[])data.Clone();
            BAK        = (byte[])Data.Clone();
            Exportable = !Data.SequenceEqual(new byte[Data.Length]);

            if (data == null)
            {
                Version = GameVersion.FRLG;
            }
            else if (versionOverride != GameVersion.Any)
            {
                Version = versionOverride;
            }
            else
            {
                Version = SaveUtil.getIsG3SAV(Data);
            }
            if (Version == GameVersion.Invalid)
            {
                return;
            }

            int[] BlockOrder1 = new int[14];
            for (int i = 0; i < 14; i++)
            {
                BlockOrder1[i] = BitConverter.ToInt16(Data, i * 0x1000 + 0xFF4);
            }
            int zeroBlock1 = Array.IndexOf(BlockOrder1, 0);

            if (Data.Length > SaveUtil.SIZE_G3RAWHALF)
            {
                int[] BlockOrder2 = new int[14];
                for (int i = 0; i < 14; i++)
                {
                    BlockOrder2[i] = BitConverter.ToInt16(Data, 0xE000 + i * 0x1000 + 0xFF4);
                }
                int zeroBlock2 = Array.IndexOf(BlockOrder2, 0);

                if (zeroBlock2 < 0)
                {
                    ActiveSAV = 0;
                }
                else if (zeroBlock1 < 0)
                {
                    ActiveSAV = 1;
                }
                else
                {
                    ActiveSAV = BitConverter.ToUInt32(Data, zeroBlock1 * 0x1000 + 0xFFC) >
                                BitConverter.ToUInt32(Data, zeroBlock2 * 0x1000 + 0xEFFC)
                    ? 0
                    : 1;
                }
                BlockOrder = ActiveSAV == 0 ? BlockOrder1 : BlockOrder2;
            }
            else
            {
                ActiveSAV  = 0;
                BlockOrder = BlockOrder1;
            }

            BlockOfs = new int[14];
            for (int i = 0; i < 14; i++)
            {
                BlockOfs[i] = Array.IndexOf(BlockOrder, i) * 0x1000 + ABO;
            }

            // Set up PC data buffer beyond end of save file.
            Box = Data.Length;
            Array.Resize(ref Data, Data.Length + SIZE_RESERVED); // More than enough empty space.

            // Copy chunk to the allocated location
            for (int i = 5; i < 14; i++)
            {
                int blockIndex = Array.IndexOf(BlockOrder, i);
                if (blockIndex == -1) // block empty
                {
                    continue;
                }
                Array.Copy(Data, blockIndex * 0x1000 + ABO, Data, Box + (i - 5) * 0xF80, chunkLength[i]);
            }

            switch (Version)
            {
            case GameVersion.RS:
                LegalKeyItems     = Legal.Pouch_Key_RS;
                OFS_PCItem        = BlockOfs[1] + 0x0498;
                OFS_PouchHeldItem = BlockOfs[1] + 0x0560;
                OFS_PouchKeyItem  = BlockOfs[1] + 0x05B0;
                OFS_PouchBalls    = BlockOfs[1] + 0x0600;
                OFS_PouchTMHM     = BlockOfs[1] + 0x0640;
                OFS_PouchBerry    = BlockOfs[1] + 0x0740;
                Personal          = PersonalTable.RS;
                break;

            case GameVersion.FRLG:
                LegalKeyItems     = Legal.Pouch_Key_FRLG;
                OFS_PCItem        = BlockOfs[1] + 0x0298;
                OFS_PouchHeldItem = BlockOfs[1] + 0x0310;
                OFS_PouchKeyItem  = BlockOfs[1] + 0x03B8;
                OFS_PouchBalls    = BlockOfs[1] + 0x0430;
                OFS_PouchTMHM     = BlockOfs[1] + 0x0464;
                OFS_PouchBerry    = BlockOfs[1] + 0x054C;
                Personal          = PersonalTable.FR;
                break;

            case GameVersion.E:
                LegalKeyItems     = Legal.Pouch_Key_E;
                OFS_PCItem        = BlockOfs[1] + 0x0498;
                OFS_PouchHeldItem = BlockOfs[1] + 0x0560;
                OFS_PouchKeyItem  = BlockOfs[1] + 0x05D8;
                OFS_PouchBalls    = BlockOfs[1] + 0x0650;
                OFS_PouchTMHM     = BlockOfs[1] + 0x0690;
                OFS_PouchBerry    = BlockOfs[1] + 0x0790;
                Personal          = PersonalTable.E;
                break;
            }
            LegalItems   = Legal.Pouch_Items_RS;
            LegalBalls   = Legal.Pouch_Ball_RS;
            LegalTMHMs   = Legal.Pouch_TMHM_RS;
            LegalBerries = Legal.Pouch_Berries_RS;

            HeldItems = Legal.HeldItems_RS;

            if (!Exportable)
            {
                resetBoxes();
            }
        }
Example #10
0
        public static bool IsFileTooSmall(long length) => length < 0x20; // bigger than PK1

        /// <summary>
        /// Tries to get an <see cref="SaveFile"/> object from the input parameters.
        /// </summary>
        /// <param name="data">Binary data</param>
        /// <param name="sav">Output result</param>
        /// <returns>True if file object reference is valid, false if none found.</returns>
        public static bool TryGetSAV(byte[] data, out SaveFile?sav)
        {
            sav = SaveUtil.GetVariantSAV(data);
            return(sav != null);
        }
Example #11
0
        protected override void setDex(PKM pkm)
        {
            if (PokeDex < 0)
            {
                return;
            }
            if (pkm.Species == 0)
            {
                return;
            }
            if (pkm.Species > MaxSpeciesID)
            {
                return;
            }
            if (Version == GameVersion.Unknown)
            {
                return;
            }

            const int brSize = 0x60;
            int       bit    = pkm.Species - 1;
            int       lang   = pkm.Language - 1; if (lang > 5)
            {
                lang--;                                        // 0-6 language vals
            }
            int origin   = pkm.Version;
            int gender   = pkm.Gender % 2; // genderless -> male
            int shiny    = pkm.IsShiny ? 1 : 0;
            int shiftoff = shiny * brSize * 2 + gender * brSize + brSize;

            // Set the [Species/Gender/Shiny] Owned Flag
            Data[PokeDex + shiftoff + bit / 8 + 0x8] |= (byte)(1 << (bit % 8));

            // Owned quality flag
            if (origin < 0x18 && bit < 649 && !ORAS) // Species: 1-649 for X/Y, and not for ORAS; Set the Foreign Owned Flag
            {
                Data[PokeDex + 0x64C + bit / 8] |= (byte)(1 << (bit % 8));
            }
            else if (origin >= 0x18 || ORAS) // Set Native Owned Flag (should always happen)
            {
                Data[PokeDex + bit / 8 + 0x8] |= (byte)(1 << (bit % 8));
            }

            // Set the Display flag if none are set
            bool Displayed = false;

            Displayed |= (Data[PokeDex + brSize * 5 + bit / 8 + 0x8] & (byte)(1 << (bit % 8))) != 0;
            Displayed |= (Data[PokeDex + brSize * 6 + bit / 8 + 0x8] & (byte)(1 << (bit % 8))) != 0;
            Displayed |= (Data[PokeDex + brSize * 7 + bit / 8 + 0x8] & (byte)(1 << (bit % 8))) != 0;
            Displayed |= (Data[PokeDex + brSize * 8 + bit / 8 + 0x8] & (byte)(1 << (bit % 8))) != 0;
            if (!Displayed) // offset is already biased by brSize, reuse shiftoff but for the display flags.
            {
                Data[PokeDex + shiftoff + brSize * 4 + bit / 8 + 0x8] |= (byte)(1 << (bit % 8));
            }

            // Set the Language
            if (lang < 0)
            {
                lang = 1;
            }
            Data[PokeDexLanguageFlags + (bit * 7 + lang) / 8] |= (byte)(1 << ((bit * 7 + lang) % 8));

            // Set DexNav count (only if not encountered previously)
            if (ORAS && getEncounterCount(pkm.Species - 1) == 0)
            {
                setEncounterCount(pkm.Species - 1, 1);
            }

            // Set Form flags
            int fc = Personal[pkm.Species].FormeCount;
            int f  = ORAS ? SaveUtil.getDexFormIndexORAS(pkm.Species, fc) : SaveUtil.getDexFormIndexXY(pkm.Species, fc);

            if (f < 0)
            {
                return;
            }

            int FormLen = ORAS ? 0x26 : 0x18;
            int FormDex = PokeDex + 0x8 + brSize * 9;

            bit = f + pkm.AltForm;

            // Set Form Seen Flag
            Data[FormDex + FormLen * shiny + bit / 8] |= (byte)(1 << (bit % 8));

            // Set Displayed Flag if necessary, check all flags
            for (int i = 0; i < fc; i++)
            {
                bit = f + i;
                if ((Data[FormDex + FormLen * 2 + bit / 8] & (byte)(1 << (bit % 8))) != 0) // Nonshiny
                {
                    return;                                                                // already set
                }
                if ((Data[FormDex + FormLen * 3 + bit / 8] & (byte)(1 << (bit % 8))) != 0) // Shiny
                {
                    return;                                                                // already set
                }
            }
            bit = f + pkm.AltForm;
            Data[FormDex + FormLen * (2 + shiny) + bit / 8] |= (byte)(1 << (bit % 8));
        }
Example #12
0
        protected override void setDex(PKM pkm)
        {
            if (pkm.Species == 0)
            {
                return;
            }
            if (pkm.Species > MaxSpeciesID)
            {
                return;
            }
            if (Version == GameVersion.Unknown)
            {
                return;
            }
            if (PokeDex < 0)
            {
                return;
            }

            const int brSize = 0x54;
            int       bit    = pkm.Species - 1;
            int       lang   = pkm.Language - 1; if (lang > 5)
            {
                lang--;                    // 0-6 language vals
            }
            int gender   = pkm.Gender % 2; // genderless -> male
            int shiny    = pkm.IsShiny ? 1 : 0;
            int shiftoff = shiny * brSize * 2 + gender * brSize + brSize;

            // Set the Species Owned Flag
            Data[PokeDex + 0x8 + bit / 8] |= (byte)(1 << (bit % 8));

            // Set the [Species/Gender/Shiny] Seen Flag
            Data[PokeDex + 0x8 + shiftoff + bit / 8] |= (byte)(1 << (bit % 8));

            // Set the Display flag if none are set
            bool Displayed = false;

            Displayed |= (Data[PokeDex + 0x8 + brSize * 5 + bit / 8] & (byte)(1 << (bit % 8))) != 0;
            Displayed |= (Data[PokeDex + 0x8 + brSize * 6 + bit / 8] & (byte)(1 << (bit % 8))) != 0;
            Displayed |= (Data[PokeDex + 0x8 + brSize * 7 + bit / 8] & (byte)(1 << (bit % 8))) != 0;
            Displayed |= (Data[PokeDex + 0x8 + brSize * 8 + bit / 8] & (byte)(1 << (bit % 8))) != 0;
            if (!Displayed) // offset is already biased by brSize, reuse shiftoff but for the display flags.
            {
                Data[PokeDex + 0x8 + shiftoff + brSize * 4 + bit / 8] |= (byte)(1 << (bit % 8));
            }

            // Set the Language
            if (lang < 0)
            {
                lang = 1;
            }
            Data[PokeDexLanguageFlags + (bit * 7 + lang) / 8] |= (byte)(1 << ((bit * 7 + lang) % 8));

            // Formes
            int fc = Personal[pkm.Species].FormeCount;
            int f  = B2W2 ? SaveUtil.getDexFormIndexB2W2(pkm.Species, fc) : SaveUtil.getDexFormIndexBW(pkm.Species, fc);

            if (f < 0)
            {
                return;
            }

            int FormLen = B2W2 ? 0xB : 0x9;
            int FormDex = PokeDex + 0x8 + brSize * 9;

            bit = f + pkm.AltForm;

            // Set Form Seen Flag
            Data[FormDex + FormLen * shiny + bit / 8] |= (byte)(1 << (bit % 8));

            // Set Displayed Flag if necessary, check all flags
            for (int i = 0; i < fc; i++)
            {
                bit = f + i;
                if ((Data[FormDex + FormLen * 2 + bit / 8] & (byte)(1 << (bit % 8))) != 0) // Nonshiny
                {
                    return;                                                                // already set
                }
                if ((Data[FormDex + FormLen * 3 + bit / 8] & (byte)(1 << (bit % 8))) != 0) // Shiny
                {
                    return;                                                                // already set
                }
            }
            bit = f + pkm.AltForm;
            Data[FormDex + FormLen * (2 + shiny) + bit / 8] |= (byte)(1 << (bit % 8));
        }
Example #13
0
        public SAV1(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
        {
            Data       = data ?? new byte[SaveUtil.SIZE_G1RAW];
            BAK        = (byte[])Data.Clone();
            Exportable = !IsRangeEmpty(0, Data.Length);

            if (data == null)
            {
                Version = GameVersion.RBY;
            }
            else if (versionOverride != GameVersion.Any)
            {
                Version = versionOverride;
            }
            else
            {
                Version = SaveUtil.GetIsG1SAV(Data);
            }
            if (Version == GameVersion.Invalid)
            {
                return;
            }
            if (Starter != 0)
            {
                Version = Yellow ? GameVersion.YW : GameVersion.RB;
            }

            Box = Data.Length;
            Array.Resize(ref Data, Data.Length + SIZE_RESERVED);
            Party = GetPartyOffset(0);

            Japanese = SaveUtil.GetIsG1SAVJ(Data);
            Personal = PersonalTable.Y;

            // Stash boxes after the save file's end.
            byte[] TempBox = new byte[SIZE_STOREDBOX];
            for (int i = 0; i < BoxCount; i++)
            {
                if (i < BoxCount / 2)
                {
                    Array.Copy(Data, 0x4000 + i * TempBox.Length, TempBox, 0, TempBox.Length);
                }
                else
                {
                    Array.Copy(Data, 0x6000 + (i - BoxCount / 2) * TempBox.Length, TempBox, 0, TempBox.Length);
                }
                PokemonList1 PL1 = new PokemonList1(TempBox, Japanese ? PokemonList1.CapacityType.StoredJP : PokemonList1.CapacityType.Stored, Japanese);
                for (int j = 0; j < PL1.Pokemon.Length; j++)
                {
                    if (j < PL1.Count)
                    {
                        byte[] pkDat = new PokemonList1(PL1[j]).GetBytes();
                        pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + i * SIZE_BOX + j * SIZE_STORED);
                    }
                    else
                    {
                        byte[] pkDat = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Single, Japanese)];
                        pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + i * SIZE_BOX + j * SIZE_STORED);
                    }
                }
            }

            Array.Copy(Data, Japanese ? 0x302D : 0x30C0, TempBox, 0, TempBox.Length);
            PokemonList1 curBoxPL = new PokemonList1(TempBox, Japanese ? PokemonList1.CapacityType.StoredJP : PokemonList1.CapacityType.Stored, Japanese);

            for (int i = 0; i < curBoxPL.Pokemon.Length; i++)
            {
                if (i < curBoxPL.Count)
                {
                    byte[] pkDat = new PokemonList1(curBoxPL[i]).GetBytes();
                    pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + CurrentBox * SIZE_BOX + i * SIZE_STORED);
                }
                else
                {
                    byte[] pkDat = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Single, Japanese)];
                    pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + CurrentBox * SIZE_BOX + i * SIZE_STORED);
                }
            }

            byte[] TempParty = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Party, Japanese)];
            Array.Copy(Data, Japanese ? 0x2ED5 : 0x2F2C, TempParty, 0, TempParty.Length);
            PokemonList1 partyList = new PokemonList1(TempParty, PokemonList1.CapacityType.Party, Japanese);

            for (int i = 0; i < partyList.Pokemon.Length; i++)
            {
                if (i < partyList.Count)
                {
                    byte[] pkDat = new PokemonList1(partyList[i]).GetBytes();
                    pkDat.CopyTo(Data, GetPartyOffset(i));
                }
                else
                {
                    byte[] pkDat = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Single, Japanese)];
                    pkDat.CopyTo(Data, GetPartyOffset(i));
                }
            }

            byte[] rawDC = new byte[0x38];
            Array.Copy(Data, Japanese ? 0x2CA7 : 0x2CF4, rawDC, 0, rawDC.Length);
            byte[] TempDaycare = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Single, Japanese)];
            TempDaycare[0] = rawDC[0];
            Array.Copy(rawDC, 1, TempDaycare, 2 + 1 + PKX.SIZE_1PARTY + StringLength, StringLength);
            Array.Copy(rawDC, 1 + StringLength, TempDaycare, 2 + 1 + PKX.SIZE_1PARTY, StringLength);
            Array.Copy(rawDC, 1 + 2 * StringLength, TempDaycare, 2 + 1, PKX.SIZE_1STORED);
            PokemonList1 daycareList = new PokemonList1(TempDaycare, PokemonList1.CapacityType.Single, Japanese);

            daycareList.GetBytes().CopyTo(Data, GetPartyOffset(7));
            Daycare = GetPartyOffset(7);

            EventFlag        = Japanese ? 0x29E9 : 0x29F3;
            ObjectSpawnFlags = Japanese ? 0x2848 : 0x2852; // 2 bytes after Coin

            // Enable Pokedex editing
            PokeDex = 0;

            if (!Exportable)
            {
                ClearBoxes();
            }
        }
Example #14
0
        public SAV3(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
        {
            Data       = data ?? new byte[SaveUtil.SIZE_G3RAW];
            BAK        = (byte[])Data.Clone();
            Exportable = !IsRangeEmpty(0, Data.Length);

            int[] BlockOrder1 = GetBlockOrder(0);
            if (Data.Length > SaveUtil.SIZE_G3RAWHALF)
            {
                int[] BlockOrder2 = GetBlockOrder(0xE000);
                ActiveSAV  = GetActiveSaveIndex(BlockOrder1, BlockOrder2);
                BlockOrder = ActiveSAV == 0 ? BlockOrder1 : BlockOrder2;
            }
            else
            {
                ActiveSAV  = 0;
                BlockOrder = BlockOrder1;
            }

            BlockOfs = new int[BLOCK_COUNT];
            for (int i = 0; i < BLOCK_COUNT; i++)
            {
                int index = Array.IndexOf(BlockOrder, i);
                BlockOfs[i] = index < 0 ? int.MinValue : (index * SIZE_BLOCK) + ABO;
            }

            if (data == null)
            {
                Version = GameVersion.FRLG;
            }
            else if (versionOverride != GameVersion.Any)
            {
                Version = versionOverride;
            }
            else
            {
                Version = GetVersion(Data, BlockOfs[0]);
            }

            // Set up PC data buffer beyond end of save file.
            Box = Data.Length;
            Array.Resize(ref Data, Data.Length + SIZE_RESERVED); // More than enough empty space.

            // Copy chunk to the allocated location
            for (int i = 5; i < BLOCK_COUNT; i++)
            {
                int blockIndex = Array.IndexOf(BlockOrder, i);
                if (blockIndex == -1) // block empty
                {
                    continue;
                }
                Array.Copy(Data, (blockIndex * SIZE_BLOCK) + ABO, Data, Box + ((i - 5) * 0xF80), chunkLength[i]);
            }

            // Japanese games are limited to 5 character OT names; any unused characters are 0xFF.
            // 5 for JP, 7 for INT. There's always 1 terminator, thus we can check 0x6-0x7 being 0xFFFF = INT
            // OT name is stored at the top of the first block.
            Japanese = BitConverter.ToInt16(Data, BlockOfs[0] + 0x6) == 0;

            PokeDex = BlockOfs[0] + 0x18;
            switch (Version)
            {
            case GameVersion.RS:
                LegalKeyItems     = Legal.Pouch_Key_RS;
                OFS_PCItem        = BlockOfs[1] + 0x0498;
                OFS_PouchHeldItem = BlockOfs[1] + 0x0560;
                OFS_PouchKeyItem  = BlockOfs[1] + 0x05B0;
                OFS_PouchBalls    = BlockOfs[1] + 0x0600;
                OFS_PouchTMHM     = BlockOfs[1] + 0x0640;
                OFS_PouchBerry    = BlockOfs[1] + 0x0740;
                SeenFlagOffsets   = new[] { PokeDex + 0x44, BlockOfs[1] + 0x938, BlockOfs[4] + 0xC0C };
                EventFlag         = BlockOfs[2] + 0x2A0;
                EventConst        = EventFlag + (EventFlagMax / 8);
                Daycare           = BlockOfs[4] + 0x11C;
                break;

            case GameVersion.E:
                LegalKeyItems     = Legal.Pouch_Key_E;
                OFS_PCItem        = BlockOfs[1] + 0x0498;
                OFS_PouchHeldItem = BlockOfs[1] + 0x0560;
                OFS_PouchKeyItem  = BlockOfs[1] + 0x05D8;
                OFS_PouchBalls    = BlockOfs[1] + 0x0650;
                OFS_PouchTMHM     = BlockOfs[1] + 0x0690;
                OFS_PouchBerry    = BlockOfs[1] + 0x0790;
                SeenFlagOffsets   = new[] { PokeDex + 0x44, BlockOfs[1] + 0x988, BlockOfs[4] + 0xCA4 };
                EventFlag         = BlockOfs[2] + 0x2F0;
                EventConst        = EventFlag + (EventFlagMax / 8);
                Daycare           = BlockOfs[4] + 0x1B0;
                break;

            case GameVersion.FRLG:
                LegalKeyItems     = Legal.Pouch_Key_FRLG;
                OFS_PCItem        = BlockOfs[1] + 0x0298;
                OFS_PouchHeldItem = BlockOfs[1] + 0x0310;
                OFS_PouchKeyItem  = BlockOfs[1] + 0x03B8;
                OFS_PouchBalls    = BlockOfs[1] + 0x0430;
                OFS_PouchTMHM     = BlockOfs[1] + 0x0464;
                OFS_PouchBerry    = BlockOfs[1] + 0x054C;
                SeenFlagOffsets   = new[] { PokeDex + 0x44, BlockOfs[1] + 0x5F8, BlockOfs[4] + 0xB98 };
                EventFlag         = BlockOfs[1] + 0xEE0;
                EventConst        = BlockOfs[2] + 0x80; // EventFlag + (EventFlagMax / 8);
                Daycare           = BlockOfs[4] + 0x100;
                break;
            }
            Personal = SaveUtil.GetG3Personal(Version);
            LoadEReaderBerryData();
            LegalItems   = Legal.Pouch_Items_RS;
            LegalBalls   = Legal.Pouch_Ball_RS;
            LegalTMHMs   = Legal.Pouch_TMHM_RS;
            LegalBerries = Legal.Pouch_Berries_RS;
            HeldItems    = Legal.HeldItems_RS;

            // Sanity Check SeenFlagOffsets -- early saves may not have block 4 initialized yet
            SeenFlagOffsets = SeenFlagOffsets?.Where(z => z >= 0).ToArray();

            if (!Exportable)
            {
                ClearBoxes();
            }
        }
Example #15
0
 private ushort GetChecksum(byte[] data) => SaveUtil.CRC16_CCITT(data, Offset, Length);
Example #16
0
        public SAV3(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
        {
            Data       = data == null ? new byte[SaveUtil.SIZE_G3RAW] : (byte[])data.Clone();
            BAK        = (byte[])Data.Clone();
            Exportable = !Data.All(z => z == 0);

            if (data == null)
            {
                Version = GameVersion.FRLG;
            }
            else if (versionOverride != GameVersion.Any)
            {
                Version = versionOverride;
            }
            else
            {
                Version = SaveUtil.GetIsG3SAV(Data);
            }
            if (Version == GameVersion.Invalid)
            {
                return;
            }

            int[] BlockOrder1 = new int[BLOCK_COUNT];
            for (int i = 0; i < BLOCK_COUNT; i++)
            {
                BlockOrder1[i] = BitConverter.ToInt16(Data, i * SIZE_BLOCK + 0xFF4);
            }
            int zeroBlock1 = Array.IndexOf(BlockOrder1, 0);

            if (Data.Length > SaveUtil.SIZE_G3RAWHALF)
            {
                int[] BlockOrder2 = new int[BLOCK_COUNT];
                for (int i = 0; i < BLOCK_COUNT; i++)
                {
                    BlockOrder2[i] = BitConverter.ToInt16(Data, 0xE000 + i * SIZE_BLOCK + 0xFF4);
                }
                int zeroBlock2 = Array.IndexOf(BlockOrder2, 0);

                if (zeroBlock2 < 0)
                {
                    ActiveSAV = 0;
                }
                else if (zeroBlock1 < 0)
                {
                    ActiveSAV = 1;
                }
                else
                {
                    ActiveSAV = BitConverter.ToUInt32(Data, zeroBlock1 * SIZE_BLOCK + 0xFFC) >
                                BitConverter.ToUInt32(Data, zeroBlock2 * SIZE_BLOCK + 0xEFFC)
                    ? 0
                    : 1;
                }
                BlockOrder = ActiveSAV == 0 ? BlockOrder1 : BlockOrder2;
            }
            else
            {
                ActiveSAV  = 0;
                BlockOrder = BlockOrder1;
            }

            BlockOfs = new int[BLOCK_COUNT];
            for (int i = 0; i < BLOCK_COUNT; i++)
            {
                int index = Array.IndexOf(BlockOrder, i);
                BlockOfs[i] = index < 0 ? int.MinValue : index * SIZE_BLOCK + ABO;
            }

            // Set up PC data buffer beyond end of save file.
            Box = Data.Length;
            Array.Resize(ref Data, Data.Length + SIZE_RESERVED); // More than enough empty space.

            // Copy chunk to the allocated location
            for (int i = 5; i < BLOCK_COUNT; i++)
            {
                int blockIndex = Array.IndexOf(BlockOrder, i);
                if (blockIndex == -1) // block empty
                {
                    continue;
                }
                Array.Copy(Data, blockIndex * SIZE_BLOCK + ABO, Data, Box + (i - 5) * 0xF80, chunkLength[i]);
            }

            // Japanese games are limited to 5 character OT names; any unused characters are 0xFF.
            // 5 for JP, 7 for INT. There's always 1 terminator, thus we can check 0x6-0x7 being 0xFFFF = INT
            // OT name is stored at the top of the first block.
            Japanese = BitConverter.ToInt16(data, BlockOfs[0] + 0x6) == 0;

            switch (Version)
            {
            case GameVersion.RS:
                LegalKeyItems     = Legal.Pouch_Key_RS;
                OFS_PCItem        = BlockOfs[1] + 0x0498;
                OFS_PouchHeldItem = BlockOfs[1] + 0x0560;
                OFS_PouchKeyItem  = BlockOfs[1] + 0x05B0;
                OFS_PouchBalls    = BlockOfs[1] + 0x0600;
                OFS_PouchTMHM     = BlockOfs[1] + 0x0640;
                OFS_PouchBerry    = BlockOfs[1] + 0x0740;
                Personal          = PersonalTable.RS;
                SeenFlagOffsets   = new[] { BlockOfs[0] + 0x5C, BlockOfs[1] + 0x938, BlockOfs[4] + 0xC0C };
                EventFlag         = BlockOfs[2] + 0x2A0;
                EventConst        = EventFlag + EventFlagMax / 8;
                break;

            case GameVersion.E:
                LegalKeyItems     = Legal.Pouch_Key_E;
                OFS_PCItem        = BlockOfs[1] + 0x0498;
                OFS_PouchHeldItem = BlockOfs[1] + 0x0560;
                OFS_PouchKeyItem  = BlockOfs[1] + 0x05D8;
                OFS_PouchBalls    = BlockOfs[1] + 0x0650;
                OFS_PouchTMHM     = BlockOfs[1] + 0x0690;
                OFS_PouchBerry    = BlockOfs[1] + 0x0790;
                Personal          = PersonalTable.E;
                SeenFlagOffsets   = new[] { BlockOfs[0] + 0x5C, BlockOfs[1] + 0x988, BlockOfs[4] + 0xCA4 };
                EventFlag         = BlockOfs[2] + 0x2F0;
                EventConst        = EventFlag + EventFlagMax / 8;
                break;

            case GameVersion.FRLG:
                LegalKeyItems     = Legal.Pouch_Key_FRLG;
                OFS_PCItem        = BlockOfs[1] + 0x0298;
                OFS_PouchHeldItem = BlockOfs[1] + 0x0310;
                OFS_PouchKeyItem  = BlockOfs[1] + 0x03B8;
                OFS_PouchBalls    = BlockOfs[1] + 0x0430;
                OFS_PouchTMHM     = BlockOfs[1] + 0x0464;
                OFS_PouchBerry    = BlockOfs[1] + 0x054C;
                Personal          = PersonalTable.FR;
                SeenFlagOffsets   = new[] { BlockOfs[0] + 0x5C, BlockOfs[1] + 0x5F8, BlockOfs[4] + 0xB98 };
                EventFlag         = BlockOfs[2] + 0x000;
                EventConst        = EventFlag + EventFlagMax / 8;
                break;
            }
            LoadEReaderBerryData();
            LegalItems   = Legal.Pouch_Items_RS;
            LegalBalls   = Legal.Pouch_Ball_RS;
            LegalTMHMs   = Legal.Pouch_TMHM_RS;
            LegalBerries = Legal.Pouch_Berries_RS;
            HeldItems    = Legal.HeldItems_RS;

            // Sanity Check SeenFlagOffsets -- early saves may not have block 4 initialized yet
            SeenFlagOffsets = SeenFlagOffsets?.Where(z => z >= 0).ToArray();

            if (!Exportable)
            {
                ClearBoxes();
            }
        }
Example #17
0
 protected SAV_STADIUM(bool japanese, int size) : base(size)
 {
     Japanese = japanese;
     OT       = SaveUtil.GetSafeTrainerName(this, (LanguageID)Language);
 }