fix for conversion interruption when errors occur

This commit is contained in:
Boris Nikolaev
2026-02-14 13:31:12 +03:00
parent 92b615f077
commit 67b2837684
8 changed files with 158 additions and 36 deletions
+11 -6
View File
@@ -7,6 +7,7 @@ using dq8chr2glb.Converter;
using dq8chr2glb.Core.InfoCfg; using dq8chr2glb.Core.InfoCfg;
using dq8chr2glb.Core.MDSFormat; using dq8chr2glb.Core.MDSFormat;
using dq8chr2glb.Core.MOTFormat; using dq8chr2glb.Core.MOTFormat;
using dq8chr2glb.Logger;
using SixLabors.ImageSharp; using SixLabors.ImageSharp;
using Texture = dq8chr2glb.TM2Format.Texture; using Texture = dq8chr2glb.TM2Format.Texture;
@@ -80,7 +81,7 @@ public class ChrFile
} }
var spacer = new string('.', spaces); var spacer = new string('.', spaces);
Console.WriteLine($" Process: {file.name + spacer} {file.data.Length} bytes"); Log.Line($" Process: {file.name + spacer} {file.data.Length} bytes");
} }
public void Clean() public void Clean()
@@ -118,7 +119,16 @@ public class ChrFile
private void ProcessMDSFile(IncludedFile file, string outputDir) private void ProcessMDSFile(IncludedFile file, string outputDir)
{ {
if (extract)
{
ProcessRawFile(file, outputDir);
}
var mdsScene = Reader.Read(file.data, file.name); var mdsScene = Reader.Read(file.data, file.name);
if (mdsScene == null)
{
return;
}
if (convert) if (convert)
{ {
@@ -127,11 +137,6 @@ public class ChrFile
converter.Convert(mdsScene, textures, rootName); converter.Convert(mdsScene, textures, rootName);
mdsConverters.Add(converter); mdsConverters.Add(converter);
} }
if (extract)
{
ProcessRawFile(file, outputDir);
}
} }
private void ProcessConfig(IncludedFile file, string outputDir) private void ProcessConfig(IncludedFile file, string outputDir)
+45 -9
View File
@@ -38,6 +38,13 @@ public class MDSConverter
var gltfScene = _gltfModel.UseScene("DefaultScene"); var gltfScene = _gltfModel.UseScene("DefaultScene");
var nodesWithParents = new HashSet<int>(); var nodesWithParents = new HashSet<int>();
if (scene.nodes == null || scene.nodes.Length == 0)
{
Log.Line($"There is nothing to convert in {name}", LogLevel.Info);
return;
}
foreach (var node in scene.nodes) foreach (var node in scene.nodes)
{ {
if (node.parentIndex >= 0) if (node.parentIndex >= 0)
@@ -125,12 +132,13 @@ public class MDSConverter
for (var i = clip.startFrame; i < clip.endFrame; i++) for (var i = clip.startFrame; i < clip.endFrame; i++)
{ {
var keyframe = curve.keyframes[i]; if (i >= curve.keyframes.Count || curve.keyframes[i] == null)
if (keyframe == null)
{ {
continue; continue;
} }
var keyframe = curve.keyframes[i];
if (curve.curveType == KeyframeType.Quaternion) if (curve.curveType == KeyframeType.Quaternion)
{ {
var time = keyframe.frame / FRAMERATE / clip.speed; var time = keyframe.frame / FRAMERATE / clip.speed;
@@ -161,6 +169,11 @@ public class MDSConverter
{ {
foreach (var tex in textures) foreach (var tex in textures)
{ {
if (tex == null)
{
continue;
}
byte[] imageBytes; byte[] imageBytes;
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
{ {
@@ -240,20 +253,36 @@ public class MDSConverter
} }
} }
skin.BindJoints(jointBindings); try
{
skin.BindJoints(jointBindings);
}
catch (Exception e)
{
Log.Error(e);
return;
}
foreach (var node in _gltfModel.LogicalNodes) foreach (var node in _gltfModel.LogicalNodes)
{ {
if (node.Mesh != null) if (node.Mesh != null)
{ {
if (node.Mesh.Primitives.Any(i => i.VertexAccessors.ContainsKey("WEIGHTS_0"))) var hasWeights = node.Mesh.Primitives.All(i => i.VertexAccessors.ContainsKey("WEIGHTS_0"));
if (hasWeights)
{ {
if (Quaternion.Dot(node.LocalTransform.GetDecomposed().Rotation, Quaternion.Identity) >= 0.999f) if (Quaternion.Dot(node.LocalTransform.GetDecomposed().Rotation, Quaternion.Identity) >= 0.999f)
{ {
node.LocalTransform = Matrix4x4.Identity; node.LocalTransform = Matrix4x4.Identity;
} }
node.Skin = skin; try
{
node.Skin = skin;
}
catch (Exception e)
{
Log.Line(e.TargetSite.Name);
}
} }
} }
} }
@@ -273,13 +302,20 @@ public class MDSConverter
{ {
var path = Path.Combine(filePath, _name + (textFormat ? ".gltf" : ".glb")); var path = Path.Combine(filePath, _name + (textFormat ? ".gltf" : ".glb"));
if (textFormat) try
{ {
_gltfModel.SaveGLTF(path); if (textFormat)
{
_gltfModel.SaveGLTF(path);
}
else
{
_gltfModel.SaveGLB(path);
}
} }
else catch (Exception e)
{ {
_gltfModel.SaveGLB(path); Log.Error(e);
} }
} }
} }
+27 -6
View File
@@ -20,7 +20,6 @@ namespace dq8chr2glb.Converter
var hasUvs = (mdsMesh.features & MeshFeatures.UVs) != 0; var hasUvs = (mdsMesh.features & MeshFeatures.UVs) != 0;
var hasSkinning = (mdsMesh.features & MeshFeatures.Weights) != 0; var hasSkinning = (mdsMesh.features & MeshFeatures.Weights) != 0;
// Выбираем подходящий тип вершины на основе атрибутов
if (hasSkinning) if (hasSkinning)
{ {
if (hasUvs) if (hasUvs)
@@ -52,9 +51,9 @@ namespace dq8chr2glb.Converter
private static (bool hasUvs, bool hasSkinning) AnalyzeMeshAttributes(MDSMesh mdsMesh) private static (bool hasUvs, bool hasSkinning) AnalyzeMeshAttributes(MDSMesh mdsMesh)
{ {
var hasUvs = mdsMesh.uv != null && mdsMesh.uv.Length > 0 && var hasUvs = mdsMesh.uv != null && mdsMesh.uv.Length > 0 &&
Array.Exists(mdsMesh.uv, uv => uv != null && uv.Length >= 2); Array.Exists(mdsMesh.uv, uv => uv != null && uv.Length >= 2);
var hasSkinning = mdsMesh.weights != null && mdsMesh.bones != null && var hasSkinning = mdsMesh.weights != null && mdsMesh.bones != null &&
mdsMesh.bones.Length > 0; // && Array.Exists(mdsMesh.bones, b => b != null) mdsMesh.bones.Length > 0;
return (hasUvs, hasSkinning); return (hasUvs, hasSkinning);
} }
@@ -102,6 +101,8 @@ namespace dq8chr2glb.Converter
var boneWeights = mdsMesh.weights[i]; var boneWeights = mdsMesh.weights[i];
var bindings = new (int JointIndex, float Weight)[Math.Min(4, boneIndices.Length)]; var bindings = new (int JointIndex, float Weight)[Math.Min(4, boneIndices.Length)];
var wSum = 0f;
for (var b = 0; b < bindings.Length; b++) for (var b = 0; b < bindings.Length; b++)
{ {
var mdsBoneIndex = boneIndices[b]; var mdsBoneIndex = boneIndices[b];
@@ -109,14 +110,25 @@ namespace dq8chr2glb.Converter
{ {
var gltfBoneIndex = nodesMap[mdsBoneIndex]; var gltfBoneIndex = nodesMap[mdsBoneIndex];
bindings[b] = (Math.Max(gltfBoneIndex, 0), boneWeights[b]); bindings[b] = (Math.Max(gltfBoneIndex, 0), boneWeights[b]);
wSum += boneWeights[b];
} }
else else
{ {
// Обработка случая, когда MDS-индекс не найден в маппинге bindings[b] = (0, boneWeights[b]);
bindings[b] = (0, boneWeights[b]); // или другое значение по умолчанию
} }
} }
if (wSum < 0.00001f)
{
skinningData.Add(Array.Empty<(int, float)>());
continue;
}
for (var index = 0; index < bindings.Length; index++)
{
bindings[index].Weight /= wSum;
}
skinningData.Add(bindings); skinningData.Add(bindings);
} }
else else
@@ -185,7 +197,16 @@ namespace dq8chr2glb.Converter
} }
} }
return root.CreateMesh(meshBuilder); try
{
return root.CreateMesh(meshBuilder);
}
catch (Exception e)
{
Log.Line($"Create Mesh Error: {mdsMesh.name}", LogLevel.Error);
Log.Error(e);
return null;
}
} }
private static VertexBuilder<TvG, TvM, TvS> CreateVertex<TvG, TvM, TvS>( private static VertexBuilder<TvG, TvM, TvS> CreateVertex<TvG, TvM, TvS>(
+1 -1
View File
@@ -17,7 +17,7 @@ public class Reader
if (header.version != 200) if (header.version != 200)
{ {
Console.WriteLine($"Unknown version: {header.version}"); Log.Line($"Unknown version: {header.version}.");
return null; return null;
} }
+15 -1
View File
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using dq8chr2glb.Logger;
namespace dq8chr2glb.Core.MOTFormat; namespace dq8chr2glb.Core.MOTFormat;
@@ -93,7 +94,15 @@ public class Importer
keyframe.type = KeyframeType.Quaternion; keyframe.type = KeyframeType.Quaternion;
keyframe.rotation = ReadQuaternion(data, valuesOffset, scale); keyframe.rotation = ReadQuaternion(data, valuesOffset, scale);
curve.keyframes[frame] = keyframe; try
{
curve.keyframes[frame] = keyframe;
}
catch (Exception e)
{
Log.Line($"Skip negative frame number: {frame}", LogLevel.Warning);
}
valuesOffset += 8; valuesOffset += 8;
} }
} }
@@ -144,6 +153,11 @@ public class Importer
var toValues = header.valuesOffset; var toValues = header.valuesOffset;
foreach (var frame in frames) foreach (var frame in frames)
{ {
if (frame == null || frame < 0)
{
continue;
}
var scale = new Vector3(header.scaleX, header.scaleY, var scale = new Vector3(header.scaleX, header.scaleY,
header.scaleZ); header.scaleZ);
+26 -1
View File
@@ -13,13 +13,38 @@ public enum LogLevel
public static class Log public static class Log
{ {
// public const LogLevel LogLevel = Logger.LogLevel.Info;
public static void Line(object data, LogLevel level = LogLevel.Debug) public static void Line(object data, LogLevel level = LogLevel.Debug)
{ {
var lastColor = Console.ForegroundColor;
var color = level switch
{
LogLevel.Error => ConsoleColor.Red,
LogLevel.Debug => ConsoleColor.White,
LogLevel.Info => ConsoleColor.White,
LogLevel.Warning => ConsoleColor.Yellow,
LogLevel.None => ConsoleColor.White,
};
if (level == LogLevel.Debug)
{
return;
}
Console.ForegroundColor = color;
Console.WriteLine(data.ToString()); Console.WriteLine(data.ToString());
Console.ForegroundColor = lastColor;
} }
public static void Error(Exception e, LogLevel level = LogLevel.Error) public static void Error(Exception e, LogLevel level = LogLevel.Error)
{ {
Console.WriteLine($"{e.Message}\n From:\n{e.Source}\nStacktrace:\n{e.StackTrace}"); var lastColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write($"[{level}] ");
Console.ForegroundColor = ConsoleColor.DarkRed;
// Console.Write($"{e.Message}\n From:\n{e.Source}\nStacktrace:\n{e.StackTrace}\n");
Console.Write($"{e.Message}\n");
Console.ForegroundColor = lastColor;
} }
} }
+5 -4
View File
@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using dq8chr2glb.Logger;
namespace dq8chr2glb; namespace dq8chr2glb;
@@ -64,7 +65,7 @@ public class Program
var files = Directory.GetFiles(inputPath, "*.chr", SearchOption.TopDirectoryOnly); var files = Directory.GetFiles(inputPath, "*.chr", SearchOption.TopDirectoryOnly);
foreach (var file in files) foreach (var file in files)
{ {
Console.WriteLine($"Processing: {Path.GetFileName(file)}"); Log.Line($"Processing: {Path.GetFileName(file)}", LogLevel.Info);
try try
{ {
chrFile.Process(file, outputPath); chrFile.Process(file, outputPath);
@@ -78,18 +79,18 @@ public class Program
chrFile.Clean(); chrFile.Clean();
} }
Console.WriteLine(files.Length == 0 ? "No .chr files found in the input directory." : "Done!"); Log.Line(files.Length == 0 ? "No .chr files found in the input directory." : "Done!", LogLevel.Info);
} }
else else
{ {
if (!File.Exists(inputPath)) if (!File.Exists(inputPath))
{ {
Console.WriteLine($"Error: Input file not found: {inputPath}"); Log.Line($"Input file not found: {inputPath}", LogLevel.Error);
return; return;
} }
chrFile.Process(inputPath, outputPath); chrFile.Process(inputPath, outputPath);
Console.WriteLine("Done!"); Log.Line("Done!", LogLevel.Info);
} }
} }
+26 -6
View File
@@ -1,4 +1,5 @@
using System; using System;
using dq8chr2glb.Logger;
using SixLabors.ImageSharp; using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@@ -12,6 +13,12 @@ public static class TM2Format
var header = TM2Header.GetHeader(data); var header = TM2Header.GetHeader(data);
var image = GetImageBuffer(header, data); var image = GetImageBuffer(header, data);
if (image == null)
{
Log.Line($"Failed to create texture {name}", LogLevel.Error);
return null;
}
var img = new Image<Rgba32>(header.Width, header.Height); var img = new Image<Rgba32>(header.Width, header.Height);
for (var y = 0; y < header.Height; y++) for (var y = 0; y < header.Height; y++)
@@ -45,15 +52,23 @@ public static class TM2Format
{ {
var palette = new byte[256][]; var palette = new byte[256][];
for (var i = 0; i < 256; ++i) try
{ {
var rgba = new byte[4]; for (var i = 0; i < 256; ++i)
Array.Copy(file, 0x40 + header.ImageSize + i * 4, rgba, 0, 4); {
rgba[3] = (byte)Math.Min(rgba[3] << 1, 0xFF); var rgba = new byte[4];
Array.Copy(file, 0x40 + header.ImageSize + i * 4, rgba, 0, 4);
rgba[3] = (byte)Math.Min(rgba[3] << 1, 0xFF);
(rgba[2], rgba[0]) = (rgba[0], rgba[2]); (rgba[2], rgba[0]) = (rgba[0], rgba[2]);
palette[(i & 0xe7) | ((i & 0x10) >> 1) | ((i & 0x08) << 1)] = rgba; palette[(i & 0xe7) | ((i & 0x10) >> 1) | ((i & 0x08) << 1)] = rgba;
}
}
catch (Exception e)
{
Log.Error(e);
palette = null;
} }
return palette; return palette;
@@ -63,6 +78,11 @@ public static class TM2Format
{ {
var palette = GetPalette8bbp(tm2file, header); var palette = GetPalette8bbp(tm2file, header);
if (palette == null)
{
return null;
}
var imageBuf = new byte[4 * header.Width * header.Height]; var imageBuf = new byte[4 * header.Width * header.Height];
for (var y = header.Height - 1; y >= 0; --y) for (var y = header.Height - 1; y >= 0; --y)
{ {