支持EV图像合成
This commit is contained in:
parent
f38fc2c4cc
commit
a6f10be546
@ -1,243 +1,30 @@
|
|||||||
using ImageMagick;
|
using ImageMagick;
|
||||||
using System.Text;
|
|
||||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
|
||||||
|
|
||||||
namespace EscudeTools
|
namespace EscudeTools
|
||||||
{
|
{
|
||||||
public class Image
|
|
||||||
{
|
|
||||||
//public byte[] file = new byte[64]; // Image file name
|
|
||||||
//public int page; // Image memory
|
|
||||||
//public int back_page; // Back image memory
|
|
||||||
public uint width; // Width
|
|
||||||
public uint height; // Height
|
|
||||||
public uint depth; // Color depth
|
|
||||||
//public int id; // ID
|
|
||||||
public int reff; // Reference counter
|
|
||||||
//public bool cache; // Cache flag
|
|
||||||
public bool isFile; // Is it an image file
|
|
||||||
//public uint[] extra = new uint[8]; // Reserved
|
|
||||||
|
|
||||||
public string fileStr; // 自己加的,用于保存文件名
|
|
||||||
}
|
|
||||||
public class GInfo
|
|
||||||
{
|
|
||||||
public int width; // Width
|
|
||||||
public int height; // Height
|
|
||||||
public int depth; // Color depth
|
|
||||||
public string pixel; // address of the pixel data
|
|
||||||
public uint pitch;
|
|
||||||
public string palette;
|
|
||||||
}
|
|
||||||
public class LsfImage
|
|
||||||
{
|
|
||||||
//public bool cache; // Cache flag
|
|
||||||
public Image img; // Layer image
|
|
||||||
}
|
|
||||||
public class LsfData
|
|
||||||
{
|
|
||||||
//public byte[] path = new byte[64]; // LSF folder
|
|
||||||
public LsfFileHeader lfh; // LSF file header
|
|
||||||
public LsfLayerInfo[] lli; // LSF layer information
|
|
||||||
public LsfImage[] layer; // LSF layer image
|
|
||||||
|
|
||||||
public string pathStr;
|
|
||||||
public string lsfName;
|
|
||||||
}
|
|
||||||
public class LsfFileHeader
|
|
||||||
{
|
|
||||||
//public uint signature; // Header signature (LSF) 0x46534C
|
|
||||||
public ushort revision; // Revision number
|
|
||||||
public ushort bg; // Background flag
|
|
||||||
public ushort id; // ID
|
|
||||||
public ushort layer_count; // Number of layers
|
|
||||||
public int width; // Width in pixels
|
|
||||||
public int height; // Height in pixels
|
|
||||||
public int bx; // Base coordinates
|
|
||||||
public int by; // Base coordinates
|
|
||||||
}
|
|
||||||
public class LsfLayerInfo
|
|
||||||
{
|
|
||||||
public byte[] name = new byte[64]; // File name
|
|
||||||
public byte[] text = new byte[64]; // Generic string
|
|
||||||
public Rect rect; // Layer position
|
|
||||||
public int cx; // Center coordinates
|
|
||||||
public int cy; // Center coordinates
|
|
||||||
public byte index; // Position
|
|
||||||
public byte state; // State
|
|
||||||
public byte mode; // Drawing mode
|
|
||||||
public byte opacity; // Opacity
|
|
||||||
public uint fill; // Fill color
|
|
||||||
public uint value; // Generic value
|
|
||||||
|
|
||||||
public string nameStr; // 自己加的,用于保存文件名
|
|
||||||
public string textStr; // 自己加的,用于保存通用名
|
|
||||||
public string indexStr; // Position str
|
|
||||||
public string stateStr; // State str
|
|
||||||
public string modeStr; // Drawing mode str
|
|
||||||
public string opacityStr; // Opacity str
|
|
||||||
|
|
||||||
public bool skip = false; // 是否跳过
|
|
||||||
|
|
||||||
}
|
|
||||||
public class Rect
|
|
||||||
{
|
|
||||||
public int left; // Top-left corner X coordinate of the rectangle
|
|
||||||
public int top; // Top-left corner Y coordinate of the rectangle
|
|
||||||
public int right; // Bottom-right corner X coordinate of the rectangle
|
|
||||||
public int bottom; // Bottom-right corner Y coordinate of the rectangle
|
|
||||||
}
|
|
||||||
public class CgInfo
|
|
||||||
{
|
|
||||||
public int kind; // Image category
|
|
||||||
public int index; // Image index
|
|
||||||
public int x; // Coordinates
|
|
||||||
public int y; // Coordinates
|
|
||||||
public int scale; // Scale factor
|
|
||||||
public bool loop; // Loop flag
|
|
||||||
public byte[] name = new byte[64]; // Registered name
|
|
||||||
public byte[] file = new byte[64]; // File name
|
|
||||||
public byte[] option = new byte[128]; // Options
|
|
||||||
public uint coverd; // White-out ID
|
|
||||||
public uint filter; // Filter
|
|
||||||
public uint color; // Color
|
|
||||||
public uint id; // Image identification ID
|
|
||||||
public uint loc; // Coordinate list
|
|
||||||
public uint spot; // Coordinate index
|
|
||||||
public int order; // CG viewing display order
|
|
||||||
public uint link; // Related CG
|
|
||||||
|
|
||||||
public string nameStr;
|
|
||||||
public string fileStr;
|
|
||||||
public string optionStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ImageManager
|
public class ImageManager
|
||||||
{
|
{
|
||||||
static readonly byte[] lsfFileSignature = [0x4C, 0x53, 0x46, 0x00];
|
public static bool EvProcess(LsfData ld, int[] n, string target)
|
||||||
static readonly byte[] lsfLayerSkipSignature = [0x00, 0x75, 0x6C, 0x00]; //flowchat部分的lsf块
|
|
||||||
static readonly byte[] motV1Signature = [0x6D, 0x6F, 0x74, 0x00]; // mot v1 file signature
|
|
||||||
static readonly byte[] motV2Signature = [0x4D, 0x4F, 0x54, 0x00]; // MOT v2 file signature
|
|
||||||
private static string WorkPath = string.Empty;
|
|
||||||
private LsfData lsfData = new();
|
|
||||||
private List<LsfData> lsfDatas = [];
|
|
||||||
|
|
||||||
private bool preFetchInfo;
|
|
||||||
|
|
||||||
public bool LoadLsf(string path, bool preFI = false)
|
|
||||||
{
|
{
|
||||||
if (!File.Exists(path))
|
//get base size
|
||||||
return false;
|
int height = ld.lfh.height, width = ld.lfh.width;
|
||||||
preFetchInfo = preFI;
|
using var baseImage = new MagickImage(MagickColors.Transparent, (uint)width, (uint)height);
|
||||||
lsfData.pathStr = Path.GetDirectoryName(path);
|
for (int i = 0; i < n.Length; i++)
|
||||||
lsfData.lsfName = Path.GetFileNameWithoutExtension(path);
|
|
||||||
lsfData.lfh = LoadLsfHeader(path);
|
|
||||||
lsfData.lli = LoadLsfLayerInfo(path);
|
|
||||||
lsfData.layer = new LsfImage[lsfData.lfh.layer_count];
|
|
||||||
for (int i = 0; i < lsfData.lfh.layer_count; i++)
|
|
||||||
{
|
{
|
||||||
string imgPath = Path.Combine(lsfData.pathStr, lsfData.lli[i].nameStr + ".png");
|
string imgPath = ld.layer[n[i]].img.fileStr;
|
||||||
LsfImage li = new();
|
using var overlayImage = new MagickImage(imgPath);
|
||||||
if (!lsfData.lli[i].skip)
|
int offsetX = ld.lli[n[i]].rect.left;
|
||||||
{
|
int offsetY = ld.lli[n[i]].rect.top;
|
||||||
li.img = LoadLsfImage(imgPath);
|
int mode = ld.lli[n[i]].mode;
|
||||||
lsfData.layer[i] = li;
|
baseImage.Composite(overlayImage, offsetX, offsetY, (mode == 3) ? CompositeOperator.Multiply : ((mode == 10) ? CompositeOperator.Plus : CompositeOperator.Over));
|
||||||
}
|
}
|
||||||
}
|
baseImage.Write(target);
|
||||||
lsfDatas.Add(lsfData);
|
|
||||||
lsfData = new();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LsfFileHeader LoadLsfHeader(string path)
|
public static bool STProcess()
|
||||||
{
|
{
|
||||||
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
|
throw new NotImplementedException();
|
||||||
if (fs.Length < 0x1C)
|
|
||||||
throw new Exception("Invalid LSF Header");
|
|
||||||
using var br = new BinaryReader(fs);
|
|
||||||
byte[] head = br.ReadBytes(4);
|
|
||||||
if (!head.SequenceEqual(lsfFileSignature))
|
|
||||||
throw new Exception("Invalid LSF file");
|
|
||||||
LsfFileHeader lfh = new()
|
|
||||||
{
|
|
||||||
//lfh.signature = br.ReadUInt32(); //无用
|
|
||||||
revision = br.ReadUInt16(),
|
|
||||||
bg = br.ReadUInt16(),
|
|
||||||
id = br.ReadUInt16(),
|
|
||||||
layer_count = br.ReadUInt16(),
|
|
||||||
width = br.ReadInt32(),
|
|
||||||
height = br.ReadInt32(),
|
|
||||||
bx = br.ReadInt32(),
|
|
||||||
by = br.ReadInt32()
|
|
||||||
};
|
|
||||||
return lfh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private LsfLayerInfo[] LoadLsfLayerInfo(string path)
|
|
||||||
{
|
|
||||||
EncodingProvider provider = CodePagesEncodingProvider.Instance;
|
|
||||||
Encoding? shiftJis = provider.GetEncoding("shift-jis");
|
|
||||||
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
|
|
||||||
using var br = new BinaryReader(fs);
|
|
||||||
br.ReadBytes(0x1C); // Skip the header
|
|
||||||
long remainingBytes = br.BaseStream.Length - br.BaseStream.Position;
|
|
||||||
if (remainingBytes != lsfData.lfh.layer_count * 0xA4)
|
|
||||||
throw new Exception("Invalid LSF Layer Info");
|
|
||||||
LsfLayerInfo[] llis = new LsfLayerInfo[lsfData.lfh.layer_count];
|
|
||||||
for (int i = 0; i < lsfData.lfh.layer_count; i++)
|
|
||||||
{
|
|
||||||
LsfLayerInfo l = new()
|
|
||||||
{
|
|
||||||
name = br.ReadBytes(64),
|
|
||||||
text = br.ReadBytes(64),
|
|
||||||
rect = new Rect
|
|
||||||
{
|
|
||||||
left = br.ReadInt32(),
|
|
||||||
top = br.ReadInt32(),
|
|
||||||
right = br.ReadInt32(),
|
|
||||||
bottom = br.ReadInt32()
|
|
||||||
},
|
|
||||||
cx = br.ReadInt32(),
|
|
||||||
cy = br.ReadInt32(),
|
|
||||||
index = br.ReadByte(),
|
|
||||||
state = br.ReadByte(),
|
|
||||||
mode = br.ReadByte(),
|
|
||||||
opacity = br.ReadByte(),
|
|
||||||
fill = br.ReadUInt32(),
|
|
||||||
value = br.ReadUInt32()
|
|
||||||
};
|
|
||||||
if (l.name.Take(4).SequenceEqual(lsfLayerSkipSignature))//临时处理
|
|
||||||
l.skip = true;
|
|
||||||
l.nameStr = shiftJis.GetString(l.name).TrimEnd('\0');
|
|
||||||
l.textStr = shiftJis.GetString(l.text).TrimEnd('\0');
|
|
||||||
l.indexStr = l.index.ToString().TrimEnd('\0');
|
|
||||||
l.stateStr = l.state.ToString().TrimEnd('\0');
|
|
||||||
l.modeStr = l.mode.ToString().TrimEnd('\0');
|
|
||||||
l.opacityStr = l.opacity.ToString().TrimEnd('\0');
|
|
||||||
llis[i] = l;
|
|
||||||
}
|
|
||||||
return llis;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Image LoadLsfImage(string imgPath)
|
|
||||||
{
|
|
||||||
if (!File.Exists(imgPath))
|
|
||||||
throw new Exception("Image file not found");//一般文件都是存在的,不存在是因为这是特殊lsf
|
|
||||||
Image i = new()
|
|
||||||
{
|
|
||||||
fileStr = imgPath,
|
|
||||||
isFile = true,
|
|
||||||
reff = 1
|
|
||||||
};
|
|
||||||
if (preFetchInfo)
|
|
||||||
{
|
|
||||||
using var image = new MagickImage(imgPath);
|
|
||||||
i.width = image.Width;
|
|
||||||
i.height = image.Height;
|
|
||||||
i.depth = image.Depth;
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
244
EscudeTools/LsfManager.cs
Normal file
244
EscudeTools/LsfManager.cs
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
using ImageMagick;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace EscudeTools
|
||||||
|
{
|
||||||
|
public class Image
|
||||||
|
{
|
||||||
|
//public byte[] file = new byte[64]; // Image file name
|
||||||
|
//public int page; // Image memory
|
||||||
|
//public int back_page; // Back image memory
|
||||||
|
public uint width; // Width
|
||||||
|
public uint height; // Height
|
||||||
|
public uint depth; // Color depth
|
||||||
|
//public int id; // ID
|
||||||
|
public int reff; // Reference counter
|
||||||
|
//public bool cache; // Cache flag
|
||||||
|
public bool isFile; // Is it an image file
|
||||||
|
//public uint[] extra = new uint[8]; // Reserved
|
||||||
|
|
||||||
|
public string fileStr; // 自己加的,用于保存文件名
|
||||||
|
}
|
||||||
|
public class GInfo
|
||||||
|
{
|
||||||
|
public int width; // Width
|
||||||
|
public int height; // Height
|
||||||
|
public int depth; // Color depth
|
||||||
|
public string pixel; // address of the pixel data
|
||||||
|
public uint pitch;
|
||||||
|
public string palette;
|
||||||
|
}
|
||||||
|
public class LsfImage
|
||||||
|
{
|
||||||
|
//public bool cache; // Cache flag
|
||||||
|
public Image img; // Layer image
|
||||||
|
}
|
||||||
|
public class LsfData
|
||||||
|
{
|
||||||
|
//public byte[] path = new byte[64]; // LSF folder
|
||||||
|
public LsfFileHeader lfh; // LSF file header
|
||||||
|
public LsfLayerInfo[] lli; // LSF layer information
|
||||||
|
public LsfImage[] layer; // LSF layer image
|
||||||
|
|
||||||
|
public string pathStr;
|
||||||
|
public string lsfName;
|
||||||
|
}
|
||||||
|
public class LsfFileHeader
|
||||||
|
{
|
||||||
|
//public uint signature; // Header signature (LSF) 0x46534C
|
||||||
|
public ushort revision; // Revision number
|
||||||
|
public ushort bg; // Background flag
|
||||||
|
public ushort id; // ID
|
||||||
|
public ushort layer_count; // Number of layers
|
||||||
|
public int width; // Width in pixels
|
||||||
|
public int height; // Height in pixels
|
||||||
|
public int bx; // Base coordinates
|
||||||
|
public int by; // Base coordinates
|
||||||
|
}
|
||||||
|
public class LsfLayerInfo
|
||||||
|
{
|
||||||
|
public byte[] name = new byte[64]; // File name
|
||||||
|
public byte[] text = new byte[64]; // Generic string
|
||||||
|
public Rect rect; // Layer position
|
||||||
|
public int cx; // Center coordinates
|
||||||
|
public int cy; // Center coordinates
|
||||||
|
public byte index; // Position
|
||||||
|
public byte state; // State
|
||||||
|
public byte mode; // Drawing mode
|
||||||
|
public byte opacity; // Opacity
|
||||||
|
public uint fill; // Fill color
|
||||||
|
public uint value; // Generic value
|
||||||
|
|
||||||
|
public string nameStr; // 自己加的,用于保存文件名
|
||||||
|
public string textStr; // 自己加的,用于保存通用名
|
||||||
|
public string indexStr; // Position str
|
||||||
|
public string stateStr; // State str
|
||||||
|
public string modeStr; // Drawing mode str
|
||||||
|
public string opacityStr; // Opacity str
|
||||||
|
|
||||||
|
public bool skip = false; // 是否跳过
|
||||||
|
|
||||||
|
}
|
||||||
|
public class Rect
|
||||||
|
{
|
||||||
|
public int left; // Top-left corner X coordinate of the rectangle
|
||||||
|
public int top; // Top-left corner Y coordinate of the rectangle
|
||||||
|
public int right; // Bottom-right corner X coordinate of the rectangle
|
||||||
|
public int bottom; // Bottom-right corner Y coordinate of the rectangle
|
||||||
|
}
|
||||||
|
public class CgInfo
|
||||||
|
{
|
||||||
|
public int kind; // Image category
|
||||||
|
public int index; // Image index
|
||||||
|
public int x; // Coordinates
|
||||||
|
public int y; // Coordinates
|
||||||
|
public int scale; // Scale factor
|
||||||
|
public bool loop; // Loop flag
|
||||||
|
public byte[] name = new byte[64]; // Registered name
|
||||||
|
public byte[] file = new byte[64]; // File name
|
||||||
|
public byte[] option = new byte[128]; // Options
|
||||||
|
public uint coverd; // White-out ID
|
||||||
|
public uint filter; // Filter
|
||||||
|
public uint color; // Color
|
||||||
|
public uint id; // Image identification ID
|
||||||
|
public uint loc; // Coordinate list
|
||||||
|
public uint spot; // Coordinate index
|
||||||
|
public int order; // CG viewing display order
|
||||||
|
public uint link; // Related CG
|
||||||
|
|
||||||
|
public string nameStr;
|
||||||
|
public string fileStr;
|
||||||
|
public string optionStr;
|
||||||
|
}
|
||||||
|
public class LsfManager
|
||||||
|
{
|
||||||
|
static readonly byte[] lsfFileSignature = [0x4C, 0x53, 0x46, 0x00];
|
||||||
|
static readonly byte[] lsfLayerSkipSignature = [0x00, 0x75, 0x6C, 0x00]; //flowchat部分的lsf块
|
||||||
|
private static string WorkPath = string.Empty;
|
||||||
|
private LsfData lsfData = new();
|
||||||
|
private Dictionary<string, LsfData> lsfDataLookup = new();
|
||||||
|
|
||||||
|
private bool preFetchInfo;
|
||||||
|
|
||||||
|
public bool LoadLsf(string path, bool preFI = false)
|
||||||
|
{
|
||||||
|
if (!File.Exists(path))
|
||||||
|
return false;
|
||||||
|
preFetchInfo = preFI;
|
||||||
|
lsfData.pathStr = Path.GetDirectoryName(path);
|
||||||
|
lsfData.lsfName = Path.GetFileNameWithoutExtension(path).ToLower();
|
||||||
|
lsfData.lfh = LoadLsfHeader(path);
|
||||||
|
lsfData.lli = LoadLsfLayerInfo(path);
|
||||||
|
lsfData.layer = new LsfImage[lsfData.lfh.layer_count];
|
||||||
|
for (int i = 0; i < lsfData.lfh.layer_count; i++)
|
||||||
|
{
|
||||||
|
string imgPath = Path.Combine(lsfData.pathStr, lsfData.lli[i].nameStr + ".png");
|
||||||
|
LsfImage li = new();
|
||||||
|
if (!lsfData.lli[i].skip)
|
||||||
|
{
|
||||||
|
li.img = LoadLsfImage(imgPath);
|
||||||
|
lsfData.layer[i] = li;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lsfDataLookup[lsfData.lsfName] = lsfData;
|
||||||
|
lsfData = new();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LsfData? FindLsfDataByName(string name)
|
||||||
|
{
|
||||||
|
lsfDataLookup.TryGetValue(name.ToLower(), out var lsfData);
|
||||||
|
return lsfData; // 如果未找到,则返回 null
|
||||||
|
}
|
||||||
|
|
||||||
|
private LsfFileHeader LoadLsfHeader(string path)
|
||||||
|
{
|
||||||
|
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||||
|
if (fs.Length < 0x1C)
|
||||||
|
throw new Exception("Invalid LSF Header");
|
||||||
|
using var br = new BinaryReader(fs);
|
||||||
|
byte[] head = br.ReadBytes(4);
|
||||||
|
if (!head.SequenceEqual(lsfFileSignature))
|
||||||
|
throw new Exception("Invalid LSF file");
|
||||||
|
LsfFileHeader lfh = new()
|
||||||
|
{
|
||||||
|
//lfh.signature = br.ReadUInt32(); //无用
|
||||||
|
revision = br.ReadUInt16(),
|
||||||
|
bg = br.ReadUInt16(),
|
||||||
|
id = br.ReadUInt16(),
|
||||||
|
layer_count = br.ReadUInt16(),
|
||||||
|
width = br.ReadInt32(),
|
||||||
|
height = br.ReadInt32(),
|
||||||
|
bx = br.ReadInt32(),
|
||||||
|
by = br.ReadInt32()
|
||||||
|
};
|
||||||
|
return lfh;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LsfLayerInfo[] LoadLsfLayerInfo(string path)
|
||||||
|
{
|
||||||
|
EncodingProvider provider = CodePagesEncodingProvider.Instance;
|
||||||
|
Encoding? shiftJis = provider.GetEncoding("shift-jis");
|
||||||
|
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||||
|
using var br = new BinaryReader(fs);
|
||||||
|
br.ReadBytes(0x1C); // Skip the header
|
||||||
|
long remainingBytes = br.BaseStream.Length - br.BaseStream.Position;
|
||||||
|
if (remainingBytes != lsfData.lfh.layer_count * 0xA4)
|
||||||
|
throw new Exception("Invalid LSF Layer Info");
|
||||||
|
LsfLayerInfo[] llis = new LsfLayerInfo[lsfData.lfh.layer_count];
|
||||||
|
for (int i = 0; i < lsfData.lfh.layer_count; i++)
|
||||||
|
{
|
||||||
|
LsfLayerInfo l = new()
|
||||||
|
{
|
||||||
|
name = br.ReadBytes(64),
|
||||||
|
text = br.ReadBytes(64),
|
||||||
|
rect = new Rect
|
||||||
|
{
|
||||||
|
left = br.ReadInt32(),
|
||||||
|
top = br.ReadInt32(),
|
||||||
|
right = br.ReadInt32(),
|
||||||
|
bottom = br.ReadInt32()
|
||||||
|
},
|
||||||
|
cx = br.ReadInt32(),
|
||||||
|
cy = br.ReadInt32(),
|
||||||
|
index = br.ReadByte(),
|
||||||
|
state = br.ReadByte(),
|
||||||
|
mode = br.ReadByte(),
|
||||||
|
opacity = br.ReadByte(),
|
||||||
|
fill = br.ReadUInt32(),
|
||||||
|
value = br.ReadUInt32()
|
||||||
|
};
|
||||||
|
if (l.name.Take(4).SequenceEqual(lsfLayerSkipSignature))//临时处理
|
||||||
|
l.skip = true;
|
||||||
|
l.nameStr = shiftJis.GetString(l.name).TrimEnd('\0');
|
||||||
|
l.textStr = shiftJis.GetString(l.text).TrimEnd('\0');
|
||||||
|
l.indexStr = l.index.ToString().TrimEnd('\0');
|
||||||
|
l.stateStr = l.state.ToString().TrimEnd('\0');
|
||||||
|
l.modeStr = l.mode.ToString().TrimEnd('\0');
|
||||||
|
l.opacityStr = l.opacity.ToString().TrimEnd('\0');
|
||||||
|
llis[i] = l;
|
||||||
|
}
|
||||||
|
return llis;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Image LoadLsfImage(string imgPath)
|
||||||
|
{
|
||||||
|
if (!File.Exists(imgPath))
|
||||||
|
throw new Exception("Image file not found");//一般文件都是存在的,不存在是因为这是特殊lsf
|
||||||
|
Image i = new()
|
||||||
|
{
|
||||||
|
fileStr = imgPath,
|
||||||
|
isFile = true,
|
||||||
|
reff = 1
|
||||||
|
};
|
||||||
|
if (preFetchInfo)
|
||||||
|
{
|
||||||
|
using var image = new MagickImage(imgPath);
|
||||||
|
i.width = image.Width;
|
||||||
|
i.height = image.Height;
|
||||||
|
i.depth = image.Depth;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,207 @@
|
|||||||
namespace EscudeTools
|
using Microsoft.Data.Sqlite;
|
||||||
|
|
||||||
|
namespace EscudeTools
|
||||||
{
|
{
|
||||||
internal class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
if (Directory.Exists(args[0]))
|
if (Directory.Exists(args[0]) && File.Exists(args[1]))
|
||||||
|
//if (File.Exists(args[0]))
|
||||||
{
|
{
|
||||||
|
string graphicsDBPath = args[1];
|
||||||
|
using SqliteConnection connection = new($"Data Source={graphicsDBPath};");
|
||||||
|
connection.Open();
|
||||||
|
List<string> tableNames = [];
|
||||||
|
string[] foundTN = new string[2];
|
||||||
|
List<int> tableIds = [];
|
||||||
|
bool found1 = false, found2 = false;
|
||||||
|
using (var command = new SqliteCommand("SELECT name FROM sqlite_master WHERE type='table';", connection))
|
||||||
|
using (var reader = command.ExecuteReader())
|
||||||
|
{
|
||||||
|
int id = 0;
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
string tableName = reader.GetString(0);
|
||||||
|
if (tableName.StartsWith("イベント"))
|
||||||
|
{
|
||||||
|
foundTN[0] = tableName;
|
||||||
|
found1 = true;
|
||||||
|
}
|
||||||
|
else if (tableName.StartsWith("立ち"))
|
||||||
|
{
|
||||||
|
foundTN[1] = tableName;
|
||||||
|
found2 = true;
|
||||||
|
}
|
||||||
|
tableNames.Add(tableName);
|
||||||
|
tableIds.Add(id++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(found1 && found2))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < tableNames.Count; i++)
|
||||||
|
Console.WriteLine($"{tableIds[i]}: {tableNames[i]}");
|
||||||
|
if (!found1)
|
||||||
|
{
|
||||||
|
Console.WriteLine("自动识别失败,请选择存放CG信息的数据表ID: ");
|
||||||
|
string? input = Console.ReadLine();
|
||||||
|
if (int.TryParse(input, out int userInputId))
|
||||||
|
{
|
||||||
|
if (userInputId >= 0 && userInputId < tableIds.Count)
|
||||||
|
{
|
||||||
|
foundTN[0] = tableNames[userInputId];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Invalid ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Invalid input. Please enter a valid number.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found2)
|
||||||
|
{
|
||||||
|
Console.WriteLine("自动识别失败,请选择存放立绘信息的数据表ID: ");
|
||||||
|
string? input = Console.ReadLine();
|
||||||
|
if (int.TryParse(input, out int userInputId))
|
||||||
|
{
|
||||||
|
if (userInputId >= 0 && userInputId < tableIds.Count)
|
||||||
|
{
|
||||||
|
foundTN[1] = tableNames[userInputId];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Invalid ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Invalid input. Please enter a valid number.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
List<EvTable> evts = [];
|
||||||
|
List<StTable> stts = [];
|
||||||
|
using (var command = new SqliteCommand($"SELECT * FROM {foundTN[0]};", connection))
|
||||||
|
{
|
||||||
|
using var reader = command.ExecuteReader();
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.IsDBNull(0) || string.IsNullOrEmpty(reader.GetString(0)))
|
||||||
|
continue;
|
||||||
|
evts.Add(new EvTable
|
||||||
|
{
|
||||||
|
name = reader.GetString(0),
|
||||||
|
file = reader.GetString(1),
|
||||||
|
option = reader.GetString(2).Split(' '),
|
||||||
|
coverd = (uint)reader.GetInt32(3),
|
||||||
|
filter = (uint)reader.GetInt32(4),
|
||||||
|
color = (uint)reader.GetInt32(5),
|
||||||
|
id = (uint)reader.GetInt32(6),
|
||||||
|
loc = (uint)reader.GetInt32(7),
|
||||||
|
order = reader.GetInt32(8),
|
||||||
|
link = (uint)reader.GetInt32(9)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
using (var command = new SqliteCommand($"SELECT * FROM {foundTN[1]};", connection))
|
||||||
|
{
|
||||||
|
using var reader = command.ExecuteReader();
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.IsDBNull(0) || string.IsNullOrEmpty(reader.GetString(0)))
|
||||||
|
continue;
|
||||||
|
stts.Add(new StTable
|
||||||
|
{
|
||||||
|
name = reader.GetString(0),
|
||||||
|
file = reader.GetString(1),
|
||||||
|
option = reader.GetString(2).Split(' '),
|
||||||
|
coverd = (uint)reader.GetInt32(3),
|
||||||
|
filter = (uint)reader.GetInt32(4),
|
||||||
|
color = (uint)reader.GetInt32(5),
|
||||||
|
id = (uint)reader.GetInt32(6),
|
||||||
|
loc = (uint)reader.GetInt32(7),
|
||||||
|
order = reader.GetInt32(8),
|
||||||
|
link = (uint)reader.GetInt32(9)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string[] files = Directory.GetFiles(args[0], "*.lsf", SearchOption.AllDirectories);
|
string[] files = Directory.GetFiles(args[0], "*.lsf", SearchOption.AllDirectories);
|
||||||
ImageManager im = new();
|
LsfManager lm = new();
|
||||||
foreach (string file in files)
|
foreach (string file in files)
|
||||||
{
|
{
|
||||||
if (im.LoadLsf(file))
|
if (lm.LoadLsf(file, true))
|
||||||
Console.WriteLine($"Load {file} Success");
|
Console.WriteLine($"Load {file} Success");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Load {file} Failed");
|
Console.WriteLine($"Load {file} Failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Console.WriteLine("OK");
|
connection.Close();
|
||||||
|
string outputDir = Path.Combine(Path.GetDirectoryName(args[0]), "Output");
|
||||||
|
if (!Directory.Exists(outputDir))
|
||||||
|
Directory.CreateDirectory(outputDir);
|
||||||
|
var parallelOptions = new ParallelOptions
|
||||||
|
{
|
||||||
|
MaxDegreeOfParallelism = 6 // 设置最大并行线程数
|
||||||
|
};
|
||||||
|
Parallel.ForEach(evts, parallelOptions, evt =>
|
||||||
|
//foreach (EvTable evt in evts)
|
||||||
|
{
|
||||||
|
if (evt.order == 0) //仅提取鉴赏中有的CG
|
||||||
|
return;
|
||||||
|
//continue;
|
||||||
|
string targetFilename = Path.Combine(outputDir, evt.name + ".png"); //最后保存可用的文件名(这里还没有后缀)
|
||||||
|
LsfData lsfData = lm.FindLsfDataByName(evt.file) ?? throw new Exception("Something Wrong");
|
||||||
|
List<int> pendingList = [];
|
||||||
|
foreach (string o in evt.option)
|
||||||
|
{
|
||||||
|
int t = TableManagercs.ParseOptions(lsfData, o);
|
||||||
|
if (t == -1)
|
||||||
|
continue;
|
||||||
|
pendingList.Add(t);
|
||||||
|
}
|
||||||
|
if (pendingList[0] != 0)
|
||||||
|
pendingList.Insert(0, 0);
|
||||||
|
if (!ImageManager.EvProcess(lsfData, [.. pendingList], targetFilename))
|
||||||
|
throw new Exception("Process Fail");
|
||||||
|
else
|
||||||
|
Console.WriteLine($"Export {evt.name} Success");
|
||||||
|
});
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//// 批量读取lsf文件
|
||||||
|
//if (Directory.Exists(args[0]))
|
||||||
|
//{
|
||||||
|
// string[] files = Directory.GetFiles(args[0], "*.lsf", SearchOption.AllDirectories);
|
||||||
|
// LsfManager lm = new();
|
||||||
|
// foreach (string file in files)
|
||||||
|
// {
|
||||||
|
// if (lm.LoadLsf(file, true))
|
||||||
|
// Console.WriteLine($"Load {file} Success");
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// Console.WriteLine($"Load {file} Failed");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// Console.WriteLine("OK");
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -220,7 +402,7 @@
|
|||||||
// Console.WriteLine(" -h Display help info");
|
// Console.WriteLine(" -h Display help info");
|
||||||
//}
|
//}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"profiles": {
|
"profiles": {
|
||||||
"EscudeTools": {
|
"EscudeTools": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"commandLineArgs": "G:\\x221.local\\lab2"
|
"commandLineArgs": "G:\\x221.local\\lab2\\ev\\1\r\n\"G:\\x221.local\\lab2\\db_graphics.db\""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
84
EscudeTools/TableManagercs.cs
Normal file
84
EscudeTools/TableManagercs.cs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace EscudeTools
|
||||||
|
{
|
||||||
|
public class EvTable
|
||||||
|
{
|
||||||
|
public string name;
|
||||||
|
public string file;
|
||||||
|
public string[] option;
|
||||||
|
public uint coverd;
|
||||||
|
public uint filter;
|
||||||
|
public uint color;
|
||||||
|
public uint id;
|
||||||
|
public uint loc;
|
||||||
|
public int order;
|
||||||
|
public uint link;
|
||||||
|
}
|
||||||
|
public class StTable
|
||||||
|
{
|
||||||
|
public string name;
|
||||||
|
public string file;
|
||||||
|
public string[] option;
|
||||||
|
public uint coverd;
|
||||||
|
public uint filter;
|
||||||
|
public uint color;
|
||||||
|
public uint id;
|
||||||
|
public uint loc;
|
||||||
|
public int order;
|
||||||
|
public uint link;
|
||||||
|
}
|
||||||
|
public class TableManagercs
|
||||||
|
{
|
||||||
|
public static int ParseOptions(LsfData ld, string input)
|
||||||
|
{
|
||||||
|
// 正则表达式匹配 p*:* 格式
|
||||||
|
Regex regex = new(@"p(\d+):(\d+)");
|
||||||
|
MatchCollection matches = regex.Matches(input);
|
||||||
|
|
||||||
|
List<int> results = [];
|
||||||
|
|
||||||
|
foreach (Match match in matches)
|
||||||
|
{
|
||||||
|
if (match.Groups.Count == 3)
|
||||||
|
{
|
||||||
|
if (int.TryParse(match.Groups[1].Value, out int firstNumber) &&
|
||||||
|
int.TryParse(match.Groups[2].Value, out int secondNumber))
|
||||||
|
{
|
||||||
|
results.Add(firstNumber);//index
|
||||||
|
results.Add(secondNumber);//state //可能要+1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < ld.lli.Length; i++)
|
||||||
|
{
|
||||||
|
if (ld.lli[i].index == 0 && ld.lli[i].state == 0)
|
||||||
|
{
|
||||||
|
if (ld.lli[i].index == results[0] && ld.lli[i].state + 1 == results[1])
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
if (ld.lli[i].index == results[0] && ld.lli[i].state == results[1])
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
//一般可以忽略这个警告
|
||||||
|
Console.WriteLine($"[WARN] Found invalid index:state data {results[0]}:{results[1]} in {ld.lsfName}, may be a bug?");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ParseCharactor(string input)
|
||||||
|
{
|
||||||
|
// 使用正则表达式匹配格式 * _ *
|
||||||
|
Regex regex = new(@"^(\d+)_.*$");
|
||||||
|
var match = regex.Match(input);
|
||||||
|
|
||||||
|
if (match.Success && match.Groups.Count > 1)
|
||||||
|
{
|
||||||
|
if (int.TryParse(match.Groups[1].Value, out int result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
example/2024-10-22_174116.png
Normal file
BIN
example/2024-10-22_174116.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 504 KiB |
Loading…
Reference in New Issue
Block a user