Files
YarikStudio/AssetStudio/FileReader.cs
2023-06-10 20:19:18 +04:00

199 lines
7.1 KiB
C#

using System;
using System.IO;
using System.Linq;
using static AssetStudio.ImportHelper;
namespace AssetStudio
{
public class FileReader : EndianBinaryReader
{
public string FullPath;
public string FileName;
public FileType FileType;
private static readonly byte[] gzipMagic = { 0x1f, 0x8b };
private static readonly byte[] brotliMagic = { 0x62, 0x72, 0x6F, 0x74, 0x6C, 0x69 };
private static readonly byte[] zipMagic = { 0x50, 0x4B, 0x03, 0x04 };
private static readonly byte[] zipSpannedMagic = { 0x50, 0x4B, 0x07, 0x08 };
private static readonly byte[] mhy0Magic = { 0x6D, 0x68, 0x79, 0x30 };
private static readonly byte[] narakaMagic = { 0x15, 0x1E, 0x1C, 0x0D, 0x0D, 0x23, 0x21 };
private static readonly byte[] gunfireMagic = { 0x7C, 0x6D, 0x79, 0x72, 0x27, 0x7A, 0x73, 0x78, 0x3F };
public FileReader(string path) : this(path, File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { }
public FileReader(string path, Stream stream, bool leaveOpen = false) : base(stream, EndianType.BigEndian, leaveOpen)
{
FullPath = Path.GetFullPath(path);
FileName = Path.GetFileName(path);
FileType = CheckFileType();
}
private FileType CheckFileType()
{
var signature = this.ReadStringToNull(20);
Position = 0;
switch (signature)
{
case "ENCR":
case "UnityWeb":
case "UnityRaw":
case "UnityArchive":
case "UnityFS":
return FileType.BundleFile;
case "UnityWebData1.0":
return FileType.WebFile;
case "blk":
return FileType.BlkFile;
default:
{
byte[] magic = ReadBytes(2);
Position = 0;
if (gzipMagic.SequenceEqual(magic))
{
return FileType.GZipFile;
}
Position = 0x20;
magic = ReadBytes(6);
Position = 0;
if (brotliMagic.SequenceEqual(magic))
{
return FileType.BrotliFile;
}
if (IsSerializedFile())
{
return FileType.AssetsFile;
}
magic = ReadBytes(4);
Position = 0;
if (zipMagic.SequenceEqual(magic) || zipSpannedMagic.SequenceEqual(magic))
{
return FileType.ZipFile;
}
if (mhy0Magic.SequenceEqual(magic))
{
return FileType.Mhy0File;
}
magic = ReadBytes(7);
Position = 0;
if (narakaMagic.SequenceEqual(magic))
{
return FileType.BundleFile;
}
magic = ReadBytes(9);
Position = 0;
if (gunfireMagic.SequenceEqual(magic))
{
Position = 0x32;
return FileType.BundleFile;
}
return FileType.ResourceFile;
}
}
}
private bool IsSerializedFile()
{
var fileSize = BaseStream.Length;
if (fileSize < 20)
{
return false;
}
var m_MetadataSize = ReadUInt32();
long m_FileSize = ReadUInt32();
var m_Version = ReadUInt32();
long m_DataOffset = ReadUInt32();
var m_Endianess = ReadByte();
var m_Reserved = ReadBytes(3);
if (m_Version >= 22)
{
if (fileSize < 48)
{
Position = 0;
return false;
}
m_MetadataSize = ReadUInt32();
m_FileSize = ReadInt64();
m_DataOffset = ReadInt64();
}
Position = 0;
if (m_FileSize != fileSize)
{
return false;
}
if (m_DataOffset > fileSize)
{
return false;
}
return true;
}
public static bool IsReadable(string path, Game game)
{
var reader = new FileReader(path);
reader = reader.PreProcessing(game);
reader.Dispose();
return reader.FileType != FileType.ResourceFile;
}
}
public static class FileReaderExtensions
{
public static FileReader PreProcessing(this FileReader reader, Game game)
{
if (reader.FileType == FileType.ResourceFile || !game.Type.IsNormal())
{
switch (game.Type)
{
case GameType.GI_Pack:
reader = DecryptPack(reader, game);
break;
case GameType.GI_CB1:
reader = DecryptMark(reader);
break;
case GameType.EnsembleStars:
reader = DecryptEnsembleStar(reader);
break;
case GameType.OPFP:
reader = ParseOPFP(reader);
break;
case GameType.AlchemyStars:
reader = ParseAlchemyStars(reader);
break;
case GameType.FantasyOfWind:
reader = DecryptFantasyOfWind(reader);
break;
case GameType.ShiningNikki:
reader = ParseShiningNikki(reader);
break;
case GameType.HelixWaltz2:
reader = ParseHelixWaltz2(reader);
break;
case GameType.AnchorPanic:
reader = DecryptAnchorPanic(reader);
break;
}
}
if (reader.FileType == FileType.BundleFile && game.Type.IsBlockFile())
{
try
{
var signature = reader.ReadStringToNull();
reader.ReadInt32();
reader.ReadStringToNull();
reader.ReadStringToNull();
var size = reader.ReadInt64();
if (!(signature == "UnityFS" && size == reader.BaseStream.Length))
{
reader.FileType = FileType.BlockFile;
}
}
catch (Exception) { }
reader.Position = 0;
}
return reader;
}
}
}