Skip to content

Commit

Permalink
Merge pull request #1 from Crequency/dev=main
Browse files Browse the repository at this point in the history
[Pull Request] Crequency/KitX#278
  • Loading branch information
Dynesshely committed Feb 25, 2024
2 parents 1bdf927 + 91f4e9e commit bdfba3d
Show file tree
Hide file tree
Showing 53 changed files with 3,508 additions and 18 deletions.
797 changes: 797 additions & 0 deletions .gitignore

Large diffs are not rendered by default.

12 changes: 0 additions & 12 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
[submodule "KitX Contracts"]
path = KitX Contracts
url = git@github.com:Crequency/KitX-Contracts.git
[submodule "KitX File Formats"]
path = KitX File Formats
url = git@github.com:Crequency/KitX-File-Formats.git
[submodule "KitX Loaders"]
path = KitX Loaders
url = git@github.com:Crequency/KitX-Loaders.git
[submodule "KitX Plugins"]
path = KitX Plugins
url = git@github.com:Crequency/KitX-Plugins.git
[submodule "KitX Rules"]
path = KitX Rules
url = git@github.com:Crequency/KitX-Rules.git
[submodule "KitX Script"]
path = KitX Script
url = git@github.com:Crequency/KitX-Script.git
2 changes: 1 addition & 1 deletion KitX Contracts
1 change: 0 additions & 1 deletion KitX File Formats
Submodule KitX File Formats deleted from 50b818
1 change: 0 additions & 1 deletion KitX Loaders
Submodule KitX Loaders deleted from 49dbb6
1 change: 0 additions & 1 deletion KitX Plugins
Submodule KitX Plugins deleted from bbb51b
1 change: 0 additions & 1 deletion KitX Rules
Submodule KitX Rules deleted from 646eb4
2 changes: 1 addition & 1 deletion KitX Script
220 changes: 220 additions & 0 deletions KitX.FileFormats/ExtensionsPackage/Decoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace KitX.FileFormats.ExtensionsPackage;

public class Decoder
{
public Decoder(string packagePath, Options? options = null)
{
PackagePath = packagePath;

Options = options ?? new();
}

public string PackagePath { get; set; }

public Options Options { get; set; }

internal struct FileMapItem
{
internal long FileNamePathLength;

internal long FileBodyLength;
}

public Tuple<string, string> GetLoaderAndPluginInfo()
{
var fs = new FileStream(PackagePath, FileMode.Open, FileAccess.Read);
var reader = new BinaryReader(fs);

var header = reader.ReadBytes(16);

if (!Header.IsKXP(ref header))
throw new InvalidDataException("It's not a KXP Package.");

_ = reader.ReadBytes(16);

long loaderStructLength, pluginStructLength;

loaderStructLength = BitConverter.ToInt64(reader.ReadBytes(8), 0);

var loaderStructString = Encoding.UTF8.GetString(reader.ReadBytes((int)loaderStructLength));

pluginStructLength = BitConverter.ToInt64(reader.ReadBytes(8), 0);

var pluginStructString = Encoding.UTF8.GetString(reader.ReadBytes((int)pluginStructLength));

var result = new Tuple<string, string>(loaderStructString, pluginStructString);

reader.Close();
fs.Close();

return result;
}

public Tuple<string, string> Decode(string releaseFolder)
{
if (!Directory.Exists(releaseFolder))
_ = Directory.CreateDirectory(releaseFolder);

var src = File.ReadAllBytes(PackagePath);

#region 获取文件表头, 验证文件是否为 KXP 文件 (0 - 15)

var header = new byte[16]; // 文件标头

for (int i = 0; i < 16; ++i) header[i] = src[i];

if (!Header.IsKXP(ref header))
throw new Exception("It's not a KXP Package.");

#endregion

#region 获取哈希校验值 (16 - 31)

var hash = new byte[16]; // 哈希部分

var cursor = 16; // 文件流指针

for (; cursor < 32; ++cursor) // 取出哈希部分的字节
hash[cursor - 16] = src[cursor];

if (Options.Verbose)
Console.WriteLine($"Hash Code: {Encoding.UTF8.GetString(hash)}");

#endregion

#region 获取 LoaderStruct 部分

var loaderStructLengthByte = new byte[8];

for (int i = 0; i < 8; ++i, ++cursor) loaderStructLengthByte[i] = src[cursor];

var loaderStructLength = BitConverter.ToInt64(loaderStructLengthByte, 0);

if (Options.Verbose)
Console.WriteLine($"Loader Struct Length: {loaderStructLength}");

var loaderStructByte = new byte[loaderStructLength]; // Loader Struct 的 Byte 数组

for (int i = 0; i < loaderStructLength; ++i, ++cursor) loaderStructByte[i] = src[cursor];

#endregion

#region 获取 PluginStruct 部分

var pluginStructLengthByte = new byte[8];

for (int i = 0; i < 8; ++i, ++cursor) pluginStructLengthByte[i] = src[cursor];

var pluginStructLength = BitConverter.ToInt64(pluginStructLengthByte, 0);

if (Options.Verbose)
Console.WriteLine($"Plugin Struct Length: {pluginStructLength}");

var pluginStructByte = new byte[pluginStructLength]; // Plugin Struct 的 Byte 数组

for (int i = 0; i < pluginStructLength; ++i, ++cursor) pluginStructByte[i] = src[cursor];

#endregion

#region 获取 LoaderStruct 和 PluginStruct 字符串

var loaderStruct = Encoding.UTF8.GetString(loaderStructByte);
var pluginStruct = Encoding.UTF8.GetString(pluginStructByte);

if (Options.Verbose)
{
Console.WriteLine($"Loader Struct: {loaderStruct}");
Console.WriteLine($"Plugin Struct: {pluginStruct}");
}

#endregion

#region 获取文件地图

var fileMapLengthByte = new byte[8]; // 文件地图长度

for (int i = 0; i < 8; ++i, ++cursor) fileMapLengthByte[i] = src[cursor];

var fileMapLength = BitConverter.ToInt64(fileMapLengthByte, 0);

if (Options.Verbose)
Console.WriteLine($"File Map Length: {fileMapLength}");

var FileMap = new List<FileMapItem>(); // 文件地图

for (long i = 0; i < fileMapLength; ++i)
{
var fileNameLength = new byte[8]; // 文件名长度
var fileBodyLength = new byte[8]; // 文件体长度
for (int j = 0; j < 8; ++j, ++cursor) fileNameLength[j] = src[cursor];
for (int j = 0; j < 8; ++j, ++cursor) fileBodyLength[j] = src[cursor];

FileMap.Add(new FileMapItem()
{
FileNamePathLength = BitConverter.ToInt64(fileNameLength, 0),
FileBodyLength = BitConverter.ToInt64(fileBodyLength, 0)
});

if (Options.Verbose)
{
Console.WriteLine($"File Name Length: {BitConverter.ToInt64(fileNameLength, 0)}");
Console.WriteLine($"File Body Length: {BitConverter.ToInt64(fileBodyLength, 0)}");
}
}

#endregion

#region 获取源文件文件名与文件体并立即写回释放文件夹

foreach (var item in FileMap)
{
var fn = new byte[item.FileNamePathLength];
var fb = new byte[item.FileBodyLength];

for (int i = 0; i < item.FileNamePathLength; ++i, ++cursor)
fn[i] = src[cursor];

for (int i = 0; i < item.FileBodyLength; ++i, ++cursor)
fb[i] = src[cursor];

var fileName = Encoding.UTF8.GetString(fn);
var dirPath = Path.GetDirectoryName(
Path.GetFullPath($"{releaseFolder}/{fileName}")
);

if (!Directory.Exists(dirPath))
Directory.CreateDirectory(dirPath);

File.WriteAllBytes($"{releaseFolder}/{fileName}", fb);
}

#endregion

#region 计算哈希校验值

using var md5 = MD5.Create();

var localHash = md5.ComputeHash(src, 32, src.Length - 32);

if (Options.Verbose)
Console.WriteLine($"Local Hash Code: {Encoding.UTF8.GetString(localHash)}");

#endregion

#region 哈希校验不一样时抛出异常

for (var i = 0; i < 16; ++i)
if (!hash[i].Equals(localHash[i]))
throw new Exception("MD5 Hash Error.");

#endregion

return Tuple.Create(loaderStruct, pluginStruct);
}
}
Loading

0 comments on commit bdfba3d

Please sign in to comment.