- [Core] Added new entries.
This commit is contained in:
@@ -3,10 +3,9 @@ using System;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -111,7 +110,6 @@ namespace AssetStudio
|
||||
|
||||
public List<StreamFile> fileList;
|
||||
|
||||
|
||||
private bool HasUncompressedDataHash = true;
|
||||
private bool HasBlockInfoNeedPaddingAtStart = true;
|
||||
|
||||
@@ -530,7 +528,15 @@ namespace AssetStudio
|
||||
}
|
||||
case CompressionType.Lzma: //LZMA
|
||||
{
|
||||
SevenZipHelper.StreamDecompress(reader.BaseStream, blocksStream, blockInfo.compressedSize, blockInfo.uncompressedSize);
|
||||
var compressedStream = reader.BaseStream;
|
||||
if (Game.Type.IsNetEase() && i == 0)
|
||||
{
|
||||
var compressedBytesSpan = reader.ReadBytes((int)blockInfo.compressedSize).AsSpan();
|
||||
NetEaseUtils.DecryptWithoutHeader(compressedBytesSpan);
|
||||
var ms = new MemoryStream(compressedBytesSpan.ToArray());
|
||||
compressedStream = ms;
|
||||
}
|
||||
SevenZipHelper.StreamDecompress(compressedStream, blocksStream, blockInfo.compressedSize, blockInfo.uncompressedSize);
|
||||
break;
|
||||
}
|
||||
case CompressionType.Lz4: //LZ4
|
||||
@@ -553,7 +559,7 @@ namespace AssetStudio
|
||||
}
|
||||
if (Game.Type.IsNetEase() && i == 0)
|
||||
{
|
||||
NetEaseUtils.Decrypt(compressedBytesSpan);
|
||||
NetEaseUtils.DecryptWithHeader(compressedBytesSpan);
|
||||
}
|
||||
if (Game.Type.IsArknightsEndfield() && i == 0)
|
||||
{
|
||||
|
||||
@@ -9,29 +9,38 @@ namespace AssetStudio
|
||||
public static class NetEaseUtils
|
||||
{
|
||||
private static readonly byte[] Signature = new byte[] { 0xEE, 0xDD };
|
||||
public static void Decrypt(Span<byte> bytes)
|
||||
public static void DecryptWithHeader(Span<byte> bytes)
|
||||
{
|
||||
var (encryptedOffset, encryptedSize) = ReadHeader(bytes);
|
||||
var encrypted = bytes.Slice(encryptedOffset, encryptedSize);
|
||||
Decrypt(encrypted);
|
||||
}
|
||||
public static void DecryptWithoutHeader(Span<byte> bytes)
|
||||
{
|
||||
var encrypted = bytes[..Math.Min(bytes.Length, 0x1000)];
|
||||
Decrypt(encrypted);
|
||||
}
|
||||
private static void Decrypt(Span<byte> bytes)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt block with NetEase encryption...");
|
||||
|
||||
var (encryptedOffset, encryptedSize) = ReadHeader(bytes);
|
||||
var encrypted = bytes.Slice(encryptedOffset, encryptedSize);
|
||||
var encryptedInts = MemoryMarshal.Cast<byte, int>(encrypted);
|
||||
var encryptedInts = MemoryMarshal.Cast<byte, int>(bytes);
|
||||
|
||||
var seedInts = new int[] { encryptedInts[3], encryptedInts[1], encryptedInts[4], encrypted.Length, encryptedInts[2] };
|
||||
var seedInts = new int[] { encryptedInts[3], encryptedInts[1], encryptedInts[4], bytes.Length, encryptedInts[2] };
|
||||
var seedBytes = MemoryMarshal.AsBytes<int>(seedInts).ToArray();
|
||||
var seed = (int)CRC.CalculateDigest(seedBytes, 0, (uint)seedBytes.Length);
|
||||
|
||||
var keyPart0 = seed ^ (encryptedInts[7] + 0x1981);
|
||||
var keyPart1 = seed ^ (encrypted.Length + 0x2013);
|
||||
var keyPart1 = seed ^ (bytes.Length + 0x2013);
|
||||
var keyPart2 = seed ^ (encryptedInts[5] + 0x1985);
|
||||
var keyPart3 = seed ^ (encryptedInts[6] + 0x2018);
|
||||
|
||||
for (int i = 0; i < 0x20; i++)
|
||||
{
|
||||
encrypted[i] ^= 0xA6;
|
||||
bytes[i] ^= 0xA6;
|
||||
}
|
||||
|
||||
var block = encrypted[0x20..];
|
||||
var block = bytes[0x20..];
|
||||
var keyVector = new int[] { keyPart2, keyPart0, keyPart1, keyPart3 };
|
||||
var keysVector = new int[] { 0x571, keyPart3, 0x892, 0x750, keyPart2, keyPart0, 0x746, keyPart1, 0x568 };
|
||||
if (block.Length >= 0x80)
|
||||
@@ -65,7 +74,7 @@ namespace AssetStudio
|
||||
var remainingCount = dataBlock.Length % 0x80;
|
||||
if (remainingCount > 0)
|
||||
{
|
||||
var remaining = encrypted[^remainingCount..];
|
||||
var remaining = bytes[^remainingCount..];
|
||||
for (int i = 0; i < remainingCount; i++)
|
||||
{
|
||||
remaining[i] ^= (byte)(keyBlock[i] ^ ((uint)keysVector[(uint)keyVector[i % keyVector.Length] % keysVector.Length] % 0xFF) ^ i);
|
||||
|
||||
@@ -212,6 +212,9 @@ namespace AssetStudio
|
||||
case GameType.Reverse1999:
|
||||
reader = DecryptReverse1999(reader);
|
||||
break;
|
||||
case GameType.JJKPhantomParade:
|
||||
reader = DecryptReverse1999(reader);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (reader.FileType == FileType.BundleFile && game.Type.IsBlockFile() || reader.FileType == FileType.ENCRFile || reader.FileType == FileType.BlbFile)
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace AssetStudio
|
||||
Games.Add(index++, new Game(GameType.GirlsFrontline));
|
||||
Games.Add(index++, new Game(GameType.Reverse1999));
|
||||
Games.Add(index++, new Game(GameType.ArknightsEndfield));
|
||||
Games.Add(index++, new Game(GameType.JJKPhantomParade));
|
||||
}
|
||||
public static Game GetGame(GameType gameType) => GetGame((int)gameType);
|
||||
public static Game GetGame(int index)
|
||||
@@ -156,7 +157,9 @@ namespace AssetStudio
|
||||
CodenameJump,
|
||||
GirlsFrontline,
|
||||
Reverse1999,
|
||||
ArknightsEndfield
|
||||
ArknightsEndfield,
|
||||
JJKPhantomParade,
|
||||
|
||||
}
|
||||
|
||||
public static class GameTypes
|
||||
|
||||
@@ -950,5 +950,71 @@ namespace AssetStudio
|
||||
return (byte)(key + (byte)(2 * ((key & 1) + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
public static FileReader DecryptJJKPhantomParade(FileReader reader)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Jujutsu Kaisen: Phantom Parade encryption");
|
||||
|
||||
var key = reader.ReadBytes(2);
|
||||
var signatureBytes = reader.ReadBytes(13);
|
||||
var generation = reader.ReadByte();
|
||||
|
||||
for (int i = 0; i < 13; i++)
|
||||
{
|
||||
signatureBytes[i] ^= key[i % key.Length];
|
||||
}
|
||||
|
||||
var signature = Encoding.UTF8.GetString(signatureBytes);
|
||||
if (signature != "_GhostAssets_")
|
||||
{
|
||||
throw new Exception("Invalid signature");
|
||||
}
|
||||
|
||||
generation ^= (byte)(key[0] ^ key[1]);
|
||||
|
||||
if (generation != 1)
|
||||
{
|
||||
throw new Exception("Invalid generation");
|
||||
}
|
||||
|
||||
|
||||
long value = 0;
|
||||
var data = reader.ReadBytes((int)reader.Remaining);
|
||||
var blockCount = data.Length / 0x10;
|
||||
|
||||
using var writerMS = new MemoryStream();
|
||||
using var writer = new BinaryWriter(writerMS);
|
||||
for (int i = 0; i <= blockCount; i++)
|
||||
{
|
||||
if (i % 0x40 == 0)
|
||||
{
|
||||
value = 0x64 * ((i / 0x40) + 1);
|
||||
}
|
||||
writer.Write(value);
|
||||
writer.Write((long)0);
|
||||
value += 1;
|
||||
}
|
||||
|
||||
using var aes = Aes.Create();
|
||||
aes.Key = new byte[] { 0x36, 0x31, 0x35, 0x34, 0x65, 0x30, 0x30, 0x66, 0x39, 0x45, 0x39, 0x63, 0x65, 0x34, 0x36, 0x64, 0x63, 0x39, 0x30, 0x35, 0x34, 0x45, 0x30, 0x37, 0x31, 0x37, 0x33, 0x41, 0x61, 0x35, 0x34, 0x36 };
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Padding = PaddingMode.None;
|
||||
var encryptor = aes.CreateEncryptor();
|
||||
|
||||
var keyBytes = writerMS.ToArray();
|
||||
keyBytes = encryptor.TransformFinalBlock(keyBytes, 0, keyBytes.Length);
|
||||
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
data[i] ^= keyBytes[i];
|
||||
}
|
||||
|
||||
Logger.Verbose("Decrypted Jujutsu Kaisen: Phantom Parade file successfully !!");
|
||||
|
||||
MemoryStream ms = new();
|
||||
ms.Write(data);
|
||||
ms.Position = 0;
|
||||
return new FileReader(reader.FullPath, ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user