mirror of
https://github.com/kwsch/NHSE.git
synced 2026-04-03 15:34:55 -05:00
Add: Campsite Editor to Map tab. Allows users to set whether there is or is not a villager visiting the campsite, allows selecting that visitor and to edit the visit timestamp (defaults to current timestamp override so visitor is there on same day load). Back-tracked offsets from old Cylindircal Earth save schemas for old save revs. If campsite is not unlocked on the save, the UI is locked out with a message. Cleanup: Moved UpdateFruitsFlag to misc editor class over save class because that was bad form. Oops.
219 lines
8.5 KiB
C#
219 lines
8.5 KiB
C#
using System;
|
|
|
|
namespace NHSE.Core;
|
|
|
|
/// <summary>
|
|
/// Offset info and object retrieval logic for <see cref="MainSave"/>
|
|
/// </summary>
|
|
public abstract class MainSaveOffsets
|
|
{
|
|
public const int PlayerCount = 8;
|
|
public const int VillagerCount = 10;
|
|
public virtual int PatternCount => PatternCount1;
|
|
protected const int PatternCount1 = 50;
|
|
protected const int PatternCount2 = 100;
|
|
public const int PatternTailorCount = 8;
|
|
public const int BuildingCount = 46;
|
|
public const int RecycleBinCount = 40;
|
|
|
|
public abstract int Animal { get; }
|
|
|
|
public abstract int LandMyDesign { get; }
|
|
public abstract int PatternsPRO { get; }
|
|
public abstract int PatternFlag { get; }
|
|
public abstract int PatternTailor { get; }
|
|
|
|
public abstract int PatternsEditFlagStart { get; }
|
|
public abstract int PatternsProEditFlagStart { get; }
|
|
|
|
public abstract int WeatherArea { get; }
|
|
public abstract int WeatherRandSeed { get; }
|
|
|
|
public abstract int MainFieldStructure { get; }
|
|
|
|
public abstract int EventFlagLand { get; }
|
|
public abstract int FruitFlags { get; }
|
|
public abstract int FieldItem { get; }
|
|
public abstract int LandMakingMap { get; }
|
|
public abstract int OutsideField { get; }
|
|
public abstract int MyDesignMap { get; }
|
|
public abstract int PlayerHouseList { get; }
|
|
public abstract int NpcHouseList { get; }
|
|
public abstract int ShopKabu { get; }
|
|
public abstract int Museum { get; }
|
|
public abstract int Visitor { get; }
|
|
|
|
public abstract int SaveFg { get; }
|
|
public abstract int SpecialtyFruit { get; }
|
|
public abstract int SisterFruit { get; }
|
|
public abstract int SisterFlower { get; }
|
|
public abstract int SpecialtyFlower { get; }
|
|
public abstract int BulletinBoard { get; }
|
|
public abstract int AirportThemeColor { get; }
|
|
public abstract int GSaveCampSite { get; }
|
|
public abstract int GSaveNpcCamp { get; }
|
|
public abstract int CampLastVisitTime { get; }
|
|
|
|
public abstract int LostItemBox { get; }
|
|
public abstract int LastSavedTime { get; }
|
|
|
|
public abstract int VillagerSize { get; }
|
|
public abstract int VillagerHouseSize { get; }
|
|
public abstract int PlayerHouseSize { get; }
|
|
public abstract int PlayerRoomSize { get; }
|
|
|
|
public virtual int AcreColumnCount => 7;
|
|
|
|
public abstract IVillager ReadVillager(Memory<byte> data);
|
|
public abstract IVillagerHouse ReadVillagerHouse(Memory<byte> data);
|
|
public abstract IPlayerHouse ReadPlayerHouse(Memory<byte> data);
|
|
public abstract IPlayerRoom ReadPlayerRoom(Memory<byte> data);
|
|
|
|
public static MainSaveOffsets GetOffsets(FileHeaderInfo Info)
|
|
{
|
|
var rev = Info.GetKnownRevisionIndex();
|
|
return rev switch
|
|
{
|
|
0 => new MainSaveOffsets10(),
|
|
1 => new MainSaveOffsets11(),
|
|
2 => new MainSaveOffsets11(),
|
|
3 => new MainSaveOffsets11(),
|
|
4 => new MainSaveOffsets11(),
|
|
5 => new MainSaveOffsets11(),
|
|
6 => new MainSaveOffsets12(),
|
|
7 => new MainSaveOffsets12(),
|
|
8 => new MainSaveOffsets13(),
|
|
9 => new MainSaveOffsets13(),
|
|
10 => new MainSaveOffsets14(),
|
|
11 => new MainSaveOffsets14(),
|
|
12 => new MainSaveOffsets14(),
|
|
13 => new MainSaveOffsets15(),
|
|
14 => new MainSaveOffsets15(),
|
|
15 => new MainSaveOffsets16(),
|
|
16 => new MainSaveOffsets17(),
|
|
17 => new MainSaveOffsets18(),
|
|
18 => new MainSaveOffsets19(),
|
|
19 => new MainSaveOffsets110(),
|
|
20 => new MainSaveOffsets111(),
|
|
21 => new MainSaveOffsets111(),
|
|
22 => new MainSaveOffsets20(),
|
|
23 => new MainSaveOffsets20(),
|
|
24 => new MainSaveOffsets20(),
|
|
25 => new MainSaveOffsets20(),
|
|
26 => new MainSaveOffsets20(),
|
|
27 => new MainSaveOffsets20(),
|
|
28 => new MainSaveOffsets20(),
|
|
29 => new MainSaveOffsets20(),
|
|
30 => new MainSaveOffsets20(),
|
|
31 => new MainSaveOffsets30(),
|
|
_ => throw new IndexOutOfRangeException("Unknown revision!" + Environment.NewLine + Info),
|
|
};
|
|
}
|
|
|
|
public DesignPattern ReadPattern(ReadOnlySpan<byte> data, int index)
|
|
{
|
|
if ((uint)index >= PatternCount)
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
return ReadPatternAtOffset(data, LandMyDesign + (index * DesignPattern.SIZE));
|
|
}
|
|
|
|
public static DesignPattern ReadPatternAtOffset(ReadOnlySpan<byte> data, int offset)
|
|
{
|
|
var v = data.Slice(offset, DesignPattern.SIZE).ToArray();
|
|
return new DesignPattern(v);
|
|
}
|
|
|
|
public void WritePattern(DesignPattern p, Span<byte> data, int index, Span<byte> playerID, Span<byte> townID)
|
|
{
|
|
if ((uint)index >= PatternCount)
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
playerID.CopyTo(p.Data[0x54..]); // overwrite playerID bytes so player owns
|
|
townID.CopyTo(p.Data[0x38..]); // overwrite townID bytes so player owns
|
|
ReadOnlySpan<byte> wipeflag = [0x02, 0xEE, 0x00, 0x00]; // wipe so player owns
|
|
wipeflag.CopyTo(p.Data[0x70..]);
|
|
p.Data.CopyTo(data[(LandMyDesign + (index * DesignPattern.SIZE))..]);
|
|
ReadOnlySpan<byte> editedflag = [0x00];
|
|
editedflag.CopyTo(data[(PatternsEditFlagStart + index)..]); // set edited flag for name import to work
|
|
}
|
|
|
|
public DesignPatternPRO ReadPatternPRO(ReadOnlySpan<byte> data, int index)
|
|
{
|
|
if ((uint)index >= PatternCount)
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
var ofs = PatternsPRO + (index * DesignPatternPRO.SIZE);
|
|
return ReadPatternPROAtOffset(data, ofs);
|
|
}
|
|
|
|
public static DesignPatternPRO ReadPatternPROAtOffset(ReadOnlySpan<byte> data, int ofs)
|
|
{
|
|
var v = data.Slice(ofs, DesignPatternPRO.SIZE).ToArray();
|
|
return new DesignPatternPRO(v);
|
|
}
|
|
|
|
public void WritePatternPRO(DesignPatternPRO p, Span<byte> data, int index, Span<byte> playerID, Span<byte> townID)
|
|
{
|
|
if ((uint)index >= PatternCount)
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
playerID.CopyTo(p.Data[0x54..]); // overwrite playerID bytes so player owns
|
|
townID.CopyTo(p.Data[0x38..]); // overwrite townID bytes so player owns
|
|
ReadOnlySpan<byte> wipeflag = [0x00, 0x00, 0x00, 0x00]; // wipe so player owns
|
|
wipeflag.CopyTo(p.Data[0x70..]);
|
|
p.Data.CopyTo(data[(PatternsPRO + (index * DesignPatternPRO.SIZE))..]);
|
|
ReadOnlySpan<byte> editedflag = [0x00];
|
|
editedflag.CopyTo(data[(PatternsProEditFlagStart + index)..]); // set edited flag for name import to work
|
|
}
|
|
|
|
public IVillager ReadVillager(ReadOnlySpan<byte> data, int index)
|
|
{
|
|
if ((uint)index >= VillagerCount)
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
|
var size = VillagerSize;
|
|
var v = data.Slice(Animal + (index * size), size).ToArray();
|
|
return ReadVillager(v);
|
|
}
|
|
|
|
public void WriteVillager(IVillager v, Span<byte> data, int index)
|
|
{
|
|
if ((uint)index >= VillagerCount)
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
var size = VillagerSize;
|
|
v.Write().CopyTo(data[(Animal + (index * size))..]);
|
|
}
|
|
|
|
public IVillagerHouse ReadVillagerHouse(ReadOnlySpan<byte> data, int index)
|
|
{
|
|
if ((uint)index >= VillagerCount)
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
|
var size = VillagerHouseSize;
|
|
var v = data.Slice(NpcHouseList + (index * size), size).ToArray();
|
|
return ReadVillagerHouse(v);
|
|
}
|
|
|
|
public void WriteVillagerHouse(IVillagerHouse v, Span<byte> data, int index)
|
|
{
|
|
if ((uint)index >= VillagerCount)
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
var size = VillagerHouseSize;
|
|
v.Write().CopyTo(data[(NpcHouseList + (index * size))..]);
|
|
}
|
|
|
|
public IPlayerHouse ReadPlayerHouse(ReadOnlySpan<byte> data, int index)
|
|
{
|
|
if ((uint)index >= PlayerCount)
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
|
var size = PlayerHouseSize;
|
|
var v = data.Slice(PlayerHouseList + (index * size), size).ToArray();
|
|
return ReadPlayerHouse(v);
|
|
}
|
|
|
|
public void WritePlayerHouse(IPlayerHouse v, Span<byte> data, int index)
|
|
{
|
|
if ((uint)index >= PlayerCount)
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
var size = PlayerHouseSize;
|
|
v.Write().CopyTo(data[(PlayerHouseList + (index * size))..]);
|
|
}
|
|
} |