Core - Massive DLCManager changes

This commit is contained in:
miku-666
2025-12-14 04:02:14 +01:00
parent c2cf85df70
commit 08dcfbb288
15 changed files with 478 additions and 129 deletions

View File

@@ -114,11 +114,11 @@ namespace PckStudio.Core
return new ReadOnlyCollection<Frame>(_frames);
}
public IReadOnlyCollection<Frame> GetInterpolatedFrames()
public IEnumerable<Frame> GetInterpolatedFrames()
{
if (Interpolate)
{
return new ReadOnlyCollection<Frame>(InternalGetInterpolatedFrames().ToList());
return InternalGetInterpolatedFrames();
}
return GetFrames();
}
@@ -133,7 +133,7 @@ namespace PckStudio.Core
nextFrame = _frames[i + 1];
for (int tick = 0; tick < currentFrame.Ticks; tick++)
{
double delta = 1.0f - tick / (double)currentFrame.Ticks;
float delta = 1.0f - tick / (float)currentFrame.Ticks;
yield return new Frame(currentFrame.Texture.Interpolate(nextFrame.Texture, delta));
}
}

View File

@@ -9,17 +9,21 @@ using OMI;
using OMI.Formats.Color;
using OMI.Formats.GameRule;
using OMI.Formats.Languages;
using OMI.Formats.Model;
using OMI.Formats.Pck;
using OMI.Workers.Color;
using OMI.Workers.GameRule;
using OMI.Workers.Language;
using OMI.Workers.Material;
using OMI.Workers.Model;
using OMI.Workers.Pck;
using PckStudio.Core.App;
using PckStudio.Core.Deserializer;
using PckStudio.Core.Extensions;
using PckStudio.Core.FileFormats;
using PckStudio.Core.Interfaces;
using PckStudio.Core.IO.PckAudio;
using PckStudio.Core.Model;
using PckStudio.Interfaces;
namespace PckStudio.Core.DLC
@@ -39,6 +43,8 @@ namespace PckStudio.Core.DLC
public ConsolePlatform Platform => _platform;
public DLCPackageContentSerilasationType ContentSerilasationType { get; set; } = DLCPackageContentSerilasationType.Local;
/// <summary>
/// See <see cref="AvailableLanguages"/> for details.
/// </summary>
@@ -48,6 +54,7 @@ namespace PckStudio.Core.DLC
private readonly Random _rng = new Random();
private ByteOrder _byteOrder;
private ConsolePlatform _platform;
private PckFileCompiler _pckFileCompiler;
/// <param name="byteOrder"></param>
@@ -57,6 +64,7 @@ namespace PckStudio.Core.DLC
{
_platform = platform;
_byteOrder = GetByteOrderForPlatform(Platform);
_pckFileCompiler = new PckFileCompiler(_byteOrder, GetPlatformCompressionType(), GameRuleFile.CompressionLevel.None);
SetPreferredLanguage(preferredLanguage);
}
@@ -90,7 +98,7 @@ namespace PckStudio.Core.DLC
localisation.AddLanguage(PreferredLanguage);
localisation.AddLocKey(PACKAGE_DISPLAYNAME_ID, name);
_packageRegistry.RegisterPackage(identifier, package, localisation);
return package;
}
@@ -138,9 +146,11 @@ namespace PckStudio.Core.DLC
{
_packageRegistry.RegisterPackage(identifier, package, localisation);
}
return package;
return new RawAssetDLCPackage(fileInfo.Name, pckFile, ByteOrder);
}
public bool CloseDLCPackage(int identifier) => _packageRegistry.UnregisterPackage(identifier);
internal LOCFile GetLocalisation(int identifier)
{
return _packageRegistry.ContainsPackage(identifier) ? _packageRegistry.GetLocalisation(identifier) : default;
@@ -173,47 +183,61 @@ namespace PckStudio.Core.DLC
bool couldBeMiniGamePack = fileInfo.Name == DEFAULT_MINIGAME_PACK_FILENAME;
bool hasSkins = TryGetDLCSkinPackage(name, identifier, pckFile, fileReader, out IDLCPackage skinPackage);
DLCPackageType dlcPackageType = hasSkins ? DLCPackageType.SkinPack : DLCPackageType.RawAssets;
DirectoryInfo dataDirectoryInfo = fileInfo.Directory.EnumerateDirectories().Where(dirInfo => dirInfo.Name == DATA_DIRECTORY_NAME).FirstOrDefault();
if (dataDirectoryInfo is null)
return hasSkins ? skinPackage : InvalidDLCPackage.Instance;
{
Trace.TraceInformation("No data directory found.");
return skinPackage ?? new RawAssetDLCPackage(name, pckFile, ByteOrder);
}
bool hasTexturePack = TryGetTexturePack(name, description, identifier, dataDirectoryInfo, pckFile, fileReader, out IDLCPackage texturePackage);
if (hasTexturePack)
if (!hasTexturePack)
{
dlcPackageType = DLCPackageType.TexturePack;
}
Trace.TraceWarning("Couldn't parse texturepack.");
return skinPackage ?? new RawAssetDLCPackage(name, pckFile, ByteOrder);
}
Dictionary<string, IDictionary<string, byte[]>> mapData = GetMapData(pckFile, dataDirectoryInfo);
PckAudioFile pckAudioFile = pckFile.GetAssetsByType(PckAssetType.AudioFile).FirstOrDefault()?.GetData(new PckAudioFileReader(ByteOrder));
IDictionary<string, byte[]> audios = default;
if (pckAudioFile != null)
{
var songs = pckAudioFile.Categories.SelectMany(audioCategory => audioCategory.SongNames).ToList();
audios = dataDirectoryInfo.EnumerateFiles("*.binka")
.Where(audioFile => songs.Contains(Path.GetFileNameWithoutExtension(audioFile.Name)))
.ToDictionary(audioFile => audioFile.Name, audioFile => File.ReadAllBytes(audioFile.FullName));
}
GameRuleFile.CompressionType compressionType = GetPlatformCompressionType();
var reader = new GameRuleFileReader(compressionType);
IEnumerable<GameRuleFile> gameRules = pckFile.GetAssetsByType(PckAssetType.GameRulesFile)
.Concat(pckFile.GetAssetsByType(PckAssetType.GameRulesHeader))
.Select(asset => asset.GetData(reader));
Dictionary<string, SaveData> mapData = GetMapData(gameRules, dataDirectoryInfo);
if (mapData.Count == 0)
return texturePackage ?? skinPackage ?? new RawAssetDLCPackage(name, pckFile, ByteOrder);
if (mapData.Count == 1)
{
dlcPackageType = DLCPackageType.MashUpPack;
}
Debug.WriteLine(dlcPackageType);
return new DLCMashUpPackage(name, description, identifier, null, null, pckAudioFile, audios, parentPackage: null, skinPackage: skinPackage, texturePackage: texturePackage);
return new RawAssetDLCPackage(name, pckFile, ByteOrder);
}
private Dictionary<string, IDictionary<string, byte[]>> GetMapData(PckFile pck, DirectoryInfo dataDirectory)
{
GameRuleFile.CompressionType compressionType = GetPlatformCompressionType();
var reader = new GameRuleFileReader(compressionType);
IEnumerable<string> values = pck.GetAssetsByType(PckAssetType.GameRulesFile)
.Concat(pck.GetAssetsByType(PckAssetType.GameRulesHeader))
.Select(asset => asset.GetData(reader))
private Dictionary<string, SaveData> GetMapData(IEnumerable<GameRuleFile> gameRuleFiles, DirectoryInfo dataDirectory)
{
IEnumerable<string> values = gameRuleFiles
.SelectMany(grf => grf.Root.GetRules().Where(rule => rule.Name == GRF_MAP_OPTIONS_NAME && rule.ContainsParameter(BASE_SAVE_NAME_GRF_PARAMETER_KEY)))
.Select(rule => rule.GetParameterValue(BASE_SAVE_NAME_GRF_PARAMETER_KEY));
return dataDirectory.EnumerateFiles("*.mcs")
.Where(file => values.Contains(file.Name))
.ToDictionary(worldFile => worldFile.Name, worldFile => MapReader.OpenSave(worldFile.OpenRead()));
}
.ToDictionary(worldFile => worldFile.Name, worldFile => MapReader.OpenSaveData(worldFile.OpenRead()));
}
private bool TryGetTexturePack(string name, string description, int identifier, DirectoryInfo dataDirectoryInfo, PckFile pckFile, PckFileReader pckFormatReader, out IDLCPackage texturePackage)
{
{
if (dataDirectoryInfo is null)
{
texturePackage = default;
@@ -233,13 +257,13 @@ namespace PckStudio.Core.DLC
{
texturePackage = default;
return false;
}
}
PckFile infoPck = texturePackInfo.GetData(pckFormatReader);
FileInfo texturePackFileInfo = dataDirectoryInfo.EnumerateFiles().Where(fileInfo => fileInfo.Name == dataPath).FirstOrDefault();
if (!IsValidPckFile(texturePackFileInfo))
{
{
texturePackage = null;
return false;
}
@@ -248,11 +272,8 @@ namespace PckStudio.Core.DLC
PckFile texturePackPck = pckFormatReader.FromStream(texturePackFileStream);
texturePackage = GetTexturePackageFromPckFile(name, description, identifier, infoPck, texturePackPck, resolution);
IDictionary<string, byte[]> audios = dataDirectoryInfo.EnumerateFiles("*.binka")
.ToDictionary(audioFile => audioFile.Name, audioFile => File.ReadAllBytes(audioFile.FullName));
return texturePackage is not null;
}
}
private IDLCPackage GetTexturePackageFromPckFile(string name, string description, int identifier, PckFile infoPck, PckFile dataPck, DLCTexturePackage.TextureResolution resolution)
{
@@ -260,35 +281,38 @@ namespace PckStudio.Core.DLC
return null;
if (!infoPck.TryGetAsset("comparison.png", PckAssetType.TextureFile, out PckAsset comparisonAsset))
Trace.TraceError($"Could not find 'comparison.png'.");
Trace.TraceWarning($"Could not find 'comparison.png'.");
if (!infoPck.TryGetAsset("icon.png", PckAssetType.TextureFile, out PckAsset iconnAsset))
Trace.TraceError($"Could not find 'icon.png'.");
Trace.TraceWarning($"Could not find 'icon.png'.");
Image comparisonImg = comparisonAsset?.GetTexture();
Image iconImg = iconnAsset?.GetTexture();
DLCTexturePackage.MetaData metaData = new DLCTexturePackage.MetaData(comparisonImg, iconImg);
DLCTexturePackage.MetaData metaData = new DLCTexturePackage.MetaData(comparisonAsset?.GetTexture(), iconnAsset?.GetTexture());
bool hasTerrainAtlas = TryGetAtlasFromResourceCategory(dataPck, AtlasResource.AtlasType.BlockAtlas, out Atlas terrainAtlas);
bool hasItemAtlas = TryGetAtlasFromResourceCategory(dataPck, AtlasResource.AtlasType.ItemAtlas, out Atlas itemAtlas);
bool hasParticleAtlas = TryGetAtlasFromResourceCategory(dataPck, AtlasResource.AtlasType.ParticleAtlas, out Atlas particleAtlas);
bool hasPaintingAtlas = TryGetAtlasFromResourceCategory(dataPck, AtlasResource.AtlasType.PaintingAtlas, out Atlas paintingAtlas);
bool hasMoonPhaseAtlas = TryGetAtlasFromResourceCategory(dataPck, AtlasResource.AtlasType.MoonPhaseAtlas, out Atlas moonPhaseAtlas);
TryGetAtlasFromResourceCategory(dataPck, AtlasResource.AtlasType.BlockAtlas, out Atlas terrainAtlas);
TryGetAtlasFromResourceCategory(dataPck, AtlasResource.AtlasType.ItemAtlas, out Atlas itemAtlas);
TryGetAtlasFromResourceCategory(dataPck, AtlasResource.AtlasType.ParticleAtlas, out Atlas particleAtlas);
TryGetAtlasFromResourceCategory(dataPck, AtlasResource.AtlasType.PaintingAtlas, out Atlas paintingAtlas);
TryGetAtlasFromResourceCategory(dataPck, AtlasResource.AtlasType.MoonPhaseAtlas, out Atlas moonPhaseAtlas);
TryGetAtlasFromResourceCategory(dataPck, AtlasResource.AtlasType.MapIconAtlas, out Atlas mapIconsAtlas);
TryGetAtlasFromResourceCategory(dataPck, AtlasResource.AtlasType.AdditionalMapIconsAtlas, out Atlas additionalMapIconsAtlas);
string itemAnimationAssetPath = ResourceLocation.GetPathFromCategory(ResourceCategory.ItemAnimation);
string blockAnimationAssetPath = ResourceLocation.GetPathFromCategory(ResourceCategory.BlockAnimation);
IPckAssetDeserializer<Animation> deserializer = AnimationDeserializer.DefaultDeserializer;
IDictionary<string, Animation> animations = dataPck.GetDirectoryContent(itemAnimationAssetPath, PckAssetType.TextureFile)
IDictionary<string, Animation> itemAnimations = dataPck.GetDirectoryContent(itemAnimationAssetPath, PckAssetType.TextureFile)
.Where(asset => !asset.IsMipmappedFile())
.ToDictionary(asset => Path.GetFileNameWithoutExtension(asset.Filename), deserializer.Deserialize);
bool hasCompassAnimation = animations.ContainsKey("compass");
bool hasClockAnimation = animations.ContainsKey("clock");
IDictionary<string, Animation> blockAnimations = dataPck.GetDirectoryContent(blockAnimationAssetPath, PckAssetType.TextureFile)
.Where(asset => !asset.IsMipmappedFile())
.ToDictionary(asset => Path.GetFileNameWithoutExtension(asset.Filename), deserializer.Deserialize);
if (!hasCompassAnimation)
if (!itemAnimations.ContainsKey("compass"))
Trace.TraceError("No compass animation found!");
if (!hasClockAnimation)
if (!itemAnimations.ContainsKey("clock"))
Trace.TraceError("No clock animation found!");
ITryGet<string, Image> tryGetTexture = TryGet<string, Image>.FromDelegate((string path, out Image image) =>
@@ -297,9 +321,10 @@ namespace PckStudio.Core.DLC
image = asset?.GetTexture();
return success;
});
ImageDeserializer defaultDeserializer = ImageDeserializer.DefaultDeserializer;
IEnumerable<Image> blockEntityBreakingFrames = dataPck.GetDirectoryContent("res/textures/", PckAssetType.TextureFile)
.Select(ImageDeserializer.DefaultDeserializer.Deserialize);
.Select(defaultDeserializer.Deserialize);
Animation blockEntityBreakAnimation = new Animation(blockEntityBreakingFrames);
ColorContainer colorContainer = dataPck.GetAssetsByType(PckAssetType.ColourTableFile).FirstOrDefault()?.GetData(new COLFileReader()) ?? new ColorContainer();
@@ -314,17 +339,31 @@ namespace PckStudio.Core.DLC
.ToDictionary(c => c.Name, c => (c.SurfaceColor, c.UnderwaterColor, c.FogColor));
IDictionary<string, Image> environmentTextures = dataPck.GetDirectoryContent("res/environment/", PckAssetType.TextureFile)
.ToDictionary(a => Path.GetFileNameWithoutExtension(a.Filename), ImageDeserializer.DefaultDeserializer.Deserialize);
.ToDictionary(a => Path.GetFileNameWithoutExtension(a.Filename), defaultDeserializer.Deserialize);
environmentTextures.TryGetValue("clouds", out Image clouds);
environmentTextures.TryGetValue("rain", out Image rain);
environmentTextures.TryGetValue("snow", out Image snow);
DLCTexturePackage.EnvironmentData environmentData = new DLCTexturePackage.EnvironmentData(clouds, rain, snow);
IList<KeyValuePair<string, string>> materials = dataPck.GetAssetsByType(PckAssetType.MaterialFile).FirstOrDefault()?.GetData(new MaterialFileReader())
.Select(mat => new KeyValuePair<string, string>(mat.Name, mat.Type)).ToList();
IDictionary<string, Image> terrainTextures = dataPck.GetDirectoryContent("res/terrain/", PckAssetType.TextureFile)
.ToDictionary(a => Path.GetFileNameWithoutExtension(a.Filename), defaultDeserializer.Deserialize);
terrainTextures.TryGetValue("sun", out Image sun);
terrainTextures.TryGetValue("moon", out Image moon);
AbstractModelContainer customModels = null;
if (dataPck.TryGetAsset("models.bin", PckAssetType.ModelsFile, out PckAsset modelsAsset))
{
ModelContainer models = modelsAsset.GetData(new ModelFileReader());
customModels = AbstractModelContainer.FromModelContainer(models, null);
}
IDictionary<string, string> materials = dataPck.GetAssetsByType(PckAssetType.MaterialFile).FirstOrDefault()?.GetData(new MaterialFileReader())
.ToDictionary(mat => mat.Name, mat => mat.Type);
return new DLCTexturePackage(name, description, identifier, metaData, resolution,
terrainAtlas, itemAtlas, particleAtlas, paintingAtlas, moonPhaseAtlas,
terrainAtlas, itemAtlas, particleAtlas, paintingAtlas, moonPhaseAtlas, mapIconsAtlas, additionalMapIconsAtlas,
ArmorSetDescription.Leather.GetArmorSet(tryGetTexture),
ArmorSetDescription.Chain.GetArmorSet(tryGetTexture),
ArmorSetDescription.Iron.GetArmorSet(tryGetTexture),
@@ -333,19 +372,20 @@ namespace PckStudio.Core.DLC
ArmorSetDescription.Turtle.GetArmorSet(tryGetTexture),
environmentData,
colors,
waterColors: null,
customModels: null,
waterColors,
customModels,
materials,
blockEntityBreakAnimation,
itemAnimations: null,
blockAnimations: null,
itemAnimations,
blockAnimations,
sun, moon,
parentPackage: null);
}
private bool TryGetAtlasFromResourceCategory(PckFile pck, AtlasResource.AtlasType atlasType, out Atlas atlas)
{
ResourceLocation resourceLocation = ResourceLocation.GetFromCategory(AtlasResource.GetId(atlasType));
if (!pck.TryGetAsset(resourceLocation.ToString(), PckAssetType.TextureFile, out PckAsset asset))
if (!pck.TryGetAsset(resourceLocation.FullPath, PckAssetType.TextureFile, out PckAsset asset))
{
Trace.TraceWarning($"Could not find '{resourceLocation.FullPath}'.");
atlas = null;
@@ -468,6 +508,25 @@ namespace PckStudio.Core.DLC
_byteOrder = GetByteOrderForPlatform(platform);
}
public bool CloseDLCPackage(int identifier) => _packageRegistry.UnregisterPackage(identifier);
public DLCPackageContent CompilePackage(IDLCPackage package)
{
LOCFile localisation = GetLocalisation(package.Identifier);
switch (package.GetDLCPackageType())
{
case DLCPackageType.Invalid:
break;
case DLCPackageType.RawAssets: return _pckFileCompiler.CompileRawAssets(package);
case DLCPackageType.SkinPack: return _pckFileCompiler.CompileSkinPackage(package, localisation);
case DLCPackageType.TexturePack: return _pckFileCompiler.CompileTexturePackage(package, localisation);
case DLCPackageType.MashUpPack: return _pckFileCompiler.CompileMashUpPackage(package, localisation);
case DLCPackageType.MG01:
break;
case DLCPackageType.MG02:
break;
case DLCPackageType.MG03:
break;
}
return DLCPackageContent.Empty;
}
}
}

View File

@@ -15,7 +15,7 @@ namespace PckStudio.Core.DLC
public sealed class DLCMashUpPackage : DLCPackage
{
public override string Description { get; }
private AbstractGameRule _gameRule { get; }
private AbstractGameRule _gameRule;
public bool HasAudioData => _pckAudio is not null && _audioData.Count > 0;
private IDLCPackage _skinPackage;
@@ -24,30 +24,45 @@ namespace PckStudio.Core.DLC
private IDictionary<string, byte[]> _audioData;
private PckAudioFile _pckAudio;
internal DLCMashUpPackage(string name, string description, int identifier, AbstractGameRule gameRule, IDLCPackage parentPackage, IDLCPackage skinPackage = null, IDLCPackage texturePackage = null)
internal DLCMashUpPackage(string name, string description, int identifier, AbstractGameRule gameRule, MapData mapData, PckAudioFile pckAudio, IDictionary<string, byte[]> audioData, IDLCPackage parentPackage, IDLCPackage skinPackage = null, IDLCPackage texturePackage = null)
: base(name, identifier, parentPackage)
{
Description = description;
_gameRule = gameRule;
_skinPackage = skinPackage;
_texturePackage = texturePackage;
_mapData = mapData;
_audioData = new Dictionary<string, byte[]>();
_pckAudio = pckAudio;
_audioData = audioData;
}
internal DLCMashUpPackage(string name, string description, int identifier)
: this(name, description, identifier, new RootGameRule(), null)
: this(name, description, identifier, new RootGameRule(), null, new PckAudioFile(), null, null)
{
_skinPackage = DLCSkinPackage.CreateEmpty(this);
_texturePackage = DLCTexturePackage.CreateDefaultPackage(this);
_audioData = new Dictionary<string, byte[]>();
_pckAudio = new PckAudioFile();
}
public override DLCPackageType GetDLCPackageType() => DLCPackageType.MashUpPack;
public IDLCPackage GetSkinPackage() => _skinPackage;
public IDLCPackage GetTexturePackage() => _texturePackage;
public AbstractGameRule GetGameRule() => _gameRule;
public PckAudioFile GetAudioPack() => _pckAudio;
public override DLCPackageType GetDLCPackageType() => DLCPackageType.MashUpPack;
public bool AddAudio(string name, byte[] audioData, PckAudioFile.Category category)
{
if (_audioData.ContainsKey(name) || !_pckAudio.HasCategory(category))
return false;
if (_pckAudio.TryGetCategory(category, out PckAudioFile.AudioCategory audioCategory))
{
audioCategory.SongNames.Add(name);
return true;
}
return false;
}
public AbstractGameRule GetGameRule() => _gameRule;
internal PckAudioFile GetAudioPack() => _pckAudio;
internal NamedData<byte[]>[] GetAudioFiles() => _audioData.Select(kv => new NamedData<byte[]>(kv.Key, kv.Value)).ToArray();
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using OMI.Formats.Pck;
namespace PckStudio.Core.DLC
{
public class DLCPackageContent
{
public static DLCPackageContent Empty => new DLCPackageContent(default);
internal bool IsEmpty { get; }
internal PckFile MainPck { get; }
internal DLCDataFolderContent DataFolder { get; }
public record DLCDataFolderContent
{
public NamedData<PckFile> TexturePck { get; }
public List<NamedData<byte[]>> Files { get; }
public DLCDataFolderContent(NamedData<PckFile> texturePck, NamedData<byte[]>[] files)
{
TexturePck = texturePck;
Files = new List<NamedData<byte[]>>(files);
}
public void AddFile(NamedData<byte[]> namedData) => Files.Add(namedData);
public void AddFiles(NamedData<byte[]>[] namedData) => Files.AddRange(namedData);
public void AddFile(string name, byte[] data) => AddFile(new NamedData<byte[]>(name, data));
}
public DLCPackageContent(PckFile mainPck, NamedData<PckFile> texturePck, NamedData<byte[]>[] dataFiles)
: this(mainPck, new(texturePck, dataFiles ?? Array.Empty<NamedData<byte[]>>()))
{
}
public DLCPackageContent(PckFile mainPck, DLCDataFolderContent dataFolderContent)
{
MainPck = mainPck;
DataFolder = dataFolderContent;
IsEmpty = mainPck is null;
}
public DLCPackageContent(PckFile mainPck) : this(mainPck, default) { }
}
}

View File

@@ -0,0 +1,8 @@
namespace PckStudio.Core.DLC
{
public enum DLCPackageContentSerilasationType : int
{
Local, //! create local folder with the value of: ('IDS_DISPLAY_NAME') and write all content into it.
Share //! zip file if texture or mashup pack, else just the pck file.
}
}

View File

@@ -11,13 +11,12 @@ namespace PckStudio.Core.DLC
public enum DLCSkinPackageOrder
{
ById,
CapesFirst,
SkinsFirst
ByName,
}
public sealed class DLCSkinPackage : DLCPackage
{
public DLCSkinPackageOrder SkinPackageOrder { get; set; } = DLCSkinPackageOrder.CapesFirst;
public DLCSkinPackageOrder SkinPackageOrder { get; set; } = DLCSkinPackageOrder.ById;
private readonly IDictionary<int, Image> _capes;
private readonly IDictionary<SkinIdentifier, Skin.Skin> _skins;
@@ -38,6 +37,7 @@ namespace PckStudio.Core.DLC
internal static IDLCPackage CreateEmpty(IDLCPackage parentPackage) => CreateEmpty(parentPackage.Name, parentPackage.Identifier, parentPackage);
public bool TryGetSkin(SkinIdentifier skinIdentifier, out Skin.Skin skin) => _skins.TryGetValue(skinIdentifier, out skin);
public bool TryGetCape(int capeId, out Image cape) => _capes.TryGetValue(capeId, out cape);
public bool ContainsSkin(SkinIdentifier skinIdentifier) => _skins.ContainsKey(skinIdentifier);

View File

@@ -6,13 +6,9 @@ using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using OMI.Formats.Color;
using OMI.Formats.Material;
using OMI.Formats.Model;
using OMI.Formats.Pck;
using OMI.Workers.Color;
using PckStudio.Core.Extensions;
using PckStudio.Core.Interfaces;
using PckStudio.Core.Model;
using PckStudio.Core.Properties;
namespace PckStudio.Core.DLC
@@ -43,11 +39,11 @@ namespace PckStudio.Core.DLC
public Image IconImg { get; } = iconImg;
}
public sealed class EnvironmentData
public sealed class EnvironmentData(Image clouds, Image rain, Image snow)
{
public Image Clouds;
public Image Rain;
public Image Snow;
public Image Clouds = clouds;
public Image Rain = rain;
public Image Snow = snow;
}
public MetaData Info { get; }
@@ -56,20 +52,32 @@ namespace PckStudio.Core.DLC
//! => colours.col
private IDictionary<string, Color> _colors;
private IDictionary<string, (Color surface, Color underwater, Color fog)> _waterColors;
private ModelContainer _customModels; //! can be null.. => models.bin
private MaterialContainer _materials; //! can be null..
private AbstractModelContainer _customModels; //! can be null.. => models.bin
private IDictionary<string, string> _materials; //! can be null..
//! terrain mipmaps will be generated automatically. Add mipmap option to settings menu ? -null
private Atlas _terrainAtlas;
private Atlas _itemsAtlas;
private Atlas _particlesAtlas;
private Atlas _paintingAtlas;
private ArmorSet[] _armorSets = new ArmorSet[6];
private Atlas _moonPhaseAtlas;
private ArmorSet _leatherArmorSet;
private ArmorSet _chainArmorSet;
private ArmorSet _ironArmorSet;
private ArmorSet _goldArmorSet;
private ArmorSet _diamondArmorSet;
private ArmorSet _turtleArmorSet;
private Atlas _mapIconsAtlas;
private Atlas _additionalMapIconsAtlas;
private EnvironmentData _environmentData;
private Animation _blockEntityBreakAnimation;
private IDictionary<string, Animation> _itemAnimations;
private IDictionary<string, Animation> _blockAnimations;
private Image _sun;
private Image _moon;
//! TODO: add resources from "res/misc/"
internal DLCTexturePackage(
string name,
@@ -81,14 +89,25 @@ namespace PckStudio.Core.DLC
Atlas itemsAtlas,
Atlas particlesAtlas,
Atlas paintingAtlas,
ArmorSet[] armorSets,
Atlas moonPhaseAtlas,
Atlas mapIconsAtlas,
Atlas additionalMapIconsAtlas,
ArmorSet leatherArmorSet,
ArmorSet chainArmorSet,
ArmorSet ironArmorSet,
ArmorSet goldArmorSet,
ArmorSet diamondArmorSet,
ArmorSet turtleArmorSet,
EnvironmentData environmentData,
IDictionary<string, Color> colors,
IDictionary<string, (Color surface, Color underwater, Color fog)> waterColors,
ModelContainer customModels,
MaterialContainer materials,
AbstractModelContainer customModels,
IDictionary<string, string> materials,
Animation blockEntityBreakAnimation,
IDictionary<string, Animation> itemAnimations,
IDictionary<string, Animation> blockAnimations,
Image sun,
Image moon,
IDLCPackage parentPackage
)
: base(name, identifier, parentPackage)
@@ -100,14 +119,25 @@ namespace PckStudio.Core.DLC
_itemsAtlas = itemsAtlas;
_particlesAtlas = particlesAtlas;
_paintingAtlas = paintingAtlas;
_armorSets = armorSets;
_colors = colors;
_waterColors = waterColors;
_moonPhaseAtlas = moonPhaseAtlas;
_mapIconsAtlas = mapIconsAtlas;
_additionalMapIconsAtlas = additionalMapIconsAtlas;
_leatherArmorSet = leatherArmorSet;
_chainArmorSet = chainArmorSet;
_ironArmorSet = ironArmorSet;
_goldArmorSet = goldArmorSet;
_diamondArmorSet = diamondArmorSet;
_turtleArmorSet = turtleArmorSet;
_environmentData = environmentData;
_colors = colors ?? new Dictionary<string, Color>();
_waterColors = waterColors ?? new Dictionary<string, (Color, Color, Color)>();
_customModels = customModels;
_materials = materials;
_blockEntityBreakAnimation = blockEntityBreakAnimation;
_itemAnimations = itemAnimations;
_blockAnimations = blockAnimations;
_itemAnimations = itemAnimations ?? new Dictionary<string, Animation>();
_blockAnimations = blockAnimations ?? new Dictionary<string, Animation>();
_sun = sun;
_moon = moon;
}
public TextureResolution GetResolution() => _resolution;
@@ -166,28 +196,36 @@ namespace PckStudio.Core.DLC
Atlas items = Atlas.FromResourceLocation(Resources.items_atlas, ResourceLocation.GetFromCategory(AtlasResource.GetId(AtlasResource.AtlasType.ItemAtlas)));
Atlas particles = Atlas.FromResourceLocation(Resources.particles_atlas, ResourceLocation.GetFromCategory(AtlasResource.GetId(AtlasResource.AtlasType.ParticleAtlas)));
Atlas painting = Atlas.FromResourceLocation(Resources.paintings_atlas, ResourceLocation.GetFromCategory(AtlasResource.GetId(AtlasResource.AtlasType.PaintingAtlas)));
Atlas moonPhases = Atlas.FromResourceLocation(Resources.moon_phases_atlas, ResourceLocation.GetFromCategory(AtlasResource.GetId(AtlasResource.AtlasType.MoonPhaseAtlas)));
Atlas mapIconsAtlas = Atlas.FromResourceLocation(Resources.map_icons_atlas, ResourceLocation.GetFromCategory(AtlasResource.GetId(AtlasResource.AtlasType.MapIconAtlas)));
Atlas additionalMapIconsAtlas = Atlas.FromResourceLocation(Resources.additional_map_icons_atlas, ResourceLocation.GetFromCategory(AtlasResource.GetId(AtlasResource.AtlasType.AdditionalMapIconsAtlas)));
//ColorContainer colors = new COLFileReader().FromStream(new MemoryStream());
IDictionary<string, Color> colors = null;
IDictionary<string, (Color, Color, Color)> waterColors = null;
Animation blockEntityBreakAnimation = new Animation(terrain.GetRange(0, 15, 10, ImageLayoutDirection.Horizontal).Select(t => t.Texture).ToArray(), true, 3);
ArmorSet[] armorSets = GetArmorSets();
IDictionary<string, Animation> itemAnimations = GetItemAnimations();
IDictionary<string, Animation> blockAnimations = GetBlockAnimations();
return new DLCTexturePackage(
name, description, identifier, metadata, resolution,
terrain, items, particles, painting,
armorSets,
terrain, items, particles, painting, moonPhases, mapIconsAtlas, additionalMapIconsAtlas,
new ArmorSet(ArmorSetDescription.CLOTH, Resources.cloth, Resources.cloth_b),
new ArmorSet(ArmorSetDescription.CHAIN, Resources.chain, default),
new ArmorSet(ArmorSetDescription.IRON, Resources.iron, default),
new ArmorSet(ArmorSetDescription.GOLD, Resources.gold, default),
new ArmorSet(ArmorSetDescription.DIAMOND, Resources.diamond, default),
new ArmorSet(ArmorSetDescription.TURTLE, Resources.turtle, default),
new EnvironmentData(Resources.clouds, Resources.rain, Resources.snow),
colors, waterColors,
new ModelContainer(),
new MaterialContainer(),
new AbstractModelContainer(),
new Dictionary<string, string>(),
blockEntityBreakAnimation,
itemAnimations,
blockAnimations,
sun: null,
moon: null,
parentPackage
);
}
@@ -200,19 +238,6 @@ namespace PckStudio.Core.DLC
internal Atlas GetPaintingAtlas() => _paintingAtlas;
private static ArmorSet[] GetArmorSets()
{
return new ArmorSet[6]
{
new ArmorSet(ArmorSetDescription.CLOTH, Resources.cloth, Resources.cloth_b),
new ArmorSet(ArmorSetDescription.CHAIN, Resources.chain, default),
new ArmorSet(ArmorSetDescription.IRON, Resources.iron, default),
new ArmorSet(ArmorSetDescription.GOLD, Resources.gold, default),
new ArmorSet(ArmorSetDescription.DIAMOND, Resources.diamond, default),
new ArmorSet(ArmorSetDescription.TURTLE, Resources.turtle, default)
};
}
private static IDictionary<string, Animation> GetItemAnimations()
{
return new Dictionary<string, Animation>()

View File

@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OMI;
using OMI.Formats.GameRule;
using OMI.Formats.Languages;
using OMI.Formats.Pck;
using OMI.Workers.GameRule;
using OMI.Workers.Language;
using OMI.Workers.Pck;
using PckStudio.Core.Extensions;
using PckStudio.Core.Interfaces;
using PckStudio.Core.IO.PckAudio;
namespace PckStudio.Core.DLC
{
internal sealed class PckFileCompiler
{
private ByteOrder _byteOrder;
private GameRuleFile.CompressionType _compressionType;
private GameRuleFile.CompressionLevel _compressionLevel;
internal PckFileCompiler(ByteOrder byteOrder, GameRuleFile.CompressionType compressionType, GameRuleFile.CompressionLevel compressionLevel)
{
_byteOrder = byteOrder;
_compressionType = compressionType;
_compressionLevel = compressionLevel;
}
private PckFile CreateRootPckFile(int packId, int packVerison, LOCFile localisation)
{
PckFile mainPck = new PckFile();
PckAsset meta = mainPck.CreateNewAsset("0", PckAssetType.InfoFile);
meta.AddProperty("PACKID", packId);
meta.AddProperty("PACKVERSION", packVerison);
mainPck.CreateNewAsset("localisation.loc", PckAssetType.LocalisationFile, new LOCFileWriter(localisation, 2));
return mainPck;
}
internal DLCPackageContent CompileSkinPackage(IDLCPackage package, LOCFile localisation)
{
if (package is not DLCSkinPackage skinPackage)
return DLCPackageContent.Empty;
PckFile skinsPck = skinPackage.IsRootPackage ? CreateRootPckFile(package.Identifier, 0, localisation) : new PckFile();
foreach (KeyValuePair<int, Image> kv in skinPackage.GetCapes())
{
PckAsset capeAsset = skinsPck.CreateNewAsset($"dlccape{kv.Key:08}.png", PckAssetType.CapeFile);
capeAsset.SetTexture(kv.Value);
}
foreach (Skin.Skin skin in skinPackage.GetSkins())
{
skinsPck.AddSkin("", skin, localisation);
}
return new DLCPackageContent(skinsPck);
}
internal DLCPackageContent CompileTexturePackage(IDLCPackage package, LOCFile localisation)
{
if (package is not DLCTexturePackage texturePackage)
return DLCPackageContent.Empty;
PckFile texturePackInfoPck = new PckFile();
{
texturePackInfoPck.AddTexture("comparison.png", texturePackage.Info.ComparisonImg);
texturePackInfoPck.AddTexture("icon.png", texturePackage.Info.IconImg);
}
PckFile texturePck = new PckFile();
{
texturePck.AddTexture(ResourceLocations.GetPathFromCategory(AtlasResource.GetId(AtlasResource.AtlasType.ParticleAtlas)), texturePackage.GetParticleAtlas());
texturePck.AddTexture(ResourceLocations.GetPathFromCategory(AtlasResource.GetId(AtlasResource.AtlasType.ItemAtlas)), texturePackage.GetItemsAtlas());
texturePck.AddTexture(ResourceLocations.GetPathFromCategory(AtlasResource.GetId(AtlasResource.AtlasType.BlockAtlas)), texturePackage.GetTerrainAtlas());
texturePck.AddTexture(ResourceLocations.GetPathFromCategory(AtlasResource.GetId(AtlasResource.AtlasType.PaintingAtlas)), texturePackage.GetPaintingAtlas());
}
if (package.IsRootPackage)
{
PckFile mainPck = CreateRootPckFile(package.Identifier, 0, localisation);
DLCTexturePackage.TextureResolution res = texturePackage.GetResolution();
PckAsset textureInfoAsset = mainPck.CreateNewAsset($"{res}/{res}Info.pck", PckAssetType.TexturePackInfoFile, new PckFileWriter(texturePackInfoPck, _byteOrder));
textureInfoAsset.AddProperty("PACKID", "0");
textureInfoAsset.AddProperty("DATAPATH", $"{res}Data.pck");
return new DLCPackageContent(mainPck, new NamedData<PckFile>($"{res}Data.pck", texturePck), default);
}
return new DLCPackageContent(texturePackInfoPck);
}
internal DLCPackageContent CompileMashUpPackage(IDLCPackage package, LOCFile localisation)
{
if (package is not DLCMashUpPackage mashUpPackage)
return DLCPackageContent.Empty;
PckFile skinsPck = CompileSkinPackage(mashUpPackage.GetSkinPackage(), localisation).MainPck;
DLCTexturePackage texturePackage = mashUpPackage.GetTexturePackage() as DLCTexturePackage;
DLCPackageContent texturePackContent = CompileTexturePackage(texturePackage, localisation);
DLCTexturePackage.TextureResolution res = texturePackage.GetResolution();
PckFile texturePackInfoPck = texturePackContent.MainPck;
PckFile texturePck = texturePackContent.DataFolder.TexturePck.Value;
PckFile mainPck = CreateRootPckFile(package.Identifier, 0, localisation);
_ = mainPck.CreateNewAssetIf(skinsPck is PckFile && skinsPck.AssetCount > 0, "Skins.pck", PckAssetType.SkinDataFile, new PckFileWriter(skinsPck, _byteOrder));
if (texturePackInfoPck is PckFile && texturePackInfoPck.AssetCount > 0)
{
PckAsset textureInfoAsset = mainPck.CreateNewAsset($"{res}/{res}Info.pck", PckAssetType.TexturePackInfoFile, new PckFileWriter(texturePackInfoPck, _byteOrder));
textureInfoAsset.AddProperty("PACKID", "0");
textureInfoAsset.AddProperty("DATAPATH", texturePackContent.DataFolder.TexturePck.Name);
}
{
GameRuleFile grf = mashUpPackage.GetGameRule();
grf.Header.CompressionType = _compressionType;
grf.Header.CompressionLevel = _compressionLevel;
mainPck.CreateNewAsset("GameRule.grf", PckAssetType.GameRulesFile, new GameRuleFileWriter(grf));
}
if (mashUpPackage.HasAudioData)
{
mainPck.CreateNewAsset("audio.pck", PckAssetType.AudioFile, new PckAudioFileWriter(mashUpPackage.GetAudioPack(), _byteOrder));
texturePackContent.DataFolder.AddFiles(mashUpPackage.GetAudioFiles());
}
return new DLCPackageContent(mainPck, texturePackContent.DataFolder);
}
internal DLCPackageContent CompileRawAssets(IDLCPackage package)
{
return package is RawAssetDLCPackage rawAssetDLCPackage ? new DLCPackageContent(rawAssetDLCPackage.PckFile) : DLCPackageContent.Empty;
}
}
}

View File

@@ -1,4 +1,7 @@
using System.Drawing;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using AnimatedGif;
namespace PckStudio.Core.Extensions
@@ -23,5 +26,26 @@ namespace PckStudio.Core.Extensions
ms.Position = 0;
return Image.FromStream(ms);
}
public static Animation Combine(this Animation first, Animation second, ImageLayoutDirection layoutDirection)
{
if (first == null)
return second;
if (second == null)
return first;
if (first.TextureCount != second.TextureCount)
return first;
if (first.FrameCount != second.FrameCount)
return first;
Image[] secondTextures = second.GetTextures().ToArray();
Animation animation = new Animation(first.GetTextures().enumerate().Select(ift => ift.value.Combine(secondTextures[ift.index], layoutDirection)));
foreach ((int texId, int frameTime) item in first.GetFrames().Select(f => (texId: first.GetTextureIndex(f.Texture), frameTime: f.Ticks)))
{
animation.AddFrame(item.texId, item.frameTime);
}
return animation;
}
}
}

View File

@@ -54,15 +54,15 @@ namespace PckStudio.Core.Extensions
return MathExtensions.Clamp(resultValue, 0.0f, 1.0f);
}
public static byte Mix(double ratio, byte val1, byte val2)
public static byte Mix(float ratio, byte val1, byte val2)
{
ratio = MathExtensions.Clamp(ratio, 0.0, 1.0);
ratio = MathExtensions.Clamp(ratio, 0.0f, 1.0f);
return (byte)(ratio * val1 + (1.0 - ratio) * val2);
}
public static Color Mix(this Color c1, Color c2, double ratio)
public static Color Mix(this Color c1, Color c2, float ratio)
{
ratio = MathExtensions.Clamp(ratio, 0.0, 1.0);
ratio = MathExtensions.Clamp(ratio, 0.0f, 1.0f);
return Color.FromArgb(c1.A,
Mix(ratio, c1.R, c2.R),
Mix(ratio, c1.G, c2.G),

View File

@@ -273,9 +273,9 @@ namespace PckStudio.Core.Extensions
return bitmapResult;
}
public static Image Interpolate(this Image source, Image target, double delta)
public static Image Interpolate(this Image source, Image target, float delta)
{
delta = MathExtensions.Clamp(delta, 0.0, 1.0);
delta = MathExtensions.Clamp(delta, 0.0f, 1.0f);
if (source is not Bitmap baseImage || target is not Bitmap overlayImage ||
source.Width != target.Width || source.Height != target.Height)
return source;

View File

@@ -39,8 +39,8 @@ namespace PckStudio.Core
Thumbnail = thumbnail;
World = world;
var levelData = MapReader.OpenSave(new MemoryStream(world.Value))["level.dat"];
TagCompound? levelDat = NbtDocument.LoadDocument(new MemoryStream(levelData))!.DocumentRoot?["Data"] as TagCompound;
SaveData saveData = MapReader.OpenSaveData(new MemoryStream(world.Value));
TagCompound? levelDat = saveData.LevelData.DocumentRoot?["Data"] as TagCompound;
_ = levelDat ?? throw new NullReferenceException(nameof(levelDat));
Vector3 spawn = levelDat.GetVector3("Spawn");

View File

@@ -1,13 +1,42 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Cyotek.Data.Nbt;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using OMI;
using PckStudio.Core.Extensions;
namespace PckStudio.Core
{
public class SaveData
{
public NbtDocument LevelData { get; }
public IDictionary<Guid, NbtDocument> Players { get; }
private IDictionary<string, byte[]> _worldArchive;
public SaveData(IDictionary<string, byte[]> worldArchive)
{
_worldArchive = worldArchive;
if (_worldArchive.TryGetValue("levels.dat", out byte[] levelData))
{
Stream stream = new MemoryStream(levelData);
LevelData = NbtDocument.LoadDocument(stream);
}
Players = _worldArchive
.Where(kv => kv.Key.StartsWith("players/"))
.ToDictionary(
kv => Guid.Parse(Path.GetFileNameWithoutExtension(kv.Key)),
kv => NbtDocument.LoadDocument(new MemoryStream(kv.Value)));
}
}
public class MapReader
{
public static IDictionary<string, byte[]> OpenSave(Stream stream)
public static SaveData OpenSaveData(Stream stream)
{
EndiannessAwareBinaryReader reader = new EndiannessAwareBinaryReader(stream, ByteOrder.BigEndian);
_ = reader.ReadInt32();
@@ -35,7 +64,7 @@ namespace PckStudio.Core
res.Add(path, data);
}
return res;
return new SaveData(res);
}
}
}

View File

@@ -77,9 +77,12 @@
<Compile Include="DLC\DLCMashUpPackage.cs" />
<Compile Include="DLC\DLCMiniGamePackage.cs" />
<Compile Include="DLC\DLCPackage.cs" />
<Compile Include="DLC\DLCPackageContent.cs" />
<Compile Include="DLC\DLCPackageContentSerilasationType.cs" />
<Compile Include="DLC\DLCPackageRegistry.cs" />
<Compile Include="DLC\DLCSkinPackage.cs" />
<Compile Include="DLC\DLCTexturePackage.cs" />
<Compile Include="DLC\PckFileCompiler.cs" />
<Compile Include="DLC\RawAssetDLCPackage.cs" />
<Compile Include="Extensions\AnimationExtensions.cs" />
<Compile Include="Extensions\BlendMode.cs" />

View File

@@ -5,19 +5,19 @@ namespace PckStudio.Core.Skin
{
public sealed class SkinIdentifier : IFormattable
{
public int Id { get; }
private readonly int _id;
public SkinIdentifier(int id)
{
Id = id;
_id = id;
}
public static implicit operator int(SkinIdentifier _this) => _this.Id;
public static implicit operator int(SkinIdentifier @this) => @this._id;
public string ToString(string format, IFormatProvider formatProvider) => Id.ToString(format, formatProvider);
public string ToString(string format, IFormatProvider formatProvider) => _id.ToString(format, formatProvider);
public string ToString(string format) => Id.ToString(format, NumberFormatInfo.CurrentInfo);
public string ToString(string format) => _id.ToString(format, NumberFormatInfo.CurrentInfo);
public override string ToString() => Id.ToString(NumberFormatInfo.CurrentInfo);
public override string ToString() => _id.ToString(NumberFormatInfo.CurrentInfo);
}
}