Skip to content

Commit

Permalink
Some additions for the new data loaders
Browse files Browse the repository at this point in the history
  • Loading branch information
Pyrdacor committed Mar 3, 2023
1 parent 4b4a43b commit 2832d22
Show file tree
Hide file tree
Showing 11 changed files with 106 additions and 48 deletions.
2 changes: 1 addition & 1 deletion Ambermoon.Data.Common/Places.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class Places
{
public List<Place> Entries { get; } = new List<Place>();

private Places()
public Places()
{

}
Expand Down
3 changes: 3 additions & 0 deletions Ambermoon.Data.Legacy/Serialization/DataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ public string ReadString(int length)

public string ReadString(int length, Encoding encoding)
{
if (length == 0)
return string.Empty;

CheckOutOfRange(length);
var str = encoding.GetString(data, Position, length);
str = str.Replace(encoding.GetString(new byte[] { 0xb4 }), "'");
Expand Down
2 changes: 2 additions & 0 deletions Ambermoon.Data.Legacy/Serialization/DataSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@

namespace Ambermoon.Data.Legacy.Serialization
{
#pragma warning disable CS8981
using word = UInt16;
using dword = UInt32;
using qword = UInt64;
#pragma warning restore CS8981

public class DataSerializer : IDataSerializer
{
Expand Down
2 changes: 1 addition & 1 deletion Ambermoon.Data.Pyrdacor/FileHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static bool CheckHeader(IDataReader dataReader, string header, bool skipI

bool match = dataReader.ReadString(header.Length) == header;

if (!skipIfMatch)
if (!skipIfMatch || !match)
dataReader.Position = position;

return match;
Expand Down
35 changes: 29 additions & 6 deletions Ambermoon.Data.Pyrdacor/GameData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ public class GameData : IGameData, IGraphicProvider
LazyContainerLoader<Texts, TextList> npcTextLoader;
LazyContainerLoader<Texts, TextList> partyTextLoader;
LazyContainerLoader<ItemData, Item> itemLoader;
LazyContainerLoader<Texts, TextList> itemNameLoader;
LazyContainerLoader<Texts, string> itemNameLoader;
LazyContainerLoader<Texts, TextList> itemTextLoader;
LazyContainerLoader<LocationData, Place> locationLoader;
LazyContainerLoader<Texts, TextList> locationNameLoader;
LazyContainerLoader<Texts, string> locationNameLoader;
LazyContainerLoader<TilesetData, Tileset> tilesetLoader;
LazyFileLoader<Texts, TextList> gotoPointNameLoader;
readonly Dictionary<string, Action<IDataReader>> fileHandlers = new Dictionary<string, Action<IDataReader>>();
Expand All @@ -35,6 +35,8 @@ public class GameData : IGameData, IGraphicProvider
readonly Lazy<Font> outroLargeFont;
readonly Lazy<Font> introSmallFont;
readonly Lazy<Font> introLargeFont;
readonly Lazy<Places> places;
readonly Lazy<IngameFontProvider> ingameFontProvider;

public bool Loaded { get; } = false;

Expand All @@ -56,11 +58,11 @@ public class GameData : IGameData, IGraphicProvider

public IReadOnlyList<Position> CursorHotspots => throw new NotImplementedException();

public Places Places => throw new NotImplementedException();
public Places Places => places!.Value;

public IItemManager ItemManager => itemManager!.Value;

public IFontProvider FontProvider => throw new NotImplementedException();
public IFontProvider FontProvider => ingameFontProvider!.Value;

public IDataNameProvider DataNameProvider => throw new NotImplementedException();

Expand Down Expand Up @@ -176,6 +178,27 @@ public GameData(Stream stream)
outroLargeFont = new Lazy<Font>(() => fontLoader!.Load(FontData.OutroLargeFontIndex));
introSmallFont = new Lazy<Font>(() => fontLoader!.Load(FontData.IntroSmallFontIndex));
introLargeFont = new Lazy<Font>(() => fontLoader!.Load(FontData.IntroLargeFontIndex));
ingameFontProvider = new Lazy<IngameFontProvider>(() => new(ingameFont!.Value));
places = new Lazy<Places>(() =>
{
var places = new Places();
var locationData = locationLoader!.LoadAll();
var locationNames = locationNameLoader!.LoadAll();

if (locationData.Count != locationNames.Count)
throw new AmbermoonException(ExceptionScope.Data, "Mismatch between number of location data and location name entries.");

foreach (var location in locationData)
{
if (!locationNames.TryGetValue(location.Key, out var name))
throw new AmbermoonException(ExceptionScope.Data, $"Missing location name for location data {location.Key}.");

location.Value.Name = name;

places.Entries.Add(location.Value);
}
return places;
});

// Read all files
int fileCount = reader.ReadWord();
Expand Down Expand Up @@ -244,7 +267,7 @@ void LoadItems(IDataReader dataReader)

void LoadItemNames(IDataReader dataReader)
{
itemNameLoader = new LazyContainerLoader<Texts, TextList>(dataReader, this, t => t.TextList);
itemNameLoader = new LazyContainerLoader<Texts, string>(dataReader, this, t => t.TextList.First()!);
}

void LoadItemTexts(IDataReader dataReader)
Expand All @@ -259,7 +282,7 @@ void LoadLocations(IDataReader dataReader)

void LoadLocationNames(IDataReader dataReader)
{

locationNameLoader = new LazyContainerLoader<Texts, string>(dataReader, this, t => t.TextList.First()!);
}

void LoadOutro(IDataReader dataReader)
Expand Down
10 changes: 4 additions & 6 deletions Ambermoon.Data.Pyrdacor/LazyFileLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public LazyFileLoader(IDataReader dataReader, GameData gameData, Func<T, U> valu
{
loader = () =>
{
var item = new PADF().Read(dataReader, gameData);
var item = PADF.Read(dataReader, gameData);

if (item is T typedItem)
return typedItem;
Expand All @@ -37,22 +37,20 @@ public U Load()

public LazyContainerLoader(IDataReader dataReader, GameData gameData, Func<T, U> valueProvider)
{
loader = () => new PADP().Read<T>(dataReader, gameData);
loader = () => PADP.Read<T>(dataReader, gameData);
this.valueProvider = valueProvider;
}

public U Load(ushort key)
{
if (items == null)
items = loader();
items ??= loader();

return valueProvider(items[key]);
}

public Dictionary<uint, U> LoadAll()
{
if (items == null)
items = loader();
items ??= loader();

return items.ToDictionary(i => (uint)i.Key, i => valueProvider(i.Value));
}
Expand Down
28 changes: 26 additions & 2 deletions Ambermoon.Data.Pyrdacor/Objects/Font.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
using Ambermoon.Data.Legacy.Serialization;
using Ambermoon.Data.Serialization;
using Ambermoon.Data.Serialization;

namespace Ambermoon.Data.Pyrdacor.Objects
{
/**
* Fonts can be monospaced or regular.
* The font data always starts with a 4 byte header.
* - byte GlyphWidth
* - byte GlyphHeight
* - word GlyphCount
*
* If GlyphWidth is not 0, it is a monospace font and
* each glyph uses the given dimensions. The advance
* value equals the glyph width in this case. The glyph
* image data follows the header immediately.
*
* If GlyphWidth is 0, it is a regular font where each
* glyph has a different width and optionally also a
* different advance value. The glyph height however is
* again the same for each glyph as specified in the header.
* For each glyph an entry follows the header. The first byte
* gives the glyph width. Only the 7 lower bits are considered
* so the max glyph width is 127. A width of 0 is possible. The
* glyph is not visibile then. If the msb of the value is set,
* this means that the glyph has a custom advanced value. In
* this case another byte follows giving the advance (0 to 255).
* If the msb was instead 0 the advance value equals the given
* glyph width.
*/
internal class Font
{
readonly List<Glyph> glyphs;
Expand Down
12 changes: 12 additions & 0 deletions Ambermoon.Data.Pyrdacor/Objects/IngameFont.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,16 @@ public IngameFont(Func<Font> fontProvider, Func<Font> digitFontProvider)

public Graphic GetDigitGlyphGraphic(uint glyphIndex) => digitFont.Value.GetGlyphGraphic(glyphIndex);
}

internal class IngameFontProvider : IFontProvider
{
private readonly IngameFont ingameFont;

public IngameFontProvider(IngameFont ingameFont)
{
this.ingameFont = ingameFont;
}

public IFont GetFont() => ingameFont;
}
}
2 changes: 2 additions & 0 deletions Ambermoon.Data.Pyrdacor/Objects/TextList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public TextList(IDataReader dataReader)
texts.Add(dataReader.ReadString());
}

public string? First() => GetText(0);

public string? GetText(int index) => index >= texts.Count ? null : texts[index];

public void Write(IDataWriter dataWriter)
Expand Down
6 changes: 3 additions & 3 deletions Ambermoon.Data.Pyrdacor/PADF.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Ambermoon.Data.Pyrdacor
/// <summary>
/// Pyrdacor's Ambermoon Data File
/// </summary>
internal class PADF
internal static class PADF
{
public const string Header = "PADF";

Expand Down Expand Up @@ -43,7 +43,7 @@ static PADF()
Compressions.Add(ICompression.RLE0.Identifier, ICompression.RLE0);
}

public IFileSpec Read(IDataReader reader, GameData gameData)
public static IFileSpec Read(IDataReader reader, GameData gameData)
{
if (!FileHeader.CheckHeader(reader, Header, true))
throw new AmbermoonException(ExceptionScope.Data, "The file is no PADF");
Expand Down Expand Up @@ -72,7 +72,7 @@ public IFileSpec Read(IDataReader reader, GameData gameData)
return fileSpec;
}

public void Write(IDataWriter writer, IFileSpec fileSpec, ICompression? compression = null)
public static void Write(IDataWriter writer, IFileSpec fileSpec, ICompression? compression = null)
{
writer.WriteWithoutLength(Header);
writer.WriteWithoutLength(fileSpec.Magic);
Expand Down
52 changes: 23 additions & 29 deletions Ambermoon.Data.Pyrdacor/PADP.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,17 @@ namespace Ambermoon.Data.Pyrdacor
/// <summary>
/// Pyrdacor's Ambermoon Data Package
/// </summary>
internal class PADP
internal static class PADP
{
// Note: The max file count is 0xffff-1. As file indices are 1-based, file 0 is not valid.
// So only indices 1 to 0xffff can be stored. In sum there is space for the given file count.

public const string Header = "PADP";

bool writeNoFileIndices = false;
bool wasPADF = false;

public Dictionary<ushort, T> Read<T>(IDataReader reader, GameData gameData) where T : IFileSpec, new()
public static Dictionary<ushort, T> Read<T>(IDataReader reader, GameData gameData) where T : IFileSpec, new()
{
wasPADF = false;
var result = InternalRead(reader, gameData, IFileSpec.GetSupportedVersion<T>());
bool wasPADF = false;
var result = InternalRead(reader, gameData, ref wasPADF, IFileSpec.GetSupportedVersion<T>());

if (result.Count == 0)
return new Dictionary<ushort, T>();
Expand All @@ -38,17 +35,18 @@ internal class PADP
return result.ToDictionary(r => r.Key, r => (T)r.Value);
}

public Dictionary<ushort, IFileSpec> Read(IDataReader reader, GameData gameData)
public static Dictionary<ushort, IFileSpec> Read(IDataReader reader, GameData gameData)
{
return InternalRead(reader, gameData);
bool wasPADF = false;
return InternalRead(reader, gameData, ref wasPADF);
}

Dictionary<ushort, IFileSpec> InternalRead(IDataReader reader, GameData gameData, byte? supportedVersion = null)
private static Dictionary<ushort, IFileSpec> InternalRead(IDataReader reader, GameData gameData, ref bool wasPADF, byte? supportedVersion = null)
{
if (FileHeader.CheckHeader(reader, PADF.Header, false))
{
wasPADF = true;
var spec = new PADF().Read(reader, gameData);
var spec = PADF.Read(reader, gameData);
return new Dictionary<ushort, IFileSpec> { { (ushort)1u, spec } };
}

Expand Down Expand Up @@ -111,7 +109,20 @@ Dictionary<ushort, IFileSpec> InternalRead(IDataReader reader, GameData gameData
return files;
}

public void Write<T>(IDataWriter writer, IDictionary<ushort, T> fileSpecs, ICompression? compression = null) where T : IFileSpec, new()
public static void Write<T>(IDataWriter writer, IDictionary<ushort, T> fileSpecs, ICompression? compression = null) where T : IFileSpec, new()
{
InternalWrite<T>(writer, fileSpecs, compression, false);
}

public static void Write<T>(IDataWriter writer, IEnumerable<T> fileSpecs, ICompression? compression = null) where T : IFileSpec, new()
{
if (fileSpecs.Count() >= ushort.MaxValue)
throw new AmbermoonException(ExceptionScope.Data, $"Too many files given for PADP. Allowed are {ushort.MaxValue - 1}, given are {fileSpecs.Count()}.");

InternalWrite(writer, fileSpecs.Select((s, i) => new { i, s }).ToDictionary(s => (ushort)(1 + s.i), s => s.s), compression, true);
}

private static void InternalWrite<T>(IDataWriter writer, IDictionary<ushort, T> fileSpecs, ICompression? compression, bool writeNoFileIndices) where T : IFileSpec, new()
{
if (fileSpecs.Count >= ushort.MaxValue)
throw new AmbermoonException(ExceptionScope.Data, $"Too many files given for PADP. Allowed are {ushort.MaxValue - 1}, given are {fileSpecs.Count}.");
Expand Down Expand Up @@ -164,22 +175,5 @@ Dictionary<ushort, IFileSpec> InternalRead(IDataReader reader, GameData gameData
// Write data
writer.Write(dataWriter.ToArray());
}

public void Write<T>(IDataWriter writer, IEnumerable<T> fileSpecs, ICompression? compression = null) where T : IFileSpec, new()
{
if (fileSpecs.Count() >= ushort.MaxValue)
throw new AmbermoonException(ExceptionScope.Data, $"Too many files given for PADP. Allowed are {ushort.MaxValue - 1}, given are {fileSpecs.Count()}.");

writeNoFileIndices = true;

try
{
Write(writer, fileSpecs.Select((s, i) => new { i, s }).ToDictionary(s => (ushort)(1 + s.i), s => s.s), compression);
}
finally
{
writeNoFileIndices = false;
}
}
}
}

0 comments on commit 2832d22

Please sign in to comment.