Example #1
0
 static void ReadHeader_Postfix(string __0, bool __1, ref GameSaveHeader header, object __3)
 {
     if (header != null)
     {
         ((CompressionGameSaveHeader)header).IsCompressed = IsCompressedSave;
     }
 }
Example #2
0
    public GameSave(string fileName)
    {
        Bytes      = File.ReadAllBytes(FileName = fileName);
        IsRemaster = BitConverter.ToInt32(Bytes, 8) == 0;
        SaveType   = IsRemaster switch
        {
            true when Path.GetExtension(fileName) is "" => SaveType.Switch,
            true => SaveType.Remaster,
            false => SaveType.Original,
        };
        var pattern = SaveType == SaveType.Switch ? SwitchFilePattern : RemasterFilePattern;

        if (IsRemaster && Path.GetExtension(fileName) != ".bin" && !Path.GetFileNameWithoutExtension(fileName).StartsWith(pattern))
        {
            throw new NotSupportedException($"Save file is not a user save and changing them can lead to the game infinite looping. The editor only supports saves that start with {pattern}.");
        }
        _header         = new(this);
        Buffs           = FilterToApplicable(Amalur.Buffs.Values, _header.IsFateswornAware);
        ItemDefinitions = FilterToApplicable(Amalur.ItemDefinitions.Values, _header.IsFateswornAware);

        if (BitConverter.ToInt32(Bytes, BodyStart) == CompressedFlag)
        {
            Body = new byte[BitConverter.ToInt32(Bytes, BodyStart + 4)];
            if (Body.Length != _header.BodyDataLength)
            {
                throw new NotSupportedException($"Save file appears corrupted. The header states that the body should have {_header.BodyDataLength} bytes, but the decompressed size is {Body.Length}");
            }
            var bundleInfoStart = BodyStart + 12;
            var bundleInfoSize  = BitConverter.ToInt32(Bytes, bundleInfoStart - 4);
            using var bundleInfoData = new ZLibStream(new MemoryStream(Bytes, bundleInfoStart, bundleInfoSize), CompressionMode.Decompress);
            var endOfBundle    = bundleInfoData.ReadAll(Body);
            var gameStateStart = bundleInfoStart + bundleInfoSize + 4;
            var gameStateSize  = BitConverter.ToInt32(Bytes, gameStateStart - 4);
            using var gameStateData = new ZLibStream(new MemoryStream(Bytes, gameStateStart, gameStateSize), CompressionMode.Decompress);
            gameStateData.ReadAll(Body.AsSpan(endOfBundle, Body.Length - endOfBundle));
        }
        else
        {
            Body = Bytes.AsSpan(BodyStart, BodyDataLength).ToArray();
        }
        _originalBodyLength = Body.Length;
        Stash = Stash.TryCreateStash(this);
        ReadOnlySpan <byte> data = Body;

        _bagOffset            = GetBagOffset(data);
        _gameStateStartOffset = data.IndexOf(new byte[5] {
            0xF7, 0x5D, 0x3C, 0x00, 0x0A
        });
        var typeSectionOffset =
            data.IndexOf(new byte[5] {
            0x23, 0xCC, 0x58, 0x00, 0x06
        }) is int ix and > -1
            ? ix
            : data.IndexOf(new byte[5] {
            0x23, 0xCC, 0x58, 0x00, 0x04
        }) is int pix and > -1
                ? pix
                : data.IndexOf(new byte[5] {
            0x23, 0xCC, 0x58, 0x00, 0x03
        });

        _dataLengthOffsets = new[] {
            _gameStateStartOffset + 5,     // gameStateSize
            data.IndexOf(new byte[5] {
                0x0C, 0xAE, 0x32, 0x00, 0x00
            }) + 5,                    // unknown length 1
            typeSectionOffset + 5,     // type section length
        };
        _itemContainer        = new(this, data.IndexOf(new byte[5] {
            0xD3, 0x34, 0x43, 0x00, 0x00
        }), 0x00_24_D5_68_00_00_00_0Bul);
        _itemBuffsContainer   = new(this, data.IndexOf(new byte[5] {
            0xBB, 0xD5, 0x43, 0x00, 0x00
        }), 0x00_28_60_84_00_00_00_0Bul);
        _itemSocketsContainer = new(this, data.IndexOf(new byte[5] {
            0x93, 0xCC, 0x80, 0x00, 0x00
        }), 0x00_59_36_38_00_00_00_0Bul);
        var itemLocs = _itemContainer.ToDictionary(x => x.id, x => (x.offset, x.dataLength));
        var itemBuffsLocs = _itemBuffsContainer.ToDictionary(x => x.id, x => (x.offset, x.dataLength));
        var itemSocketsLocs = _itemSocketsContainer.ToDictionary(x => x.id, x => (x.offset, x.dataLength));
        int dataLength, playerActor = 0;
        var candidates = new List <(int id, int typeIdOffset, QuestItemDefinition?questItemDef)>();

        for (int ixOfActor = _dataLengthOffsets[^ 1] + 4; BitConverter.ToInt32(Body, ixOfActor) == 0x00_75_2D_06; ixOfActor += dataLength)