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); } }
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; }