New game entries.
This commit is contained in:
@@ -156,7 +156,7 @@ namespace AssetStudio
|
||||
return header;
|
||||
}
|
||||
|
||||
private void ReadHeaderAndBlocksInfo(EndianBinaryReader reader)
|
||||
private void ReadHeaderAndBlocksInfo(FileReader reader)
|
||||
{
|
||||
if (m_Header.version >= 4)
|
||||
{
|
||||
@@ -208,7 +208,7 @@ namespace AssetStudio
|
||||
return blocksStream;
|
||||
}
|
||||
|
||||
private void ReadBlocksAndDirectory(EndianBinaryReader reader, Stream blocksStream)
|
||||
private void ReadBlocksAndDirectory(FileReader reader, Stream blocksStream)
|
||||
{
|
||||
var isCompressed = m_Header.signature == "UnityWeb";
|
||||
foreach (var blockInfo in m_BlocksInfo)
|
||||
@@ -278,7 +278,7 @@ namespace AssetStudio
|
||||
XORShift128.Init = false;
|
||||
}
|
||||
|
||||
private void ReadHeader(EndianBinaryReader reader)
|
||||
private void ReadHeader(FileReader reader)
|
||||
{
|
||||
if (Game.Type.IsBH3() && XORShift128.Init)
|
||||
{
|
||||
@@ -309,7 +309,7 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadBlocksInfoAndDirectory(EndianBinaryReader reader)
|
||||
private void ReadBlocksInfoAndDirectory(FileReader reader)
|
||||
{
|
||||
if (Game.Type.IsCNUnity())
|
||||
{
|
||||
@@ -430,7 +430,7 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadBlocks(EndianBinaryReader reader, Stream blocksStream)
|
||||
private void ReadBlocks(FileReader reader, Stream blocksStream)
|
||||
{
|
||||
for (int i = 0; i < m_BlocksInfo.Length; i++)
|
||||
{
|
||||
@@ -464,6 +464,10 @@ namespace AssetStudio
|
||||
{
|
||||
CNUnity.DecryptBlock(compressedBytesSpan, compressedSize, i);
|
||||
}
|
||||
if (Game.Type.IsOPFP())
|
||||
{
|
||||
OPFPUtils.Decrypt(compressedBytesSpan, reader.FullPath);
|
||||
}
|
||||
var uncompressedSize = (int)blockInfo.uncompressedSize;
|
||||
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
|
||||
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
|
||||
|
||||
40
AssetStudio/Crypto/OPFPUtils.cs
Normal file
40
AssetStudio/Crypto/OPFPUtils.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
public static class OPFPUtils
|
||||
{
|
||||
public static readonly string[] EncrytpedFolders = { "UI/", "Atlas/", "UITexture/", "DynamicAtlas/" };
|
||||
|
||||
public static void Decrypt(Span<byte> data, string path)
|
||||
{
|
||||
if (IsEncryptionBundle(path, out var key))
|
||||
{
|
||||
data[0] ^= key;
|
||||
for (int i = 1; i < data.Length; i++)
|
||||
{
|
||||
data[i] ^= data[i - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
private static bool IsEncryptionBundle(string path, out byte key)
|
||||
{
|
||||
path = path.Replace("\\", "/");
|
||||
foreach(var encryptedFolder in EncrytpedFolders)
|
||||
{
|
||||
var index = path.IndexOf(encryptedFolder, 0, path.Length, StringComparison.OrdinalIgnoreCase);
|
||||
if (index != -1)
|
||||
{
|
||||
var assetPath = path[index..];
|
||||
if (assetPath.StartsWith(encryptedFolder, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
key = (byte)assetPath.Length;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
key = 0x00;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,6 +148,12 @@ namespace AssetStudio
|
||||
case GameType.OPFP:
|
||||
reader = ParseOPFP(reader);
|
||||
break;
|
||||
case GameType.AlchemyStars:
|
||||
reader = ParseAlchemyStars(reader);
|
||||
break;
|
||||
case GameType.FantasyOfWind:
|
||||
reader = DecryptFantasyOfWind(reader);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (reader.FileType == FileType.BundleFile && game.Type.IsBlockFile())
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace AssetStudio
|
||||
Games.Add(index++, new Game(GameType.Naraka));
|
||||
Games.Add(index++, new Game(GameType.EnsembleStars));
|
||||
Games.Add(index++, new Game(GameType.OPFP));
|
||||
Games.Add(index++, new Game(GameType.AlchemyStars));
|
||||
Games.Add(index++, new Game(GameType.FantasyOfWind));
|
||||
}
|
||||
public static Game GetGame(int index)
|
||||
{
|
||||
@@ -123,7 +125,9 @@ namespace AssetStudio
|
||||
Naraka,
|
||||
CNUnity,
|
||||
EnsembleStars,
|
||||
OPFP
|
||||
OPFP,
|
||||
AlchemyStars,
|
||||
FantasyOfWind
|
||||
}
|
||||
|
||||
public static class GameTypes
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using static AssetStudio.BundleFile;
|
||||
using static AssetStudio.Crypto;
|
||||
|
||||
@@ -273,16 +274,91 @@ namespace AssetStudio
|
||||
|
||||
public static FileReader ParseOPFP(FileReader reader)
|
||||
{
|
||||
MemoryStream ms = new();
|
||||
|
||||
var data = reader.ReadBytes((int)reader.BaseStream.Length);
|
||||
var stream = reader.BaseStream;
|
||||
var data = reader.ReadBytes(0x1000);
|
||||
var idx = data.Search("UnityFS");
|
||||
if (idx != -1)
|
||||
{
|
||||
var count = data.Length - idx;
|
||||
ms = new(data, idx, count);
|
||||
stream = new BlockStream(stream, idx);
|
||||
}
|
||||
|
||||
return new FileReader(reader.FullPath, stream);
|
||||
}
|
||||
|
||||
public static FileReader ParseAlchemyStars(FileReader reader)
|
||||
{
|
||||
var stream = reader.BaseStream;
|
||||
var data = reader.ReadBytes(0x1000);
|
||||
var idx = data.Search("UnityFS");
|
||||
if (idx != -1)
|
||||
{
|
||||
var idx2 = data[(idx + 1)..].Search("UnityFS");
|
||||
if (idx2 != -1)
|
||||
{
|
||||
stream = new BlockStream(stream, idx + idx2 + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream = new BlockStream(stream, idx);
|
||||
}
|
||||
}
|
||||
|
||||
return new FileReader(reader.FullPath, stream);
|
||||
}
|
||||
|
||||
public static FileReader DecryptFantasyOfWind(FileReader reader)
|
||||
{
|
||||
byte[] encryptKeyName = Encoding.UTF8.GetBytes("28856");
|
||||
const int MinLength = 0xC8;
|
||||
const int KeyLength = 8;
|
||||
const int EnLength = 0x32;
|
||||
const int StartEnd = 0x14;
|
||||
const int HeadLength = 5;
|
||||
|
||||
var signature = reader.ReadStringToNull(HeadLength);
|
||||
if (string.Compare(signature, "K9999") > 0 || reader.Length <= MinLength)
|
||||
{
|
||||
reader.Position = 0;
|
||||
return reader;
|
||||
}
|
||||
|
||||
reader.Position = reader.Length + ~StartEnd;
|
||||
var keyLength = reader.ReadByte();
|
||||
reader.Position = reader.Length - StartEnd - 2;
|
||||
var enLength = reader.ReadByte();
|
||||
|
||||
var enKeyPos = (byte)((keyLength % KeyLength) + KeyLength);
|
||||
var encryptedLength = (byte)((enLength % EnLength) + EnLength);
|
||||
|
||||
reader.Position = reader.Length - StartEnd - enKeyPos;
|
||||
var encryptKey = reader.ReadBytes(KeyLength);
|
||||
|
||||
var subByte = (byte)(reader.Length - StartEnd - KeyLength - (keyLength % KeyLength));
|
||||
for (var i = 0; i < KeyLength; i++)
|
||||
{
|
||||
if (encryptKey[i] == 0)
|
||||
{
|
||||
encryptKey[i] = (byte)(subByte + i);
|
||||
}
|
||||
}
|
||||
|
||||
var key = new byte[encryptKeyName.Length + KeyLength];
|
||||
encryptKeyName.CopyTo(key.AsMemory(0));
|
||||
encryptKey.CopyTo(key.AsMemory(encryptKeyName.Length));
|
||||
|
||||
reader.Position = HeadLength;
|
||||
var data = reader.ReadBytes(encryptedLength);
|
||||
for (int i = 0; i < encryptedLength; i++)
|
||||
{
|
||||
data[i] ^= key[i % key.Length];
|
||||
}
|
||||
|
||||
MemoryStream ms = new();
|
||||
ms.Write(Encoding.UTF8.GetBytes("Unity"));
|
||||
ms.Write(data);
|
||||
reader.BaseStream.CopyTo(ms);
|
||||
ms.Position = 0;
|
||||
|
||||
return new FileReader(reader.FullPath, ms);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user