Update new encryption scheme & drop support for .NET 6.0
Signed-off-by: Bayu Satiyo <itsyuukunz@gmail.com>
This commit is contained in:
parent
ea9a31d407
commit
deb757922c
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -10,7 +10,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"preLaunchTask": "build",
|
"preLaunchTask": "build",
|
||||||
// If you have changed target frameworks, make sure to update the program path.
|
// If you have changed target frameworks, make sure to update the program path.
|
||||||
"program": "${workspaceFolder}/bin/Debug/net6.0/LightvnTools.dll",
|
"program": "${workspaceFolder}/bin/Debug/net8.0/LightvnTools.dll",
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||||
|
@ -3,14 +3,14 @@ using ICSharpCode.SharpZipLib.Zip;
|
|||||||
|
|
||||||
namespace LightvnTools
|
namespace LightvnTools
|
||||||
{
|
{
|
||||||
public class Program
|
public class LightvnTools
|
||||||
{
|
{
|
||||||
static readonly string VERSION = "1.1.0";
|
static readonly string VERSION = "1.2.0";
|
||||||
|
|
||||||
// PKZip signature
|
// PKZip signature
|
||||||
static readonly byte[] PKZIP = { 0x50, 0x4B, 0x03, 0x04 };
|
static readonly byte[] PKZIP = { 0x50, 0x4B, 0x03, 0x04 };
|
||||||
|
|
||||||
// Key used to decrypt the file header and footer (reverse)
|
// Key used to XOR the file header and footer (reverse)
|
||||||
// Text: `d6c5fKI3GgBWpZF3Tz6ia3kF0`
|
// Text: `d6c5fKI3GgBWpZF3Tz6ia3kF0`
|
||||||
// Source: https://github.com/morkt/GARbro/issues/440
|
// Source: https://github.com/morkt/GARbro/issues/440
|
||||||
static readonly byte[] KEY = { 0x64, 0x36, 0x63, 0x35, 0x66, 0x4B, 0x49, 0x33, 0x47, 0x67, 0x42, 0x57, 0x70, 0x5A, 0x46, 0x33, 0x54, 0x7A, 0x36, 0x69, 0x61, 0x33, 0x6B, 0x46, 0x30 };
|
static readonly byte[] KEY = { 0x64, 0x36, 0x63, 0x35, 0x66, 0x4B, 0x49, 0x33, 0x47, 0x67, 0x42, 0x57, 0x70, 0x5A, 0x46, 0x33, 0x54, 0x7A, 0x36, 0x69, 0x61, 0x33, 0x6B, 0x46, 0x30 };
|
||||||
@ -23,26 +23,65 @@ namespace LightvnTools
|
|||||||
Console.WriteLine($"Light.vnTools v{VERSION}");
|
Console.WriteLine($"Light.vnTools v{VERSION}");
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
"Light.vnTools is an unpack and repacking tool for Light.vn game engine (lightvn.net)."
|
"Light.vnTools is an unpack and repacking tool for game made with Light.vn game engine (lightvn.net)."
|
||||||
);
|
);
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("Usage:");
|
Console.WriteLine("Usage:");
|
||||||
Console.WriteLine(" Unpack: Drag and drop '.vndat' file to 'LightvnTools.exe'");
|
Console.WriteLine(" Unpack: Drag and drop '.vndat' / '.mcdat' file(s) to 'LightvnTools.exe'");
|
||||||
Console.WriteLine(" Repack: Drag and drop unpacked folder to 'LightvnTools.exe'");
|
Console.WriteLine(" Repack: Drag and drop unpacked folder to 'LightvnTools.exe'");
|
||||||
Console.ReadLine();
|
Console.ReadKey();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string input = args[0];
|
|
||||||
string zipPassword = Encoding.UTF8.GetString(KEY);
|
string zipPassword = Encoding.UTF8.GetString(KEY);
|
||||||
|
|
||||||
if (File.Exists(input))
|
for (int i = 0; i < args.Length; i++)
|
||||||
Unpack(input, Path.GetFileNameWithoutExtension(input), zipPassword);
|
{
|
||||||
|
if (File.Exists(args[i]))
|
||||||
|
{
|
||||||
|
if (IsVndat(args[i]))
|
||||||
|
{
|
||||||
|
UnpackVndat(args[i], Path.GetFileNameWithoutExtension(args[i]), zipPassword);
|
||||||
|
}
|
||||||
|
else if (Path.GetExtension(args[i]).Contains("mcdat"))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Decrypting {args[i]}...");
|
||||||
|
XOR(args[i], $"{args[i]}.dec");
|
||||||
|
}
|
||||||
|
else if (Path.GetExtension(args[i]).Contains("dec"))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Encrypting {args[i]}...");
|
||||||
|
XOR(args[i], args[i].Replace("dec", "enc"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Unsupported file! {args[i]}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Directory.Exists(input))
|
if (Directory.Exists(args[i]))
|
||||||
Repack(input, zipPassword);
|
{
|
||||||
|
RepackVndat(args[i], zipPassword);
|
||||||
|
GetFilesRecursive(args[i]).ToList().ForEach(path =>
|
||||||
|
{
|
||||||
|
if (path.Contains("mcdat"))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Decrypting {path}...");
|
||||||
|
XOR(path, $"{path}.dec");
|
||||||
|
}
|
||||||
|
else if (path.Contains("dec"))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Encrypting {path}...");
|
||||||
|
XOR(path, path.Replace("dec", "enc"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Console.ReadLine();
|
Console.WriteLine("\nDone.");
|
||||||
|
Console.ReadKey();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -51,14 +90,8 @@ namespace LightvnTools
|
|||||||
/// <param name="vndatFile"></param>
|
/// <param name="vndatFile"></param>
|
||||||
/// <param name="outputFolder"></param>
|
/// <param name="outputFolder"></param>
|
||||||
/// <param name="password"></param>
|
/// <param name="password"></param>
|
||||||
static void Unpack(string vndatFile, string outputFolder, string? password = "")
|
static void UnpackVndat(string vndatFile, string outputFolder, string? password = "")
|
||||||
{
|
{
|
||||||
if (!IsVndat(vndatFile))
|
|
||||||
{
|
|
||||||
Console.WriteLine($"{Path.GetFileName(vndatFile)} isn\'t a `.vndat` (zip) file!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool usePassword = IsPasswordProtectedZip(vndatFile);
|
bool usePassword = IsPasswordProtectedZip(vndatFile);
|
||||||
|
|
||||||
using ZipFile zipFile = new(vndatFile);
|
using ZipFile zipFile = new(vndatFile);
|
||||||
@ -100,7 +133,7 @@ namespace LightvnTools
|
|||||||
Console.WriteLine("Done.");
|
Console.WriteLine("Done.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only XOR `.vndat` contents that's not password protected.
|
// Only XOR the `.vndat` contents if the archive is not password protected.
|
||||||
if (!usePassword)
|
if (!usePassword)
|
||||||
{
|
{
|
||||||
string[] files = GetFilesRecursive(outputFolder);
|
string[] files = GetFilesRecursive(outputFolder);
|
||||||
@ -109,8 +142,8 @@ namespace LightvnTools
|
|||||||
{
|
{
|
||||||
foreach (string file in files)
|
foreach (string file in files)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Decrypting {file}...");
|
Console.WriteLine($"XORing {file}...");
|
||||||
XorVndatContent(file);
|
XOR(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Done.");
|
Console.WriteLine("Done.");
|
||||||
@ -123,7 +156,7 @@ namespace LightvnTools
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sourceFolder"></param>
|
/// <param name="sourceFolder"></param>
|
||||||
/// <param name="password"></param>
|
/// <param name="password"></param>
|
||||||
static void Repack(string sourceFolder, string? password = "")
|
static void RepackVndat(string sourceFolder, string? password = "")
|
||||||
{
|
{
|
||||||
string outputFile = $"{Path.GetFileName(sourceFolder)}.vndat";
|
string outputFile = $"{Path.GetFileName(sourceFolder)}.vndat";
|
||||||
string[] files = GetFilesRecursive(sourceFolder);
|
string[] files = GetFilesRecursive(sourceFolder);
|
||||||
@ -158,7 +191,7 @@ namespace LightvnTools
|
|||||||
foreach (string file in files)
|
foreach (string file in files)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Encrypting {Path.GetRelativePath(sourceFolder, file)}...");
|
Console.WriteLine($"Encrypting {Path.GetRelativePath(sourceFolder, file)}...");
|
||||||
XorVndatContent(file);
|
XOR(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,34 +284,19 @@ namespace LightvnTools
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Encrypt (XOR) `.vndat` file content.
|
/// XOR <paramref name="buffer"/> data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filePath"></param>
|
/// <param name="buffer"></param>
|
||||||
static void XorVndatContent(string filePath)
|
/// <returns></returns>
|
||||||
|
static byte[] XOR(byte[] buffer)
|
||||||
{
|
{
|
||||||
try
|
if (buffer.Length < 100)
|
||||||
{
|
{
|
||||||
byte[] buffer;
|
if (buffer.Length <= 0)
|
||||||
int bufferLength;
|
return buffer;
|
||||||
|
|
||||||
using (FileStream inputStream = File.OpenRead(filePath))
|
|
||||||
{
|
|
||||||
buffer = new byte[bufferLength = (int)inputStream.Length];
|
|
||||||
inputStream.Read(buffer, 0, bufferLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bufferLength < 100)
|
|
||||||
{
|
|
||||||
if (bufferLength == 0)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Skipping {filePath}. File is empty.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine($"File size is smaller than 100 bytes: {filePath}");
|
|
||||||
|
|
||||||
// XOR entire bytes
|
// XOR entire bytes
|
||||||
for (int i = 0; i < bufferLength; i++)
|
for (int i = 0; i < buffer.Length; i++)
|
||||||
buffer[i] ^= REVERSED_KEY[i % KEY.Length];
|
buffer[i] ^= REVERSED_KEY[i % KEY.Length];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -289,10 +307,30 @@ namespace LightvnTools
|
|||||||
|
|
||||||
// XOR the last 100 bytes
|
// XOR the last 100 bytes
|
||||||
for (int i = 0; i < 99; i++)
|
for (int i = 0; i < 99; i++)
|
||||||
buffer[bufferLength - 99 + i] ^= REVERSED_KEY[i % KEY.Length];
|
buffer[buffer.Length - 99 + i] ^= REVERSED_KEY[i % KEY.Length];
|
||||||
}
|
}
|
||||||
|
|
||||||
using FileStream outputStream = File.OpenWrite(filePath);
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Do XOR operation on the <paramref name="filePath"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath"></param>
|
||||||
|
static void XOR(string filePath, string? outputFilePath = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] buffer;
|
||||||
|
int bufferLength;
|
||||||
|
|
||||||
|
using FileStream inputStream = File.OpenRead(filePath);
|
||||||
|
buffer = new byte[bufferLength = (int)inputStream.Length];
|
||||||
|
inputStream.Read(buffer, 0, bufferLength);
|
||||||
|
|
||||||
|
buffer = XOR(buffer);
|
||||||
|
|
||||||
|
using FileStream outputStream = File.OpenWrite(outputFilePath ?? filePath);
|
||||||
outputStream.Write(buffer, 0, bufferLength);
|
outputStream.Write(buffer, 0, bufferLength);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
22
README.md
22
README.md
@ -2,19 +2,27 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Light.vnTools is an unpack and repacking tool for game created with [Light.vn](https://lightvn.net "Light.vn") game engine.
|
Light.vnTools is an unpack/decrypt and repack/encrypt tool for game made with [Light.vn](https://lightvn.net "Light.vn") (Visual Novel) game engine.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- [.NET 6.0 SDK](https://dotnet.microsoft.com/download/dotnet/6.0 "Download .NET 6.0 SDK") or [.NET 7.0 SDK](https://dotnet.microsoft.com/download/dotnet/7.0 "Download .NET 7.0 SDK")
|
- [.NET 7.0 SDK](https://dotnet.microsoft.com/download/dotnet/7.0 "Download .NET 7.0 SDK") / [.NET 8.0 SDK](https://dotnet.microsoft.com/download/dotnet/8.0 "Download .NET 8.0 SDK")
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> The "new" encryption scheme that works on the newer Light.vn version is stripping the original file name,
|
||||||
|
> so I can't recover it.
|
||||||
|
>
|
||||||
|
> We're missing the original file extension, but we can guess it by reading the file header and set the extension based on that,
|
||||||
|
>
|
||||||
|
> For the time being you need to do it yourself by grabbing a hex editor program like [ImHex](https://github.com/WerWolv/ImHex "Visit ImHex GitHub repository"), find the **magic header**, do some Googling then rename the file.
|
||||||
|
|
||||||
- Download Light.vnTools at [Releases](https://github.com/kiraio-moe/Light.vnTools/releases "Light.vnTools Releases").
|
- Download Light.vnTools at [Releases](https://github.com/kiraio-moe/Light.vnTools/releases "Light.vnTools Releases").
|
||||||
- Unpack:
|
- Unpack/Decrypt:
|
||||||
Drag and drop `.vndat` file to `LightvnTools.exe`.
|
Drag and drop `.vndat` / `.mcdat` file(s) to the `LightvnTools.exe`.
|
||||||
- Repack:
|
- Repack/Encrypt:
|
||||||
Drag and drop **unpacked folder** to `LightvnTools.exe`.
|
Drag and drop **unpacked folder/file(s)** to the `LightvnTools.exe`.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
@ -28,4 +36,4 @@ For more information about the GNU General Public License version 3.0 (GNU GPL 3
|
|||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
This tool is intentionally created as a modding tool to translate the visual novel game created with Light.vn game engine. I didn't condone any piracy in any forms such as taking the game visual and sell it illegally which harm the game developer. Use the tool at your own risk (DWYOR - Do What You Own Risk).
|
This tool is intentionally created as a modding tool to translate the visual novel game created with Light.vn game engine. I didn't condone any piracy in any forms such as taking the game visual and sell it illegally which harm the developer. Use the tool at your own risk (DWYOR - Do What You Own Risk).
|
||||||
|
Loading…
Reference in New Issue
Block a user