Example #1
0
        public Save(string filePath, bool createBackup = false)
        {
            if (File.Exists(filePath))
            {
                if (_saveFile != null)
                {
                    _saveReader.Close();
                    _saveFile.Close();
                }
                try { _saveFile = new FileStream(filePath, FileMode.Open); } catch { SuccessfullyLoaded = false; }
                if (_saveFile == null || !SuccessfullyLoaded || !_saveFile.CanWrite)
                {
                    DebugUtility.DebugManagerInstance.WriteLine(
                        $"Error: File {Path.GetFileName(filePath)} is being used by another process. Please close any process using it before editing!",
                        DebugLevel.Error);
                    try
                    {
                        _saveFile?.Close();
                    }
                    catch
                    {
                        // ignored
                    }
                    return;
                }

                _saveReader = new BinaryReader(_saveFile);
                SaveData    = _saveReader.ReadBytes((int)_saveFile.Length);
                _byteswap   = this.IsByteSwapped();
                if (_byteswap)
                {
                    SaveData = SaveDataManager.ByteSwap(SaveData); // Only byteswap the working data.
                }

                SaveType            = this.GetSaveType();
                SaveGeneration      = SaveDataManager.GetSaveGeneration(SaveType);
                FullSavePath        = filePath;
                SaveName            = Path.GetFileNameWithoutExtension(filePath);
                SavePath            = Path.GetDirectoryName(filePath) + Path.DirectorySeparatorChar;
                SaveExtension       = Path.GetExtension(filePath);
                SaveId              = SaveDataManager.GetGameId(SaveType);
                SaveDataStartOffset = SaveDataManager.GetSaveDataOffset(SaveId.ToLower(), SaveExtension?.Replace(".", "").ToLower());
                SaveInfo            = SaveDataManager.GetSaveInfo(SaveType);

                if (SaveType == SaveType.WildWorld || SaveGeneration == SaveGeneration.N3DS)
                {
                    IsBigEndian = false;
                }

                _saveReader.Close();
                _saveFile.Close();
                _saveReader.Dispose();
                _saveFile.Dispose();

                SaveInstance = this;
            }
            else
            {
                DebugUtility.DebugManagerInstance.WriteLine("File doesn't exist!", DebugLevel.Error);
            }
        }
Example #2
0
        public void Flush()
        {
            var fullSaveName = SavePath + Path.DirectorySeparatorChar + SaveName + SaveExtension;

            _saveFile   = new FileStream(fullSaveName, FileMode.OpenOrCreate);
            _saveWriter = new BinaryWriter(_saveFile);
            switch (SaveType)
            {
            case SaveType.DoubutsuNoMoriPlus:
            case SaveType.AnimalCrossing:
            case SaveType.DoubutsuNoMoriEPlus:
            case SaveType.AnimalForestEPlus:
                Write(SaveDataStartOffset + SaveInfo.SaveOffsets.Checksum,
                      new UInt16BEChecksum().Calculate(
                          SaveData.Skip(SaveDataStartOffset).Take(SaveInfo.SaveOffsets.SaveSize).ToArray(),
                          (uint)SaveInfo.SaveOffsets.Checksum), IsBigEndian);

                SaveData.Skip(SaveDataStartOffset).Take(SaveInfo.SaveOffsets.SaveSize).ToArray().CopyTo(
                    SaveData,
                    SaveDataStartOffset + SaveInfo.SaveOffsets.SaveSize);
                break;

            case SaveType.WildWorld:
                Write(SaveDataStartOffset + SaveInfo.SaveOffsets.Checksum,
                      new UInt16LEChecksum().Calculate(
                          SaveData.Skip(SaveDataStartOffset).Take(SaveInfo.SaveOffsets.SaveSize).ToArray(),
                          (uint)SaveInfo.SaveOffsets.Checksum), IsBigEndian);

                SaveData.Skip(SaveDataStartOffset).Take(SaveInfo.SaveOffsets.SaveSize).ToArray().CopyTo(
                    SaveData,
                    SaveDataStartOffset + SaveInfo.SaveOffsets.SaveSize);
                break;

            case SaveType.DoubutsuNoMori:
            case SaveType.DongwuSenlin:
                Write(SaveDataStartOffset + SaveInfo.SaveOffsets.Checksum,
                      new UInt16BEChecksum().Calculate(
                          SaveData.Skip(SaveDataStartOffset).Take(0xF980).ToArray(),
                          (uint)SaveInfo.SaveOffsets.Checksum), IsBigEndian);

                SaveData.Skip(SaveDataStartOffset).Take(SaveInfo.SaveOffsets.SaveSize).ToArray().CopyTo(
                    SaveData,
                    SaveDataStartOffset + SaveInfo.SaveOffsets.SaveSize);
                break;

            case SaveType.CityFolk:
                var crc32 = new CRC32();
                for (var i = 0; i < 4; i++)
                {
                    var playerDataOffset = SaveDataStartOffset + i * 0x86C0 + 0x1140;
                    var playerCrc32      =
                        crc32.Calculate(SaveData.Skip(playerDataOffset + 4).Take(0x759C).ToArray());
                    Write(playerDataOffset, playerCrc32, true);
                }

                Write(SaveDataStartOffset + 0x5EC60,
                      crc32.Calculate(SaveData.Skip(SaveDataStartOffset + 0x5EC64).Take(0x1497C).ToArray()),
                      true);
                Write(SaveDataStartOffset + 0x5EB04,
                      crc32.Calculate(SaveData.Skip(SaveDataStartOffset + 0x5EB08).Take(0x152).ToArray(),
                                      0x12141018), true);
                Write(SaveDataStartOffset + 0x73600,
                      crc32.Calculate(SaveData.Skip(SaveDataStartOffset + 0x73604).Take(0x19BD1C).ToArray()),
                      true);
                Write(SaveDataStartOffset,
                      crc32.Calculate(SaveData.Skip(SaveDataStartOffset + 4).Take(0x1C).ToArray()), true);
                Write(SaveDataStartOffset + 0x20,
                      crc32.Calculate(SaveData.Skip(SaveDataStartOffset + 0x24).Take(0x111C).ToArray()), true);
                break;

            case SaveType.NewLeaf:
                var crc32Reflected = new NewLeafCRC32Reflected();

                Write(SaveDataStartOffset,
                      crc32Reflected.Calculate(SaveData.Skip(SaveDataStartOffset + 4).Take(0x1C).ToArray()));
                for (var i = 0; i < 4; i++)
                {
                    var dataOffset = SaveDataStartOffset + 0x20 + i * 0x9F10;
                    Write(dataOffset,
                          crc32Reflected.Calculate(SaveData.Skip(dataOffset + 4).Take(0x6B64).ToArray()));

                    var dataOffset2 = SaveDataStartOffset + 0x20 + 0x6B68 + i * 0x9F10;
                    Write(dataOffset2,
                          crc32Reflected.Calculate(SaveData.Skip(dataOffset2 + 4).Take(0x33A4).ToArray()));
                }

                Write(SaveDataStartOffset + 0x27C60,
                      crc32Reflected.Calculate(SaveData.Skip(SaveDataStartOffset + 0x27C60 + 4).Take(0x218B0)
                                               .ToArray()));
                Write(SaveDataStartOffset + 0x49520,
                      crc32Reflected.Calculate(SaveData.Skip(SaveDataStartOffset + 0x49520 + 4).Take(0x44B8)
                                               .ToArray()));
                Write(SaveDataStartOffset + 0x4D9DC,
                      crc32Reflected.Calculate(SaveData.Skip(SaveDataStartOffset + 0x4D9DC + 4).Take(0x1E420)
                                               .ToArray()));
                Write(SaveDataStartOffset + 0x6BE00,
                      crc32Reflected.Calculate(SaveData.Skip(SaveDataStartOffset + 0x6BE00 + 4).Take(0x20)
                                               .ToArray()));
                Write(SaveDataStartOffset + 0x6BE24,
                      crc32Reflected.Calculate(SaveData.Skip(SaveDataStartOffset + 0x6BE24 + 4).Take(0x13AF8)
                                               .ToArray()));
                break;

            case SaveType.WelcomeAmiibo:
                var wCrc32Reflected = new NewLeafCRC32Reflected();
                var wCrc32Normal    = new NewLeafCRC32Normal();

                // Reflected CRC32 Implementation Checksums
                Write(SaveDataStartOffset,
                      wCrc32Reflected.Calculate(SaveData.Skip(SaveDataStartOffset + 4).Take(0x1C).ToArray()));
                for (var i = 0; i < 4; i++)
                {
                    var dataOffset = SaveDataStartOffset + 0x20 + i * 0xA480;
                    Write(dataOffset,
                          wCrc32Reflected.Calculate(SaveData.Skip(dataOffset + 4).Take(0x6B84).ToArray()));

                    var dataOffset2 = SaveDataStartOffset + 0x20 + 0x6B88 + i * 0xA480;
                    Write(dataOffset2,
                          wCrc32Reflected.Calculate(SaveData.Skip(dataOffset2 + 4).Take(0x38F4).ToArray()));
                }

                Write(SaveDataStartOffset + 0x29220,
                      wCrc32Reflected.Calculate(SaveData.Skip(SaveDataStartOffset + 0x29220 + 4).Take(0x22BC8)
                                                .ToArray()));
                Write(SaveDataStartOffset + 0x4BE00,
                      wCrc32Reflected.Calculate(SaveData.Skip(SaveDataStartOffset + 0x4BE00 + 4).Take(0x44B8)
                                                .ToArray()));
                Write(SaveDataStartOffset + 0x533A4,
                      wCrc32Reflected.Calculate(SaveData.Skip(SaveDataStartOffset + 0x533A4 + 4).Take(0x1E4D8)
                                                .ToArray()));
                Write(SaveDataStartOffset + 0x71880,
                      wCrc32Reflected.Calculate(SaveData.Skip(SaveDataStartOffset + 0x71880 + 4).Take(0x20)
                                                .ToArray()));
                Write(SaveDataStartOffset + 0x718A4,
                      wCrc32Reflected.Calculate(SaveData.Skip(SaveDataStartOffset + 0x718A4 + 4).Take(0xBE4)
                                                .ToArray()));
                Write(SaveDataStartOffset + 0x738D4,
                      wCrc32Reflected.Calculate(SaveData.Skip(SaveDataStartOffset + 0x738D4 + 4).Take(0x16188)
                                                .ToArray()));

                // Normal CRC32 Implementation Checksums
                Write(SaveDataStartOffset + 0x502BC,
                      wCrc32Normal.Calculate(SaveData.Skip(SaveDataStartOffset + 0x502BC + 4).Take(0x28F0)
                                             .ToArray()));
                Write(SaveDataStartOffset + 0x52BB0,
                      wCrc32Normal.Calculate(SaveData.Skip(SaveDataStartOffset + 0x52BB0 + 4).Take(0x7F0)
                                             .ToArray()));
                Write(SaveDataStartOffset + 0x7248C,
                      wCrc32Normal.Calculate(SaveData.Skip(SaveDataStartOffset + 0x7248C + 4).Take(0x1444)
                                             .ToArray()));
                break;
            }

            _saveWriter.Write(SaveType == SaveType.DoubutsuNoMori && _byteswap
                ? SaveDataManager.ByteSwap(SaveData)
                : SaveData); // Doubutsu no Mori is dword byteswapped
            _saveWriter.Flush();
            _saveFile.Flush();

            _saveWriter.Close();
            _saveFile.Close();
            _saveWriter.Dispose();
            _saveFile.Dispose();
            ChangesMade = false;
        }