Skip to content

Commit

Permalink
Added sub structs for SaveData
Browse files Browse the repository at this point in the history
  • Loading branch information
SWY1985 committed Nov 26, 2017
1 parent 0f18b8f commit bd6a238
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 75 deletions.
1 change: 1 addition & 0 deletions CivOne.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<PackageId>CivOne</PackageId>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>7.1</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="$(Configuration.StartsWith('Debug'))">
<DebugSymbols>true</DebugSymbols>
Expand Down
11 changes: 9 additions & 2 deletions src/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ public static IEnumerable<CityData> GetCityData(this IEnumerable<City> cityList)

private static UnitData GetUnitData(this IUnit unit, byte id)
{
byte gotoX = 0xFF, gotoY = 0;
if (!unit.Goto.IsEmpty)
{
gotoX = (byte)unit.Goto.X;
gotoY = (byte)unit.Goto.Y;
}

return new UnitData {
Id = id,
Status = unit.Status,
Expand All @@ -114,8 +121,8 @@ private static UnitData GetUnitData(this IUnit unit, byte id)
TypeId = (byte)unit.Type,
RemainingMoves = (byte)((unit.MovesLeft * 3) + unit.PartMoves),
SpecialMoves = 0,
GotoX = (byte)unit.Goto.X,
GotoY = (byte)unit.Goto.Y,
GotoX = gotoX,
GotoY = gotoY,
Visibility = 0xFF,
NextUnitId = id,
HomeCityId = unit.Home.GetId()
Expand Down
7 changes: 2 additions & 5 deletions src/Game.LoadSave.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using CivOne.Civilizations;
Expand Down Expand Up @@ -188,11 +189,6 @@ private Game(IGameData gameData)
cityList.Add(cityData.Id, city);
}

foreach (Player player in _players)
{
player.CityNameCounter = _cities.Count(c => c.Owner == PlayerNumber(player)) - 1;
}

UnitData[][] unitData = gameData.Units;
for (byte p = 0; p < 8; p++)
{
Expand All @@ -205,6 +201,7 @@ private Game(IGameData gameData)
unit.Owner = p;
unit.PartMoves = (byte)(data.RemainingMoves % 3);
unit.MovesLeft = (byte)((data.RemainingMoves - unit.PartMoves) / 3);
if (data.GotoX != 0xFF) unit.Goto = new Point(data.GotoX, data.GotoY);
if (cityList.ContainsKey(data.HomeCityId))
{
unit.SetHome(cityList[data.HomeCityId]);
Expand Down
4 changes: 2 additions & 2 deletions src/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,9 @@ internal int CityNameId(Player player)
.ThenBy(i => (i >= spareIndex) ? 0 : 1)
.ThenBy(i => i)
.ToArray();
if (player.CityNameCounter >= available.Length)
if (player.CityNamesSkipped >= available.Length)
return 0;
return available[player.CityNameCounter++];
return available[player.CityNamesSkipped];
}

internal City AddCity(Player player, int nameId, int x, int y)
Expand Down
34 changes: 34 additions & 0 deletions src/IO/SaveData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,40 @@ namespace CivOne.IO
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct SaveData
{
[StructLayout(LayoutKind.Sequential)]
internal struct City
{
public fixed byte Buildings[4]; // 0:3
public byte X, Y; // 4:5
public byte Status; // 6
public byte ActualSize; // 7
public byte VisibleSize; // 8
public byte CurrentProduction; // 9
public byte BaseTrade; // 10
public byte Owner; // 11
public ushort Food; // 12:13
public ushort Shields; // 14:15
public fixed byte ResourceTiles[6]; // 16:21
public byte NameId; // 22
public fixed byte TradingCities[3]; // 23:25
private fixed byte _padding[2]; // 27
}

[StructLayout(LayoutKind.Sequential)]
internal struct Unit
{
public byte Status; // 0
public byte X, Y; // 1:2
public byte Type; // 3
public byte RemainingMoves; // 4
public byte SpecialMoves; // 5
public byte GotoX, GotoY; // 6:7
private byte _padding1; // 8
public byte Visibility; // 9
public byte NextUnitId; // 10
public byte HomeCityId; // 11
}

public ushort GameTurn; // 0:1
public ushort HumanPlayer; // 2:3
public ushort HumanPlayerBit; // 4:5
Expand Down
2 changes: 1 addition & 1 deletion src/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public void SetGarden(int index, byte level)

private int _destroyTurn = -1;

internal int CityNameCounter = 0;
internal int CityNamesSkipped = 0;

internal short StartX { get; set; }

Expand Down
98 changes: 63 additions & 35 deletions src/SaveDataAdapter.Get.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,24 @@ namespace CivOne
{
internal partial class SaveDataAdapter
{
private void GetByteArray(string fieldName, ref byte[] bytes)
private void GetByteArray<T>(T structure, string fieldName, ref byte[] bytes) where T : struct
{
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<SaveData>());
Marshal.StructureToPtr(_saveData, ptr, false);
IntPtr offset = IntPtr.Add(ptr, (int)Marshal.OffsetOf<SaveData>(fieldName));
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<T>());
Marshal.StructureToPtr(structure, ptr, false);
IntPtr offset = IntPtr.Add(ptr, (int)Marshal.OffsetOf<T>(fieldName));
Marshal.Copy(offset, bytes, 0, bytes.Length);
Marshal.FreeHGlobal(ptr);
}

private void GetByteArray(string fieldName, ref byte[] bytes) => GetByteArray<SaveData>(_saveData, fieldName, ref bytes);

private byte[] GetBytes<T>(T structure, string fieldName, int length) where T : struct
{
byte[] output = new byte[length];
GetByteArray<T>(structure, fieldName, ref output);
return output;
}

private byte[] GetArray(string fieldName, int length)
{
byte[] output = new byte[length];
Expand All @@ -45,7 +54,26 @@ private string[] GetArray(string fieldName, int itemLength, int itemCount)
private T[] GetArray<T>(string fieldName, int length)
{
T[] output = new T[length];
Buffer.BlockCopy(GetArray(fieldName, length * Marshal.SizeOf<T>()), 0, output, 0, length);
switch (output)
{
case SaveData.City[] _:
case SaveData.Unit[] _:
int itemSize = Marshal.SizeOf<T>();
byte[] buffer = new byte[length * itemSize];
Buffer.BlockCopy(GetArray(fieldName, buffer.Length), 0, buffer, 0, buffer.Length);

IntPtr ptr = Marshal.AllocHGlobal(itemSize);
for (int i = 0; i < output.Length; i++)
{
Marshal.Copy(buffer, (i * itemSize), ptr, itemSize);
output[i] = Marshal.PtrToStructure<T>(ptr);
}
Marshal.FreeHGlobal(ptr);
break;
default:
Buffer.BlockCopy(GetArray(fieldName, length * Marshal.SizeOf<T>()), 0, output, 0, length);
break;
}
return output;
}

Expand Down Expand Up @@ -80,61 +108,61 @@ private byte[][] GetDiscoveredAdvanceIDs()

private CityData[] GetCities()
{
byte[] bytes = GetArray(nameof(SaveData.Cities), 28 * 128);
SaveData.City[] cities = GetArray<SaveData.City>(nameof(SaveData.Cities), 128);

List<CityData> output = new List<CityData>();

for (byte c = 0; c < 128; c++)
for (byte c = 0; c < cities.Length; c++)
{
int offset = 28 * c;
if (bytes[offset + 6] == 0xFF) continue;
SaveData.City city = cities[c];
if (city.Status == 0xFF) continue;

output.Add(new CityData()
{
Id = c,
NameId = bytes[offset + 22],
Buildings = bytes.FromBitIds(offset, 4).ToArray(),
X = bytes[offset + 4],
Y = bytes[offset + 5],
Status = bytes[offset + 6],
ActualSize = bytes[offset + 7],
CurrentProduction = bytes[offset + 9],
Owner = bytes[offset + 11],
Food = (ushort)((bytes[offset + 13] << 8) + bytes[offset + 12]),
Shields = (ushort)((bytes[offset + 13] << 8) + bytes[offset + 12]),
ResourceTiles = bytes.Skip(offset + 16).Take(6).ToArray()
NameId = city.NameId,
Buildings = GetBytes<SaveData.City>(city, nameof(SaveData.City.Buildings), 4).FromBitIds(0, 4).ToArray(),
X = city.X,
Y = city.Y,
Status = city.Status,
ActualSize = city.ActualSize,
CurrentProduction = city.CurrentProduction,
Owner = city.Owner,
Food = city.Food,
Shields = city.Shields,
ResourceTiles = GetBytes<SaveData.City>(city, nameof(SaveData.City.ResourceTiles), 6)
});
}
return output.ToArray();
}

private UnitData[][] GetUnits()
{
byte[] bytes = GetArray(nameof(SaveData.Units), (8 * 128 * 12));
SaveData.Unit[] units = GetArray<SaveData.Unit>(nameof(SaveData.Units), 8 * 128);
UnitData[][] output = new UnitData[8][];

for (int p = 0; p < 8; p++)
{
List<UnitData> unitData = new List<UnitData>();

for (byte u = 0; u < 128; u++)
{
int offset = (p * 128 * 12) + (12 * u);
SaveData.Unit unit = units[(p * 128) + u];

if (bytes[offset + 3] == 0xFF) continue;
if (unit.Type == 0xFF) continue;
unitData.Add(new UnitData()
{
Id = u,
Status = bytes[offset + 0],
X = bytes[offset + 1],
Y = bytes[offset + 2],
TypeId = bytes[offset + 3],
RemainingMoves = bytes[offset + 4],
SpecialMoves = bytes[offset + 5],
GotoX = bytes[offset + 6],
GotoY = bytes[offset + 7],
Visibility = bytes[offset + 9],
NextUnitId = bytes[offset + 10],
HomeCityId = bytes[offset + 11]
Status = unit.Status,
X = unit.X,
Y = unit.Y,
TypeId = unit.Type,
RemainingMoves = unit.RemainingMoves,
SpecialMoves = unit.SpecialMoves,
GotoX = unit.GotoX,
GotoY = unit.GotoY,
Visibility = unit.Visibility,
NextUnitId = unit.NextUnitId,
HomeCityId = unit.HomeCityId
});
}
output[p] = unitData.ToArray();
Expand Down
67 changes: 38 additions & 29 deletions src/SaveDataAdapter.Set.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,32 @@ namespace CivOne
{
internal partial class SaveDataAdapter
{
private void SetArray(string fieldName, params byte[] values)
private void SetArray<T>(ref T structure, string fieldName, params byte[] values) where T : struct
{
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<SaveData>());
Marshal.StructureToPtr(_saveData, ptr, false);
IntPtr offset = IntPtr.Add(ptr, (int)Marshal.OffsetOf<SaveData>(fieldName));
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<T>());
Marshal.StructureToPtr(structure, ptr, false);
IntPtr offset = IntPtr.Add(ptr, (int)Marshal.OffsetOf<T>(fieldName));
Marshal.Copy(values, 0, offset, values.Length);
_saveData = Marshal.PtrToStructure<SaveData>(ptr);
structure = Marshal.PtrToStructure<T>(ptr);
Marshal.FreeHGlobal(ptr);
}

private void SetArray(string fieldName, params byte[] values) => SetArray<SaveData>(ref _saveData, fieldName, values);

private void SetArray<T>(string fieldName, params T[] values) where T : struct
{
int itemSize = Marshal.SizeOf<T>();
byte[] bytes = new byte[values.Length * itemSize];
for (int i = 0; i < values.Length; i++)
{
T value = values[i];
IntPtr ptr = Marshal.AllocHGlobal(itemSize);
Marshal.StructureToPtr<T>(value, ptr, false);
Marshal.Copy(ptr, bytes, (i * itemSize), itemSize);
Marshal.FreeHGlobal(ptr);
}
SetArray(fieldName, bytes);
}

private void SetArray(string fieldName, params short[] values)
{
Expand Down Expand Up @@ -103,35 +120,27 @@ private void SetUnitsActive(UnitData[][] unitData)

private void SetCities(CityData[] values)
{
byte[] bytes = GetArray(nameof(SaveData.Cities), 28 * 128);
SaveData.City[] cities = GetArray<SaveData.City>(nameof(SaveData.Cities), 128);

for (int i = 0; i < new[] { values.Length, 128 }.Min(); i++)
{
int offset = (28 * i);
CityData data = values[i];

if (data.Buildings != null) bytes = bytes.ToBitIds(offset, 4, data.Buildings);
bytes[offset + 4] = data.X;
bytes[offset + 5] = data.Y;
bytes[offset + 6] = data.Status;
bytes[offset + 7] = data.ActualSize;
bytes[offset + 8] = data.ActualSize;
bytes[offset + 9] = data.CurrentProduction;
bytes[offset + 11] = data.Owner;
bytes[offset + 12] = (byte)(data.Food & 0xFF);
bytes[offset + 13] = (byte)((data.Food & 0xFF00) >> 8);
bytes[offset + 14] = (byte)(data.Shields & 0xFF);
bytes[offset + 15] = (byte)((data.Shields & 0xFF00) >> 8);
if (data.ResourceTiles != null)
{
for (int r = 0; r < data.ResourceTiles.Length; r++)
{
bytes[offset + 16 + r] = data.ResourceTiles[r];
}
}
bytes[offset + 22] = data.NameId;
// SetArray<SaveData.City>(ref cities[i], nameof(SaveData.City.Buildings), new byte[4].ToBitIds(0, 4, data.Buildings));
SetArray<SaveData.City>(ref cities[i], nameof(SaveData.City.Buildings), new byte[4].ToBitIds(0, 4, data.Buildings));
cities[i].X = data.X;
cities[i].Y = data.Y;
cities[i].Status = data.Status;
cities[i].ActualSize = data.ActualSize;
cities[i].VisibleSize = data.ActualSize; // TODO: Implement Visible Size
cities[i].CurrentProduction = data.CurrentProduction;
cities[i].BaseTrade = 0; // TODO: Implement trade
cities[i].Owner = data.Owner;
cities[i].Food = data.Food;
cities[i].Shields = data.Shields;
SetArray<SaveData.City>(ref cities[i], nameof(SaveData.City.ResourceTiles), data.ResourceTiles);
cities[i].NameId = data.NameId;
}
SetArray(nameof(SaveData.Cities), bytes);
SetArray<SaveData.City>(nameof(SaveData.Cities), cities);
}

private void SetUnitTypes(byte[] bytes) => SetArray(nameof(SaveData.UnitTypes), bytes);
Expand Down
2 changes: 1 addition & 1 deletion src/SaveDataAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace CivOne
{
internal unsafe partial class SaveDataAdapter : IGameData
{
private CityData[] DefaultCityData => Enumerable.Range(0, 128).Select(x => new CityData() { Status = 0xFF }).ToArray();
private CityData[] DefaultCityData => Enumerable.Range(0, 128).Select(x => new CityData() { Status = 0xFF, Buildings = new byte[4], ResourceTiles = new byte[6] }).ToArray();
private UnitData[][] DefaultUnitData => Enumerable.Repeat(Enumerable.Range(0, 128).Select(id => new UnitData() { Id = (byte)id, TypeId = 0xFF }).ToArray(), 8).ToArray();

private byte[] DefaultUnitTypes => new byte[952]; // TODO
Expand Down
1 change: 1 addition & 0 deletions src/Tasks/Orders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ private void CityNameAccept(object sender, EventArgs args)

private void CityNameCancel(object sender, EventArgs args)
{
Human.CityNamesSkipped++;
_unit.MovesLeft--;
EndTask();
}
Expand Down

0 comments on commit bd6a238

Please sign in to comment.