예제 #1
0
        public static SavedGame Serdes(SavedGame save, ISerializer s)
        {
            save ??= new SavedGame();
            s.Begin();

            ushort nameLength = s.UInt16("NameLength", (ushort)(save.Name?.Length ?? 0));

            save.Unk0 = s.UInt16(nameof(Unk0), save.Unk0);
            save.Name = s.FixedLengthString(nameof(Name), save.Name, nameLength);

            save.Unk1 = s.UInt32(nameof(Unk1), save.Unk1);
            var versionOffset = s.Offset;

            save.Version = s.UInt16(nameof(Version), save.Version);                 // 0
            ApiUtil.Assert(save.Version == 138);                                    // TODO: Throw error for other versions?
            save.Unk9 = s.ByteArray(nameof(Unk9), save.Unk9, 6);                    // 2

            ushort days    = s.UInt16("Days", (ushort)save.ElapsedTime.TotalDays);  // 8
            ushort hours   = s.UInt16("Hours", (ushort)save.ElapsedTime.Hours);     // A
            ushort minutes = s.UInt16("Minutes", (ushort)save.ElapsedTime.Minutes); // C

            save.ElapsedTime    = new TimeSpan(days, hours, minutes, save.ElapsedTime.Seconds, save.ElapsedTime.Milliseconds);
            save.MapId          = s.EnumU16(nameof(MapId), save.MapId);                   // E
            save.PartyX         = s.UInt16(nameof(PartyX), save.PartyX);                  // 10
            save.PartyY         = s.UInt16(nameof(PartyY), save.PartyY);                  // 12
            save.PartyDirection = s.EnumU16(nameof(PartyDirection), save.PartyDirection); // 14

            save.Unknown16 = s.ByteArrayHex(nameof(Unknown16), save.Unknown16, 0x184);    // 16

            save.ActiveMembers = s.List(
                nameof(ActiveMembers),
                save.ActiveMembers,
                MaxPartySize,
                (i, x, s2) =>
            {
                var value = s2.TransformEnumU8(
                    null,
                    save.ActiveMembers[i],
                    StoreIncrementedNullZero <PartyCharacterId> .Instance);
                s2.UInt8("dummy", 0);
                return(value);
            });

            save.Unknown1A6       = s.ByteArrayHex(nameof(Unknown1A6), save.Unknown1A6, 0xD0);                   // 1A6
            save._switches.Packed = s.ByteArrayHex(nameof(Switches), save._switches.Packed, FlagSet.PackedSize); // 276
            save.Unknown2C1       = s.ByteArrayHex(nameof(Unknown2C1), save.Unknown2C1, 0x5833);                 // 0x2C1
            s.Meta(nameof(Tickers), save._tickers.Serdes, save._tickers.Serdes);                                 // 5AF4

            save.Unknown5B9F = s.ByteArrayHex(nameof(Unknown5B9F), save.Unknown5B9F, 0x2C);
            s.List(nameof(save.Npcs), save.Npcs, MaxNpcCount, NpcState.Serdes);

            save.Unknown5B71 = s.ByteArrayHex(
                nameof(Unknown5B71),
                save.Unknown5B71,
                (int)(0x947C + versionOffset - s.Offset)); // 5B9F

            uint   permChangesSize  = s.UInt32("PermanentMapChanges_Size", (uint)(save.PermanentMapChanges.Count * MapChange.SizeOnDisk + 2));
            ushort permChangesCount = s.UInt16("PermanentMapChanges_Count", (ushort)save.PermanentMapChanges.Count);

            ApiUtil.Assert(permChangesSize == permChangesCount * MapChange.SizeOnDisk + 2);
            save.PermanentMapChanges = s.List(nameof(PermanentMapChanges), save.PermanentMapChanges, permChangesCount, MapChange.Serdes);

            uint   tempChangesSize  = s.UInt32("TemporaryMapChanges_Size", (uint)(save.TemporaryMapChanges.Count * MapChange.SizeOnDisk + 2));
            ushort tempChangesCount = s.UInt16("TemporaryMapChanges_Count", (ushort)save.TemporaryMapChanges.Count);

            ApiUtil.Assert(tempChangesSize == tempChangesCount * MapChange.SizeOnDisk + 2);
            save.TemporaryMapChanges = s.List(nameof(TemporaryMapChanges), save.TemporaryMapChanges, tempChangesCount, MapChange.Serdes);

            uint   visitedEventsSize  = s.UInt32("VisitedEvents_Size", (uint)(save.VisitedEvents.Count * VisitedEvent.SizeOnDisk + 2));
            ushort visitedEventsCount = s.UInt16("VisitedEvents_Count", (ushort)save.VisitedEvents.Count);

            ApiUtil.Assert(visitedEventsSize == visitedEventsCount * VisitedEvent.SizeOnDisk + 2);
            save.VisitedEvents = s.List(nameof(VisitedEvents), save.VisitedEvents, visitedEventsCount, VisitedEvent.Serdes);

            var charLoader = new CharacterSheetLoader();

            void SerdesPartyCharacter(int i, int size, ISerializer serializer)
            {
                if (i > 0xff)
                {
                    return;
                }

                var            key      = (PartyCharacterId)i;
                CharacterSheet existing = null;

                if (size > 0 || save.PartyMembers.TryGetValue(key, out existing))
                {
                    save.PartyMembers[key] = charLoader.Serdes(
                        existing,
                        serializer,
                        key.ToAssetId(),
                        new BasicAssetInfo {
                        Id = i
                    });
                }
            }

            void SerdesNpcCharacter(int i, int size, ISerializer serializer)
            {
                if (i > 0xff)
                {
                    return;
                }

                var            key      = (NpcCharacterId)i;
                CharacterSheet existing = null;

                if (serializer.Mode == SerializerMode.Reading || save.NpcStats.TryGetValue(key, out existing))
                {
                    save.NpcStats[key] = charLoader.Serdes(
                        existing,
                        serializer,
                        key.ToAssetId(),
                        new BasicAssetInfo {
                        Id = i
                    });
                }
            }

            void SerdesAutomap(int i, int size, ISerializer serializer)
            {
                var key = (AutoMapId)i;

                if (save.Automaps.TryGetValue(key, out _))
                {
                    serializer.ByteArray(null, save.Automaps[key], save.Automaps[key].Length);
                }
                else if (serializer.Mode == SerializerMode.Reading)
                {
                    save.Automaps[key] = serializer.ByteArray(null, null, size);
                }
            }

            void SerdesChest(int i, int size, ISerializer serializer)
            {
                var       key      = (ChestId)i;
                Inventory existing = null;

                if (serializer.Mode == SerializerMode.Reading || save.Chests.TryGetValue(key, out existing))
                {
                    save.Chests[key] = Inventory.SerdesChest(i, existing, serializer);
                }
            }

            void SerdesMerchant(int i, int size, ISerializer serializer)
            {
                if (i > 0xff)
                {
                    return;
                }

                var       key      = (MerchantId)i;
                Inventory existing = null;

                if (serializer.Mode == SerializerMode.Reading || save.Merchants.TryGetValue(key, out existing))
                {
                    save.Merchants[key] = Inventory.SerdesMerchant(i, existing, serializer);
                }
            }

            var partyIds = save.PartyMembers.Keys.Select(x => (int)x).ToList();

            partyIds.Add(199); // Force extra XLD length fields to be written for empty objects to preserve compat with original game.
            partyIds.Add(299);
            XldLoader.Serdes(XldCategory.PartyCharacter, 0, s, SerdesPartyCharacter, partyIds);
            XldLoader.Serdes(XldCategory.PartyCharacter, 1, s, SerdesPartyCharacter, partyIds);
            XldLoader.Serdes(XldCategory.PartyCharacter, 2, s, SerdesPartyCharacter, partyIds);

            var automapIds = save.Automaps.Keys.Select(x => (int)x).ToList();

            automapIds.Add(199);
            automapIds.Add(399);
            XldLoader.Serdes(XldCategory.Automap, 1, s, SerdesAutomap, automapIds);
            XldLoader.Serdes(XldCategory.Automap, 2, s, SerdesAutomap, automapIds);
            XldLoader.Serdes(XldCategory.Automap, 3, s, SerdesAutomap, automapIds);

            var chestIds = save.Chests.Keys.Select(x => (int)x).ToList();

            chestIds.Add(199);
            chestIds.Add(599);
            XldLoader.Serdes(XldCategory.Chest, 0, s, SerdesChest, chestIds);
            XldLoader.Serdes(XldCategory.Chest, 1, s, SerdesChest, chestIds);
            XldLoader.Serdes(XldCategory.Chest, 2, s, SerdesChest, chestIds);
            XldLoader.Serdes(XldCategory.Chest, 5, s, SerdesChest, chestIds);

            var merchantIds = save.Merchants.Keys.Select(x => (int)x).ToList();

            merchantIds.Add(199);
            merchantIds.Add(299);
            XldLoader.Serdes(XldCategory.Merchant, 0, s, SerdesMerchant, merchantIds);
            XldLoader.Serdes(XldCategory.Merchant, 1, s, SerdesMerchant, merchantIds);
            XldLoader.Serdes(XldCategory.Merchant, 2, s, SerdesMerchant, merchantIds);

            var npcIds = save.NpcStats.Keys.Select(x => (int)x).ToList();

            npcIds.Add(299);
            XldLoader.Serdes(XldCategory.NpcCharacter, 0, s, SerdesNpcCharacter, npcIds);
            XldLoader.Serdes(XldCategory.NpcCharacter, 1, s, SerdesNpcCharacter, npcIds);
            XldLoader.Serdes(XldCategory.NpcCharacter, 2, s, SerdesNpcCharacter, npcIds);

            s.RepeatU8("Padding", 0, 4);
            s.End();

            return(save);
        }
예제 #2
0
        public static SavedGame Serdes(SavedGame save, AssetMapping mapping, ISerializer s)
        {
            if (s == null)
            {
                throw new ArgumentNullException(nameof(s));
            }
            save ??= new SavedGame();

            ushort nameLength = s.UInt16("NameLength", (ushort)(save.Name?.Length ?? 0));

            save.Unk0 = s.UInt16(nameof(Unk0), save.Unk0);
            save.Name = s.FixedLengthString(nameof(Name), save.Name, nameLength);

            save.Unk1 = s.UInt32(nameof(Unk1), save.Unk1);
            var versionOffset = s.Offset;

            save.Version = s.UInt16(nameof(Version), save.Version);                 // 0
            ApiUtil.Assert(save.Version == 138);                                    // TODO: Throw error for other versions?
            save.Unk9 = s.ByteArray(nameof(Unk9), save.Unk9, 6);                    // 2

            ushort days    = s.UInt16("Days", (ushort)save.ElapsedTime.TotalDays);  // 8
            ushort hours   = s.UInt16("Hours", (ushort)save.ElapsedTime.Hours);     // A
            ushort minutes = s.UInt16("Minutes", (ushort)save.ElapsedTime.Minutes); // C

            save.ElapsedTime    = new TimeSpan(days, hours, minutes, save.ElapsedTime.Seconds, save.ElapsedTime.Milliseconds);
            save.MapId          = MapId.SerdesU16(nameof(MapId), save.MapId, mapping, s); // E
            save.PartyX         = s.UInt16(nameof(PartyX), save.PartyX);                  // 10
            save.PartyY         = s.UInt16(nameof(PartyY), save.PartyY);                  // 12
            save.PartyDirection = s.EnumU16(nameof(PartyDirection), save.PartyDirection); // 14

            save.Unknown16 = s.ByteArrayHex(nameof(Unknown16), save.Unknown16, 0x184);    // 16

            save.ActiveMembers = s.List(
                nameof(ActiveMembers),
                save.ActiveMembers,
                MaxPartySize,
                (i, x, s2) =>
            {
                var value = PartyMemberId.SerdesU8(null, save.ActiveMembers[i], mapping, s);
                s2.UInt8("dummy", 0);
                return(value);
            });

            save.Misc = s.Object(nameof(Misc), save.Misc, MiscState.Serdes); // 1A6
            save._switches.SetPacked(0,
                                     s.ByteArrayHex("Switches",
                                                    save._switches.GetPacked(0, FlagDictionary.OriginalSaveGameMax, mapping),
                                                    FlagDictionary.PackedSize(0, FlagDictionary.OriginalSaveGameMax)), mapping); // 276

            save.Unknown2C1 = s.ByteArrayHex(nameof(Unknown2C1), save.Unknown2C1, 0x5833);                                       // 0x2C1
            s.Object(nameof(Tickers), save._tickers, TickerDictionary.Serdes);                                                   // 5AF4

            save.Unknown5B9F = s.ByteArrayHex(nameof(Unknown5B9F), save.Unknown5B9F, 0x2C);
            var mapType = MapType.TwoD;

            s.List(nameof(save.Npcs), save.Npcs, (mapType, mapping), MaxNpcCount, NpcState.Serdes);

            save.Unknown5B71 = s.ByteArrayHex(
                nameof(Unknown5B71),
                save.Unknown5B71,
                (int)(0x947C + versionOffset - s.Offset)); // 5B9F

            uint   permChangesSize  = s.UInt32("PermanentMapChanges_Size", (uint)(save.PermanentMapChanges.Count * MapChange.SizeOnDisk + 2));
            ushort permChangesCount = s.UInt16("PermanentMapChanges_Count", (ushort)save.PermanentMapChanges.Count);

            ApiUtil.Assert(permChangesSize == permChangesCount * MapChange.SizeOnDisk + 2);
            save.PermanentMapChanges = (MapChangeCollection)s.List(
                nameof(PermanentMapChanges),
                save.PermanentMapChanges,
                mapping,
                permChangesCount,
                MapChange.Serdes,
                _ => new MapChangeCollection());

            uint   tempChangesSize  = s.UInt32("TemporaryMapChanges_Size", (uint)(save.TemporaryMapChanges.Count * MapChange.SizeOnDisk + 2));
            ushort tempChangesCount = s.UInt16("TemporaryMapChanges_Count", (ushort)save.TemporaryMapChanges.Count);

            ApiUtil.Assert(tempChangesSize == tempChangesCount * MapChange.SizeOnDisk + 2);

            save.TemporaryMapChanges = (MapChangeCollection)s.List(
                nameof(TemporaryMapChanges),
                save.TemporaryMapChanges,
                mapping,
                tempChangesCount,
                MapChange.Serdes,
                _ => new MapChangeCollection());

            uint   visitedEventsSize  = s.UInt32("VisitedEvents_Size", (uint)(save.VisitedEvents.Count * VisitedEvent.SizeOnDisk + 2));
            ushort visitedEventsCount = s.UInt16("VisitedEvents_Count", (ushort)save.VisitedEvents.Count);

            ApiUtil.Assert(visitedEventsSize == visitedEventsCount * VisitedEvent.SizeOnDisk + 2);
            save.VisitedEvents = s.List(nameof(VisitedEvents), save.VisitedEvents, mapping, visitedEventsCount, VisitedEvent.Serdes);

            var partyIds = save.Sheets.Keys.Select(x => x.Id).ToList();

            partyIds.Add(199); // Force extra XLD length fields to be written for empty objects to preserve compat with original game.
            partyIds.Add(299);

            // s.Object($"XldPartyCharacter.0");

            XldContainerLoader.Serdes(XldCategory.PartyCharacter, 0, (save, mapping), s, SerdesPartyCharacter, partyIds);
            XldContainerLoader.Serdes(XldCategory.PartyCharacter, 1, (save, mapping), s, SerdesPartyCharacter, partyIds);
            XldContainerLoader.Serdes(XldCategory.PartyCharacter, 2, (save, mapping), s, SerdesPartyCharacter, partyIds);

            var automapIds = save.Automaps.Keys.Select(x => x.Id).ToList(); // TODO: Allow extension somehow

            automapIds.Add(199);
            automapIds.Add(399);
            XldContainerLoader.Serdes(XldCategory.Automap, 1, (save, mapping), s, SerdesAutomap, automapIds);
            XldContainerLoader.Serdes(XldCategory.Automap, 2, (save, mapping), s, SerdesAutomap, automapIds);
            XldContainerLoader.Serdes(XldCategory.Automap, 3, (save, mapping), s, SerdesAutomap, automapIds);

            var chestIds = save.Inventories.Keys.Select(x => x.Id).ToList(); // TODO: Allow extension somehow

            chestIds.Add(199);
            chestIds.Add(599);
            XldContainerLoader.Serdes(XldCategory.Chest, 0, (save, mapping), s, SerdesChest, chestIds);
            XldContainerLoader.Serdes(XldCategory.Chest, 1, (save, mapping), s, SerdesChest, chestIds);
            XldContainerLoader.Serdes(XldCategory.Chest, 2, (save, mapping), s, SerdesChest, chestIds);
            XldContainerLoader.Serdes(XldCategory.Chest, 5, (save, mapping), s, SerdesChest, chestIds);

            var merchantIds = save.Inventories.Keys.Select(x => x.Id).ToList(); // TODO: Allow extension somehow

            merchantIds.Add(199);
            merchantIds.Add(299);
            XldContainerLoader.Serdes(XldCategory.Merchant, 0, (save, mapping), s, SerdesMerchant, merchantIds);
            XldContainerLoader.Serdes(XldCategory.Merchant, 1, (save, mapping), s, SerdesMerchant, merchantIds);
            XldContainerLoader.Serdes(XldCategory.Merchant, 2, (save, mapping), s, SerdesMerchant, merchantIds);

            var npcIds = save.Sheets.Keys.Select(x => x.Id).ToList(); // TODO: Allow extension somehow

            npcIds.Add(299);
            XldContainerLoader.Serdes(XldCategory.NpcCharacter, 0, (save, mapping), s, SerdesNpcCharacter, npcIds);
            XldContainerLoader.Serdes(XldCategory.NpcCharacter, 1, (save, mapping), s, SerdesNpcCharacter, npcIds);
            XldContainerLoader.Serdes(XldCategory.NpcCharacter, 2, (save, mapping), s, SerdesNpcCharacter, npcIds);

            s.RepeatU8("Padding", 0, 4);

            // TODO: Save additional sheets & inventories from mods.

            return(save);
        }