From 16dfc040a4b9245022d18c595ff13985f82ba41a Mon Sep 17 00:00:00 2001 From: Lani Aung Date: Tue, 2 Nov 2021 18:33:07 -0600 Subject: [PATCH] Saving to DB, need to render --- Controllers/HomeController.cs | 18 +--- Controllers/PlayController.cs | 7 +- Entities/CelConfigDto.cs | 14 ++- Entities/CelDto.cs | 4 +- Entities/ConfigurationDto.cs | 13 +-- Entities/KisekaeDto.cs | 6 +- Entities/PaletteColorDto.cs | 13 +++ Entities/PaletteDto.cs | 8 +- Entities/Set.cs | 1 + Extensions/DapperExtensions.cs | 96 ++++++++++++++++++ Models/CelModel.cs | 54 +--------- Models/PaletteModel.cs | 18 +--- Models/PlaysetModel.cs | 17 +--- Services/ConfigurationReaderService.cs | 135 ++++++++++++++++--------- Services/DatabaseService.cs | 90 +++++++++++++---- Services/FileParserService.cs | 32 +++--- Startup.cs | 3 - fxl.codes.kisekae.csproj | 1 - 18 files changed, 318 insertions(+), 212 deletions(-) create mode 100644 Entities/PaletteColorDto.cs create mode 100644 Extensions/DapperExtensions.cs diff --git a/Controllers/HomeController.cs b/Controllers/HomeController.cs index 84ad108..da867a7 100644 --- a/Controllers/HomeController.cs +++ b/Controllers/HomeController.cs @@ -1,7 +1,5 @@ using System; using System.Diagnostics; -using System.IO; -using System.IO.IsolatedStorage; using System.Linq; using System.Threading.Tasks; using fxl.codes.kisekae.Models; @@ -16,16 +14,11 @@ namespace fxl.codes.kisekae.Controllers { private readonly DatabaseService _databaseService; private readonly ILogger _logger; - private readonly ConfigurationReaderService _readerService; - private readonly IsolatedStorageFile _storage; - public HomeController(ILogger logger, ConfigurationReaderService readerService, DatabaseService databaseService) + public HomeController(ILogger logger, DatabaseService databaseService) { _logger = logger; - _readerService = readerService; _databaseService = databaseService; - - _storage = IsolatedStorageFile.GetUserStoreForApplication(); } [HttpGet] @@ -47,15 +40,6 @@ namespace fxl.codes.kisekae.Controllers return Redirect("/"); } - [HttpPost] - public IActionResult Select(string directory, string file) - { - var stream = _storage.OpenFile(Path.Combine(directory, file), FileMode.Open); - var model = _readerService.ParseStream(stream, directory); - model.Name = file; - return View("Play", model); - } - public IActionResult Privacy() { return View(); diff --git a/Controllers/PlayController.cs b/Controllers/PlayController.cs index 25b9b20..dd6d612 100644 --- a/Controllers/PlayController.cs +++ b/Controllers/PlayController.cs @@ -1,6 +1,3 @@ -using System.Linq; -using System.Threading.Tasks; -using fxl.codes.kisekae.Models; using fxl.codes.kisekae.Services; using Microsoft.AspNetCore.Mvc; @@ -14,8 +11,8 @@ namespace fxl.codes.kisekae.Controllers { _databaseService = databaseService; } - - public async Task Index(int id, int configId) + + public IActionResult Index(int id, int configId) { return View(); } diff --git a/Entities/CelConfigDto.cs b/Entities/CelConfigDto.cs index 4e29779..6564b5e 100644 --- a/Entities/CelConfigDto.cs +++ b/Entities/CelConfigDto.cs @@ -1,10 +1,20 @@ -using Dapper.Contrib.Extensions; +using System.ComponentModel.DataAnnotations.Schema; namespace fxl.codes.kisekae.Entities { [Table("cel_config")] public class CelConfigDto { - + public int Id { get; set; } + [Column("cel_id")] public int CelId { get; set; } + [Column("config_id")] public int ConfigId { get; set; } + public int Group { get; set; } + public int Fix { get; set; } + public Set Sets { get; set; } = Set.Unset; + public int Transparency { get; set; } + public string Comment { get; set; } + [Column("palette_id")] public int PaletteId { get; set; } + public int X { get; set; } + public int Y { get; set; } } } \ No newline at end of file diff --git a/Entities/CelDto.cs b/Entities/CelDto.cs index fa9b951..ae8c49d 100644 --- a/Entities/CelDto.cs +++ b/Entities/CelDto.cs @@ -1,4 +1,4 @@ -using Dapper.Contrib.Extensions; +using System.ComponentModel.DataAnnotations.Schema; namespace fxl.codes.kisekae.Entities { @@ -6,7 +6,7 @@ namespace fxl.codes.kisekae.Entities public class CelDto : IKisekaeFile, IKisekaeParseable { public int Id { get; set; } - public int KisekaeId { get; set; } + [Column("kisekae_id")] public int KisekaeId { get; set; } public string Filename { get; set; } public byte[] Data { get; set; } } diff --git a/Entities/ConfigurationDto.cs b/Entities/ConfigurationDto.cs index 8e74a87..3467790 100644 --- a/Entities/ConfigurationDto.cs +++ b/Entities/ConfigurationDto.cs @@ -1,18 +1,19 @@ using System.Collections.Generic; -using Dapper.Contrib.Extensions; +using System.ComponentModel.DataAnnotations.Schema; namespace fxl.codes.kisekae.Entities { [Table("configuration")] public class ConfigurationDto : IKisekaeFile { - public int Id { get; set; } - public int KisekaeId { get; set; } - public string Filename { get; set; } public string Data { get; set; } public int? Height { get; set; } public int? Width { get; set; } - public int? BorderIndex { get; set; } - public IEnumerable Cels { get; set; } + [Column("border_index")] public int? BorderIndex { get; set; } + public List Cels { get; set; } = new(); + public List Palettes { get; set; } = new(); + public int Id { get; set; } + [Column("kisekae_id")] public int KisekaeId { get; set; } + public string Filename { get; set; } } } \ No newline at end of file diff --git a/Entities/KisekaeDto.cs b/Entities/KisekaeDto.cs index a439f10..0357800 100644 --- a/Entities/KisekaeDto.cs +++ b/Entities/KisekaeDto.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using Dapper.Contrib.Extensions; +using System.ComponentModel.DataAnnotations.Schema; namespace fxl.codes.kisekae.Entities { @@ -7,8 +7,8 @@ namespace fxl.codes.kisekae.Entities public class KisekaeDto { public int Id { get; set; } - public string Name { get; set; } - public string Filename { get; set; } + public string Name { get; init; } + public string Filename { get; init; } public string Checksum { get; set; } public IEnumerable Configurations { get; set; } } diff --git a/Entities/PaletteColorDto.cs b/Entities/PaletteColorDto.cs new file mode 100644 index 0000000..03dcd2e --- /dev/null +++ b/Entities/PaletteColorDto.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace fxl.codes.kisekae.Entities +{ + [Table("palette_color")] + public class PaletteColorDto + { + public int Id { get; set; } + [Column("palette_id")] public int PaletteId { get; set; } + public int Group { get; set; } + public string Hex { get; set; } + } +} \ No newline at end of file diff --git a/Entities/PaletteDto.cs b/Entities/PaletteDto.cs index 0aad485..c2b6dcd 100644 --- a/Entities/PaletteDto.cs +++ b/Entities/PaletteDto.cs @@ -1,14 +1,14 @@ -using Dapper.Contrib.Extensions; +using System.ComponentModel.DataAnnotations.Schema; namespace fxl.codes.kisekae.Entities { [Table("palette")] public class PaletteDto : IKisekaeFile, IKisekaeParseable { - public int Id { get; set; } - public int KisekaeId { get; set; } - public string Filename { get; set; } public string Comment { get; set; } + public int Id { get; set; } + [Column("kisekae_id")] public int KisekaeId { get; set; } + public string Filename { get; set; } public byte[] Data { get; set; } } } \ No newline at end of file diff --git a/Entities/Set.cs b/Entities/Set.cs index 1819661..33ff944 100644 --- a/Entities/Set.cs +++ b/Entities/Set.cs @@ -5,6 +5,7 @@ namespace fxl.codes.kisekae.Entities [Flags] public enum Set { + Unset = -1, Zero = 2 ^ 0, One = 2 ^ 1, Two = 2 ^ 2, diff --git a/Extensions/DapperExtensions.cs b/Extensions/DapperExtensions.cs new file mode 100644 index 0000000..5310bba --- /dev/null +++ b/Extensions/DapperExtensions.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Data; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Dapper; + +namespace fxl.codes.kisekae.Extensions +{ + public static class DapperExtensions + { + public static async Task InsertAsync(this IDbConnection connection, T item) + { + var info = GetTableInfo(typeof(T)); + var statement = $"insert into {info.TableName} ({string.Join(",", info.Columns)}) values ({string.Join(",", info.Parameters)}) returning id"; + return await connection.QuerySingleAsync(statement, item); + } + + public static async Task InsertAsync(this IDbConnection connection, IEnumerable items) + { + var info = GetTableInfo(typeof(T)); + var statement = $"insert into {info.TableName} ({string.Join(",", info.Columns)}) values ({string.Join(",", info.Parameters)}) returning id"; + + var count = 0; + foreach (var item in items) + { + await connection.ExecuteAsync(statement, item); + count++; + } + + return count; + } + + public static async Task UpdateAsync(this IDbConnection connection, T item) + { + var list = new List { item }; + return await UpdateAsync(connection, list); + } + + public static async Task UpdateAsync(this IDbConnection connection, IEnumerable items) + { + var info = GetTableInfo(typeof(T)); + var statement = new StringBuilder().AppendLine($"update {info.TableName} set"); + + var sets = new List(); + for (var index = 0; index < info.Columns.Length; index++) + if (!info.Columns[index].Contains("_id")) + sets.Add($"{info.Columns[index]} = {info.Parameters[index]}"); + + statement.AppendLine(string.Join(",", sets)); + statement.AppendLine("where id = @Id"); + + var count = 0; + foreach (var item in items) + { + await connection.ExecuteAsync(statement.ToString(), item); + count++; + } + + return count; + } + + private static TableInfo GetTableInfo(Type type) + { + var tableAttribute = type.GetCustomAttribute(); + var tableName = tableAttribute?.Name ?? type.Name.ToLowerInvariant(); + + var columns = new List(); + var parameters = new List(); + foreach (var property in type.GetProperties(BindingFlags.Default | BindingFlags.Public | BindingFlags.Instance)) + { + if (string.Equals(property.Name, "id", StringComparison.InvariantCultureIgnoreCase) || property.PropertyType.IsGenericType) continue; + + var columnAttribute = property.GetCustomAttribute(); + columns.Add($"\"{columnAttribute?.Name ?? property.Name.ToLowerInvariant()}\""); + parameters.Add($"@{property.Name}"); + } + + return new TableInfo + { + TableName = tableName, + Columns = columns.ToArray(), + Parameters = parameters.ToArray() + }; + } + + private class TableInfo + { + public string TableName { get; set; } + public string[] Columns { get; set; } + public string[] Parameters { get; set; } + } + } +} \ No newline at end of file diff --git a/Models/CelModel.cs b/Models/CelModel.cs index bfa57eb..f811bd4 100644 --- a/Models/CelModel.cs +++ b/Models/CelModel.cs @@ -1,70 +1,20 @@ -using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using System.Text.RegularExpressions; -using fxl.codes.kisekae.Services; -using Microsoft.Extensions.Logging; namespace fxl.codes.kisekae.Models { public class CelModel { - private const string Regex = @"#(?\d*)\.?(?\d*)\s*(?[\w\d\-]*\.[cCeElL]*)\s*\*?(?\d*)?\s*\:?(?[\d\s]*)?;?(?[\w\d\-\s\%]*)"; public Dictionary ImageByPalette = new(); - internal CelModel(ILogger logger, string line) - { - logger.LogTrace($"Parsing cel line {line}"); - - if (line.Contains("%t")) - { - var opacity = line[line.IndexOf("%t")..].Trim(); - opacity = opacity.Split(';', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)[0]; - opacity = opacity.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)[0]; - opacity = opacity.Replace("%t", ""); - Opacity = 1.0 - double.Parse(opacity) / 255; - } - - var matcher = new Regex(Regex); - var match = matcher.Match(line); - - foreach (var groupName in matcher.GetGroupNames()) - { - var property = typeof(CelModel).GetProperty(groupName); - if (property == null) continue; - match.Groups.TryGetValue(groupName, out var group); - if (string.IsNullOrEmpty(group?.Value)) - { - if (string.Equals(groupName, "Sets")) - for (var index = 0; index < Sets.Length; index++) - Sets[index] = true; - - logger.LogWarning($"Unable to find regex group {groupName} in {line}"); - continue; - } - - var value = group.Value.Trim(); - if (property.PropertyType == typeof(string)) property.SetValue(this, value); - if (property.PropertyType == typeof(int)) property.SetValue(this, int.Parse(value)); - - if (property.PropertyType != Sets.GetType()) continue; - - foreach (var id in value.Split(' ', StringSplitOptions.RemoveEmptyEntries)) - { - var success = int.TryParse(id, out var result); - if (success) Sets[result] = true; - else logger.LogWarning($"Unable to parse {id} as cel sets"); - } - } - } - public int Id { get; init; } public int Fix { get; init; } public string FileName { get; init; } public int PaletteId { get; init; } public bool[] Sets { get; } = new bool[10]; public string Comment { get; init; } - public double Opacity { get; } = 1.0; + [JsonIgnore] public int Transparency { get; set; } + public double Opacity { get; internal set; } = 1.0; public Coordinate[] InitialPositions { get; } = new Coordinate[10]; [JsonIgnore] public string DefaultImage => ImageByPalette.ContainsKey(PaletteId) ? ImageByPalette[PaletteId] : null; public Coordinate Offset { get; set; } diff --git a/Models/PaletteModel.cs b/Models/PaletteModel.cs index 47947e3..bfc7ef9 100644 --- a/Models/PaletteModel.cs +++ b/Models/PaletteModel.cs @@ -1,24 +1,12 @@ using System; -using fxl.codes.kisekae.Services; -using Microsoft.Extensions.Logging; using SixLabors.ImageSharp; namespace fxl.codes.kisekae.Models { public class PaletteModel { - internal PaletteModel(ILogger logger, string line) - { - logger.LogTrace($"Parsing palette line {line}"); - - var parts = line.Split(';'); - FileName = parts[0].Replace("%", "").Trim(); - - if (parts.Length > 1) Comment = parts[1].Trim(); - } - - public string FileName { get; } - public string Comment { get; } - public Color[] Colors { get; set; } = Array.Empty(); + public string FileName { get; internal set; } + public string Comment { get; internal set; } + public Color[] Colors { get; internal set; } = Array.Empty(); } } \ No newline at end of file diff --git a/Models/PlaysetModel.cs b/Models/PlaysetModel.cs index ca16eab..893d4f8 100644 --- a/Models/PlaysetModel.cs +++ b/Models/PlaysetModel.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using System.Text.Json.Serialization; using SixLabors.ImageSharp; @@ -7,24 +6,12 @@ namespace fxl.codes.kisekae.Models { public class PlaysetModel { + [JsonIgnore] public Color BorderColor = Color.Black; public string Name { get; set; } public int Height { get; set; } public int Width { get; set; } - [JsonIgnore] public int BorderColorIndex { get; set; } - [JsonIgnore] public Color BorderColor { get; set; } = Color.Black; [JsonIgnore] public List Palettes { get; } = new(); public List Cels { get; } = new(); - - public bool[] EnabledSets - { - get - { - var enabled = new bool[10]; - - for (var index = 0; index < enabled.Length; index++) enabled[index] = Cels.Any(x => x.Sets[index]); - - return enabled; - } - } + public bool[] EnabledSets { get; } = new bool[10]; } } \ No newline at end of file diff --git a/Services/ConfigurationReaderService.cs b/Services/ConfigurationReaderService.cs index 91ce750..1c24d34 100644 --- a/Services/ConfigurationReaderService.cs +++ b/Services/ConfigurationReaderService.cs @@ -1,8 +1,9 @@ using System; -using System.IO; +using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; +using fxl.codes.kisekae.Entities; using fxl.codes.kisekae.Models; using Microsoft.Extensions.Logging; @@ -10,91 +11,129 @@ namespace fxl.codes.kisekae.Services { public class ConfigurationReaderService { - public const string ResolutionRegexPattern = @"\((?[0-9]*).(?[0-9]*)\)"; - private readonly FileParserService _fileParser; + private const string CelRegex = @"#(?\d*)\.?(?\d*)\s*(?[\w\d\-]*\.[cCeElL]*)\s*\*?" + + @"(?\d*)?\s*\:?(?[\d\s]*)?;?(?[\w\d\-\s\%]*)"; + + private const string ResolutionRegexPattern = @"\((?[0-9]*).(?[0-9]*)\)"; + private readonly ILogger _logger; - public ConfigurationReaderService(ILogger logger, FileParserService fileParser) + public ConfigurationReaderService(ILogger logger) { _logger = logger; - _fileParser = fileParser; } - public PlaysetModel ParseStream(Stream fileStream, string directory = null) + public void ReadConfigurationToDto(ConfigurationDto dto, IDictionary cels, IDictionary palettes) { - var model = new PlaysetModel(); var initialPositions = new StringBuilder(); - var borderColorIndex = 0; - using var reader = new StreamReader(fileStream); - var data = reader.ReadToEnd(); - - SetPlaysetProperties(data, model, initialPositions); - - SetInitialPositions(model, initialPositions.ToString()); - if (string.IsNullOrEmpty(directory)) return model; - - foreach (var palette in model.Palettes) _fileParser.ParsePalette(directory, palette); - - var celIndex = 0; - foreach (var cel in model.Cels.Where(x => !string.IsNullOrEmpty(x.FileName))) - { - _fileParser.ParseCel(directory, cel, model.Palettes); - cel.ZIndex = (model.Cels.Count - celIndex) * 10; - celIndex++; - } - - if (model.Palettes.Any() && model.Palettes[0].Colors.Any()) model.BorderColor = model.Palettes[0].Colors[borderColorIndex]; - - return model; - } - - public void SetPlaysetProperties(string data, PlaysetModel model, StringBuilder initialPositions) - { - if (string.IsNullOrEmpty(data)) return; - - foreach (var line in data.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries)) - { + foreach (var line in dto.Data.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries)) switch (line.ToCharArray()[0]) { case '(': var resolutionRegex = new Regex(ResolutionRegexPattern); var resolutionMatch = resolutionRegex.Match(line); - model.Width = int.Parse(resolutionMatch.Groups["Width"].Value); - model.Height = int.Parse(resolutionMatch.Groups["Height"].Value); + dto.Width = int.Parse(resolutionMatch.Groups["Width"].Value); + dto.Height = int.Parse(resolutionMatch.Groups["Height"].Value); break; case '[': var borderValue = line[1..]; if (borderValue.Contains(';')) borderValue = borderValue.Split(';')[0].Trim(); - model.BorderColorIndex = int.Parse(borderValue); + dto.BorderIndex = int.Parse(borderValue); break; case '%': - model.Palettes.Add(new PaletteModel(_logger, line)); + dto.Palettes.Add(SetPalette(line, palettes)); break; case '#': - model.Cels.Add(new CelModel(_logger, line)); + dto.Cels.Add(SetCel(line, dto, cels)); break; case '$': case ' ': initialPositions.Append(line.Replace("\\r\\n", "").Replace("\\n", "")); break; } - } + + SetInitialPositions(dto, initialPositions.ToString()); } - private static void SetInitialPositions(PlaysetModel model, string positions) + private CelConfigDto SetCel(string line, IKisekaeFile configuration, IDictionary cels) + { + var cel = new CelConfigDto + { + ConfigId = configuration.Id + }; + + if (line.Contains("%t")) + { + var opacity = line[line.IndexOf("%t", StringComparison.InvariantCultureIgnoreCase)..].Trim(); + opacity = opacity.Split(';', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)[0]; + opacity = opacity.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)[0]; + opacity = opacity.Replace("%t", ""); + cel.Transparency = int.Parse(opacity); + } + + var matcher = new Regex(CelRegex); + var match = matcher.Match(line); + + foreach (var groupName in matcher.GetGroupNames()) + { + match.Groups.TryGetValue(groupName, out var group); + if (string.IsNullOrEmpty(group?.Value)) continue; + + if (string.Equals(groupName, "FileName")) + { + cel.CelId = cels[group.Value.ToLowerInvariant()].Id; + continue; + } + + var property = typeof(CelConfigDto).GetProperty(groupName); + if (property == null) continue; + + var value = group.Value.Trim(); + if (property.PropertyType == typeof(string)) property.SetValue(cel, value); + if (property.PropertyType == typeof(int)) property.SetValue(cel, int.Parse(value)); + + if (property.PropertyType != cel.Sets.GetType()) continue; + + foreach (var id in value.Split(' ', StringSplitOptions.RemoveEmptyEntries)) + { + var success = int.TryParse(id, out var result); + if (!success) continue; + + if (cel.Sets == Set.Unset) + cel.Sets = (Set)(2 ^ result); + else + cel.Sets |= (Set)(2 ^ result); + } + } + + return cel; + } + + private static PaletteDto SetPalette(string line, IDictionary palettes) + { + var parts = line.Split(';'); + var palette = palettes[parts[0].Trim().ToLowerInvariant().Replace("%", "")]; + if (parts.Length > 1) palette.Comment = parts[1].Trim(); + return palette; + } + + private static void SetInitialPositions(ConfigurationDto dto, string positions) { var sets = positions.Split('$', StringSplitOptions.RemoveEmptyEntries); - for (var index = 0; index < sets.Length; index++) + foreach (var set in sets) { - var value = sets[index].Split(' ', StringSplitOptions.RemoveEmptyEntries); + var value = set.Split(' ', StringSplitOptions.RemoveEmptyEntries); for (var innerIndex = 1; innerIndex < value.Length; innerIndex++) { if (value[innerIndex] == "*") continue; var point = value[innerIndex].Split(',', StringSplitOptions.RemoveEmptyEntries); - foreach (var cel in model.Cels.Where(x => x.Id == innerIndex - 1)) - cel.InitialPositions[index] = new Coordinate(int.Parse(point[0]), int.Parse(point[1])); + foreach (var cel in dto.Cels.Where(x => x.Id == innerIndex - 1)) + { + cel.X = int.Parse(point[0]); + cel.Y = int.Parse(point[1]); + } } } } diff --git a/Services/DatabaseService.cs b/Services/DatabaseService.cs index 1dc5ba8..44ae6fb 100644 --- a/Services/DatabaseService.cs +++ b/Services/DatabaseService.cs @@ -9,8 +9,8 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Dapper; -using Dapper.Contrib.Extensions; using fxl.codes.kisekae.Entities; +using fxl.codes.kisekae.Extensions; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -61,7 +61,7 @@ namespace fxl.codes.kisekae.Services await connection.OpenAsync(); var queryParams = new { Id = id, ConfigId = configId }; - var cels = connection.Query("select * from cel_config where configId = @ConfigId", queryParams); + var cels = connection.Query("select * from cel_config where config_id = @ConfigId", queryParams); if (cels == null) { var config = connection.QuerySingle( @@ -99,28 +99,58 @@ namespace fxl.codes.kisekae.Services public async void StoreToDatabase(IFormFile file) { - await using var connection = new NpgsqlConnection(_connectionString); - await connection.OpenAsync(); var memoryStream = await GetAsMemoryStream(file); var checksum = Convert.ToBase64String(await new SHA256Managed().ComputeHashAsync(memoryStream)); memoryStream.Position = 0; // Reset for re-read - var existing = GetExisting(connection, file.FileName, checksum); - if (existing != null) return; + await using var connection = new NpgsqlConnection(_connectionString); + await connection.OpenAsync(); + var transaction = await connection.BeginTransactionAsync(); - _fileParserService.UnzipLzh(file, memoryStream); - var directory = Path.GetFileNameWithoutExtension(file.FileName); - var kisekae = new KisekaeDto + try { - Filename = file.FileName, - Name = directory, - Checksum = checksum - }; + var existing = GetExisting(connection, file.FileName, checksum); + if (existing != null) return; - kisekae.Id = await connection.InsertAsync(kisekae); + _fileParserService.UnzipLzh(file, memoryStream); + var directory = Path.GetFileNameWithoutExtension(file.FileName); + var kisekae = new KisekaeDto + { + Filename = file.FileName, + Name = directory, + Checksum = checksum + }; - var filenames = _storage.GetFileNames($"{Path.Combine(directory, "*")}"); - SetInnerFiles(directory, filenames, kisekae.Id); + kisekae.Id = await connection.InsertAsync(kisekae); + + var filenames = _storage.GetFileNames($"{Path.Combine(directory, "*")}"); + await SetInnerFiles(connection, directory, filenames, kisekae.Id); + + using var saved = await connection.QueryMultipleAsync( + "select * from configuration where kisekae_id = @id; select * from cel where kisekae_id = @id; select * from palette where kisekae_id = @id", + new { id = kisekae.Id }); + var configs = saved.Read().ToList(); + var cels = saved.Read().ToDictionary(x => x.Filename.ToLowerInvariant()); + var palettes = saved.Read().ToDictionary(x => x.Filename.ToLowerInvariant()); + + foreach (var config in configs) + { + _configurationReaderService.ReadConfigurationToDto(config, cels, palettes); + await connection.InsertAsync(config.Cels); + await connection.UpdateAsync(config.Palettes); + await connection.UpdateAsync(config); + } + + var paletteColors = SetPaletteColors(palettes.Values); + await connection.InsertAsync(paletteColors); + + await transaction.CommitAsync(); + } + catch (Exception e) + { + await transaction.RollbackAsync(); + _logger.LogError(e, $"Error saving file {file.FileName}"); + } await connection.CloseAsync(); } @@ -141,7 +171,7 @@ namespace fxl.codes.kisekae.Services return existing; } - private async void SetInnerFiles(string directory, IEnumerable filenames, int id) + private async Task SetInnerFiles(IDbConnection connection, string directory, IEnumerable filenames, int id) { var files = new List(); foreach (var filename in filenames) @@ -181,14 +211,32 @@ namespace fxl.codes.kisekae.Services } } - await using var connection = new NpgsqlConnection(_connectionString); - await connection.OpenAsync(); - await connection.InsertAsync(files.OfType()); await connection.InsertAsync(files.OfType()); await connection.InsertAsync(files.OfType()); + } - await connection.CloseAsync(); + private List SetPaletteColors(IEnumerable palettes) + { + var paletteColors = new List(); + foreach (var palette in palettes) + { + var colors = _fileParserService.ParsePalette(palette, out var groups, out var colorsPerGroup); + + for (var groupIndex = 0; groupIndex < groups; groupIndex++) + for (var colorIndex = 0; colorIndex < colorsPerGroup; colorIndex++) + { + var color = colors[groupIndex * colorsPerGroup + colorIndex]; + paletteColors.Add(new PaletteColorDto + { + Group = groupIndex, + PaletteId = palette.Id, + Hex = color.ToHex() + }); + } + } + + return paletteColors; } private static async Task GetAsMemoryStream(IFormFile file) diff --git a/Services/FileParserService.cs b/Services/FileParserService.cs index 9986b24..0fdb64a 100644 --- a/Services/FileParserService.cs +++ b/Services/FileParserService.cs @@ -6,6 +6,7 @@ using System.IO.IsolatedStorage; using System.Linq; using System.Reflection; using System.Text; +using fxl.codes.kisekae.Entities; using fxl.codes.kisekae.Models; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; @@ -78,27 +79,22 @@ namespace fxl.codes.kisekae.Services _storage.DeleteFile(file.FileName); } - public void ParsePalette(string directory, PaletteModel palette) + public Color[] ParsePalette(PaletteDto palette, out int groups, out int colorsPerGroup) { - _logger.LogTrace($"Reading palette {palette.FileName} from {directory}"); - var buffer = ReadToBuffer(directory, palette.FileName); - if (buffer.Length == 0) return; + groups = 0; + colorsPerGroup = 0; + + if (palette.Data.Length == 0) return null; + if (!palette.Data[..KissHeader.Length].SequenceEqual(KissHeader)) return GetColors(palette.Data); + + // Verify palette mark? + if (palette.Data[4] != 16) _logger.LogError($"{palette.Filename} is not a valid palette file"); - if (buffer[..KissHeader.Length].SequenceEqual(KissHeader)) - { - // Verify palette mark? - if (buffer[4] != 16) _logger.LogError($"{palette.FileName} is not a valid palette file"); + var colorDepth = Convert.ToInt32(palette.Data[5]); + colorsPerGroup = BitConverter.ToInt16(palette.Data, 8); + groups = BitConverter.ToInt16(palette.Data, 10); - var colorDepth = Convert.ToInt32(buffer[5]); - var colorsPerGroup = BitConverter.ToInt16(buffer, 8); - var groups = BitConverter.ToInt16(buffer, 10); - - palette.Colors = GetColors(buffer[32..], colorDepth, colorsPerGroup, groups); - } - else - { - palette.Colors = GetColors(buffer); - } + return GetColors(palette.Data[32..], colorDepth, colorsPerGroup, groups); } public void ParseCel(string directory, CelModel cel, IEnumerable palettes) diff --git a/Startup.cs b/Startup.cs index ac94ee1..c5c7266 100644 --- a/Startup.cs +++ b/Startup.cs @@ -1,4 +1,3 @@ -using Dapper; using fxl.codes.kisekae.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -58,8 +57,6 @@ namespace fxl.codes.kisekae "default", "{controller=Home}/{action=Index}/{id?}/{configId?}"); }); - - DefaultTypeMap.MatchNamesWithUnderscores = true; } } } \ No newline at end of file diff --git a/fxl.codes.kisekae.csproj b/fxl.codes.kisekae.csproj index 5e79b72..05c8fb4 100644 --- a/fxl.codes.kisekae.csproj +++ b/fxl.codes.kisekae.csproj @@ -14,7 +14,6 @@ -