Update README.md
Updated project Support 2017.4
This commit is contained in:
35
UnityStudio/StudioClasses/AssetPreloadData.cs
Normal file
35
UnityStudio/StudioClasses/AssetPreloadData.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
public class AssetPreloadData : ListViewItem
|
||||
{
|
||||
public long m_PathID;
|
||||
public uint Offset;
|
||||
public int Size;
|
||||
public int Type1;
|
||||
public int Type2;
|
||||
|
||||
public string TypeString;
|
||||
public int fullSize;
|
||||
public string InfoText;
|
||||
public string extension;
|
||||
|
||||
public AssetsFile sourceFile;
|
||||
public string uniqueID;
|
||||
|
||||
public EndianBinaryReader Reader
|
||||
{
|
||||
get
|
||||
{
|
||||
var reader = sourceFile.assetsFileReader;
|
||||
reader.Position = Offset;
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
483
UnityStudio/StudioClasses/AssetsFile.cs
Normal file
483
UnityStudio/StudioClasses/AssetsFile.cs
Normal file
@@ -0,0 +1,483 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
public class AssetsFile
|
||||
{
|
||||
public EndianBinaryReader assetsFileReader;
|
||||
public string filePath;
|
||||
public string bundlePath;
|
||||
public string fileName;
|
||||
public string upperFileName;
|
||||
public int fileGen;
|
||||
public bool valid;
|
||||
public string m_Version = "2.5.0f5";
|
||||
public int[] version = { 0, 0, 0, 0 };
|
||||
public string[] buildType;
|
||||
public int platform = 100663296;
|
||||
public string platformStr = "";
|
||||
public Dictionary<long, AssetPreloadData> preloadTable = new Dictionary<long, AssetPreloadData>();
|
||||
public Dictionary<long, GameObject> GameObjectList = new Dictionary<long, GameObject>();
|
||||
public Dictionary<long, Transform> TransformList = new Dictionary<long, Transform>();
|
||||
|
||||
public List<AssetPreloadData> exportableAssets = new List<AssetPreloadData>();
|
||||
public List<UnityShared> sharedAssetsList = new List<UnityShared> { new UnityShared() };
|
||||
|
||||
public SortedDictionary<int, ClassStruct> ClassStructures = new SortedDictionary<int, ClassStruct>();
|
||||
|
||||
private bool baseDefinitions;
|
||||
private List<int[]> classIDs = new List<int[]>();//use for 5.5.0
|
||||
|
||||
public static string[] buildTypeSplit = { ".", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
|
||||
|
||||
#region cmmon string
|
||||
private static Dictionary<int, string> baseStrings = new Dictionary<int, string>
|
||||
{
|
||||
{0, "AABB"},
|
||||
{5, "AnimationClip"},
|
||||
{19, "AnimationCurve"},
|
||||
{34, "AnimationState"},
|
||||
{49, "Array"},
|
||||
{55, "Base"},
|
||||
{60, "BitField"},
|
||||
{69, "bitset"},
|
||||
{76, "bool"},
|
||||
{81, "char"},
|
||||
{86, "ColorRGBA"},
|
||||
{96, "Component"},
|
||||
{106, "data"},
|
||||
{111, "deque"},
|
||||
{117, "double"},
|
||||
{124, "dynamic_array"},
|
||||
{138, "FastPropertyName"},
|
||||
{155, "first"},
|
||||
{161, "float"},
|
||||
{167, "Font"},
|
||||
{172, "GameObject"},
|
||||
{183, "Generic Mono"},
|
||||
{196, "GradientNEW"},
|
||||
{208, "GUID"},
|
||||
{213, "GUIStyle"},
|
||||
{222, "int"},
|
||||
{226, "list"},
|
||||
{231, "long long"},
|
||||
{241, "map"},
|
||||
{245, "Matrix4x4f"},
|
||||
{256, "MdFour"},
|
||||
{263, "MonoBehaviour"},
|
||||
{277, "MonoScript"},
|
||||
{288, "m_ByteSize"},
|
||||
{299, "m_Curve"},
|
||||
{307, "m_EditorClassIdentifier"},
|
||||
{331, "m_EditorHideFlags"},
|
||||
{349, "m_Enabled"},
|
||||
{359, "m_ExtensionPtr"},
|
||||
{374, "m_GameObject"},
|
||||
{387, "m_Index"},
|
||||
{395, "m_IsArray"},
|
||||
{405, "m_IsStatic"},
|
||||
{416, "m_MetaFlag"},
|
||||
{427, "m_Name"},
|
||||
{434, "m_ObjectHideFlags"},
|
||||
{452, "m_PrefabInternal"},
|
||||
{469, "m_PrefabParentObject"},
|
||||
{490, "m_Script"},
|
||||
{499, "m_StaticEditorFlags"},
|
||||
{519, "m_Type"},
|
||||
{526, "m_Version"},
|
||||
{536, "Object"},
|
||||
{543, "pair"},
|
||||
{548, "PPtr<Component>"},
|
||||
{564, "PPtr<GameObject>"},
|
||||
{581, "PPtr<Material>"},
|
||||
{596, "PPtr<MonoBehaviour>"},
|
||||
{616, "PPtr<MonoScript>"},
|
||||
{633, "PPtr<Object>"},
|
||||
{646, "PPtr<Prefab>"},
|
||||
{659, "PPtr<Sprite>"},
|
||||
{672, "PPtr<TextAsset>"},
|
||||
{688, "PPtr<Texture>"},
|
||||
{702, "PPtr<Texture2D>"},
|
||||
{718, "PPtr<Transform>"},
|
||||
{734, "Prefab"},
|
||||
{741, "Quaternionf"},
|
||||
{753, "Rectf"},
|
||||
{759, "RectInt"},
|
||||
{767, "RectOffset"},
|
||||
{778, "second"},
|
||||
{785, "set"},
|
||||
{789, "short"},
|
||||
{795, "size"},
|
||||
{800, "SInt16"},
|
||||
{807, "SInt32"},
|
||||
{814, "SInt64"},
|
||||
{821, "SInt8"},
|
||||
{827, "staticvector"},
|
||||
{840, "string"},
|
||||
{847, "TextAsset"},
|
||||
{857, "TextMesh"},
|
||||
{866, "Texture"},
|
||||
{874, "Texture2D"},
|
||||
{884, "Transform"},
|
||||
{894, "TypelessData"},
|
||||
{907, "UInt16"},
|
||||
{914, "UInt32"},
|
||||
{921, "UInt64"},
|
||||
{928, "UInt8"},
|
||||
{934, "unsigned int"},
|
||||
{947, "unsigned long long"},
|
||||
{966, "unsigned short"},
|
||||
{981, "vector"},
|
||||
{988, "Vector2f"},
|
||||
{997, "Vector3f"},
|
||||
{1006, "Vector4f"},
|
||||
{1015, "m_ScriptingClassIdentifier"},
|
||||
{1042, "Gradient"},
|
||||
{1051, "Type*"}
|
||||
};
|
||||
#endregion
|
||||
|
||||
public class UnityShared
|
||||
{
|
||||
public int Index = -2; //-2 - Prepare, -1 - Missing
|
||||
public string aName = "";
|
||||
public string fileName = "";
|
||||
}
|
||||
|
||||
public AssetsFile(string fullName, EndianBinaryReader reader)
|
||||
{
|
||||
assetsFileReader = reader;
|
||||
filePath = fullName;
|
||||
fileName = Path.GetFileName(fullName);
|
||||
upperFileName = fileName.ToUpper();
|
||||
try
|
||||
{
|
||||
int tableSize = assetsFileReader.ReadInt32();
|
||||
int dataEnd = assetsFileReader.ReadInt32();
|
||||
fileGen = assetsFileReader.ReadInt32();
|
||||
uint dataOffset = assetsFileReader.ReadUInt32();
|
||||
sharedAssetsList[0].fileName = fileName; //reference itself because sharedFileIDs start from 1
|
||||
|
||||
switch (fileGen)
|
||||
{
|
||||
case 6: //2.5.0 - 2.6.1
|
||||
{
|
||||
assetsFileReader.Position = (dataEnd - tableSize);
|
||||
assetsFileReader.Position += 1;
|
||||
break;
|
||||
}
|
||||
case 7: //3.0.0 beta
|
||||
{
|
||||
assetsFileReader.Position = (dataEnd - tableSize);
|
||||
assetsFileReader.Position += 1;
|
||||
m_Version = assetsFileReader.ReadStringToNull();
|
||||
break;
|
||||
}
|
||||
case 8: //3.0.0 - 3.4.2
|
||||
{
|
||||
assetsFileReader.Position = (dataEnd - tableSize);
|
||||
assetsFileReader.Position += 1;
|
||||
m_Version = assetsFileReader.ReadStringToNull();
|
||||
platform = assetsFileReader.ReadInt32();
|
||||
break;
|
||||
}
|
||||
case 9: //3.5.0 - 4.6.x
|
||||
{
|
||||
assetsFileReader.Position += 4; //azero
|
||||
m_Version = assetsFileReader.ReadStringToNull();
|
||||
platform = assetsFileReader.ReadInt32();
|
||||
break;
|
||||
}
|
||||
case 14: //5.0.0 beta and final
|
||||
case 15: //5.0.1 - 5.4
|
||||
case 16: //??.. no sure
|
||||
case 17: //5.5.0 and up
|
||||
{
|
||||
assetsFileReader.Position += 4; //azero
|
||||
m_Version = assetsFileReader.ReadStringToNull();
|
||||
platform = assetsFileReader.ReadInt32();
|
||||
baseDefinitions = assetsFileReader.ReadBoolean();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
//MessageBox.Show("Unsupported Unity version!" + fileGen, "UnityStudio Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (platform > 255 || platform < 0)
|
||||
{
|
||||
byte[] b32 = BitConverter.GetBytes(platform);
|
||||
Array.Reverse(b32);
|
||||
platform = BitConverter.ToInt32(b32, 0);
|
||||
assetsFileReader.endian = EndianType.LittleEndian;
|
||||
}
|
||||
|
||||
platformStr = Enum.TryParse(platform.ToString(), out BuildTarget buildTarget) ? buildTarget.ToString() : "Unknown Platform";
|
||||
|
||||
int baseCount = assetsFileReader.ReadInt32();
|
||||
for (int i = 0; i < baseCount; i++)
|
||||
{
|
||||
if (fileGen < 14)
|
||||
{
|
||||
int classID = assetsFileReader.ReadInt32();
|
||||
string baseType = assetsFileReader.ReadStringToNull();
|
||||
string baseName = assetsFileReader.ReadStringToNull();
|
||||
assetsFileReader.Position += 20;
|
||||
int memberCount = assetsFileReader.ReadInt32();
|
||||
|
||||
var cb = new List<ClassMember>();
|
||||
for (int m = 0; m < memberCount; m++)
|
||||
{
|
||||
readBase(cb, 1);
|
||||
}
|
||||
|
||||
var aClass = new ClassStruct { ID = classID, Text = (baseType + " " + baseName), members = cb };
|
||||
aClass.SubItems.Add(classID.ToString());
|
||||
ClassStructures.Add(classID, aClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
readBase5();
|
||||
}
|
||||
}
|
||||
|
||||
if (fileGen >= 7 && fileGen < 14)
|
||||
{
|
||||
assetsFileReader.Position += 4; //azero
|
||||
}
|
||||
|
||||
int assetCount = assetsFileReader.ReadInt32();
|
||||
|
||||
#region asset preload table
|
||||
string assetIDfmt = "D" + assetCount.ToString().Length; //format for unique ID
|
||||
|
||||
for (int i = 0; i < assetCount; i++)
|
||||
{
|
||||
//each table entry is aligned individually, not the whole table
|
||||
if (fileGen >= 14)
|
||||
{
|
||||
assetsFileReader.AlignStream(4);
|
||||
}
|
||||
|
||||
AssetPreloadData asset = new AssetPreloadData();
|
||||
asset.m_PathID = fileGen < 14 ? assetsFileReader.ReadInt32() : assetsFileReader.ReadInt64();
|
||||
asset.Offset = assetsFileReader.ReadUInt32();
|
||||
asset.Offset += dataOffset;
|
||||
asset.Size = assetsFileReader.ReadInt32();
|
||||
if (fileGen > 15)
|
||||
{
|
||||
int index = assetsFileReader.ReadInt32();
|
||||
asset.Type1 = classIDs[index][0];
|
||||
asset.Type2 = classIDs[index][1];
|
||||
}
|
||||
else
|
||||
{
|
||||
asset.Type1 = assetsFileReader.ReadInt32();
|
||||
asset.Type2 = assetsFileReader.ReadUInt16();
|
||||
assetsFileReader.Position += 2;
|
||||
}
|
||||
if (fileGen == 15)
|
||||
{
|
||||
byte unknownByte = assetsFileReader.ReadByte();
|
||||
//this is a single byte, not an int32
|
||||
//the next entry is aligned after this
|
||||
//but not the last!
|
||||
}
|
||||
|
||||
if (ClassIDReference.Names.TryGetValue(asset.Type2, out var typeString))
|
||||
{
|
||||
asset.TypeString = typeString;
|
||||
}
|
||||
else
|
||||
{
|
||||
asset.TypeString = "Unknown Type " + asset.Type2;
|
||||
}
|
||||
|
||||
asset.uniqueID = i.ToString(assetIDfmt);
|
||||
|
||||
asset.fullSize = asset.Size;
|
||||
asset.sourceFile = this;
|
||||
|
||||
preloadTable.Add(asset.m_PathID, asset);
|
||||
|
||||
#region read BuildSettings to get version for unity 2.x files
|
||||
if (asset.Type2 == 141 && fileGen == 6)
|
||||
{
|
||||
long nextAsset = assetsFileReader.Position;
|
||||
|
||||
BuildSettings BSettings = new BuildSettings(asset);
|
||||
m_Version = BSettings.m_Version;
|
||||
|
||||
assetsFileReader.Position = nextAsset;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
buildType = m_Version.Split(buildTypeSplit, StringSplitOptions.RemoveEmptyEntries);
|
||||
var strver = from Match m in Regex.Matches(m_Version, @"[0-9]") select m.Value;
|
||||
version = Array.ConvertAll(strver.ToArray(), int.Parse);
|
||||
if (version[0] == 2 && version[1] == 0 && version[2] == 1 && version[3] == 7)//2017.x
|
||||
{
|
||||
var nversion = new int[version.Length - 3];
|
||||
nversion[0] = 2017;
|
||||
Array.Copy(version, 4, nversion, 1, version.Length - 4);
|
||||
version = nversion;
|
||||
}
|
||||
if (fileGen >= 14)
|
||||
{
|
||||
//this looks like a list of assets that need to be preloaded in memory before anytihng else
|
||||
int someCount = assetsFileReader.ReadInt32();
|
||||
for (int i = 0; i < someCount; i++)
|
||||
{
|
||||
int num1 = assetsFileReader.ReadInt32();
|
||||
assetsFileReader.AlignStream(4);
|
||||
long m_PathID = assetsFileReader.ReadInt64();
|
||||
}
|
||||
}
|
||||
|
||||
int sharedFileCount = assetsFileReader.ReadInt32();
|
||||
for (int i = 0; i < sharedFileCount; i++)
|
||||
{
|
||||
var shared = new UnityShared();
|
||||
shared.aName = assetsFileReader.ReadStringToNull();
|
||||
assetsFileReader.Position += 20;
|
||||
var sharedFilePath = assetsFileReader.ReadStringToNull(); //relative path
|
||||
shared.fileName = Path.GetFileName(sharedFilePath);
|
||||
sharedAssetsList.Add(shared);
|
||||
}
|
||||
valid = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void readBase(List<ClassMember> cb, int level)
|
||||
{
|
||||
string varType = assetsFileReader.ReadStringToNull();
|
||||
string varName = assetsFileReader.ReadStringToNull();
|
||||
int size = assetsFileReader.ReadInt32();
|
||||
int index = assetsFileReader.ReadInt32();
|
||||
int isArray = assetsFileReader.ReadInt32();
|
||||
int num0 = assetsFileReader.ReadInt32();
|
||||
int flag = assetsFileReader.ReadInt32();
|
||||
int childrenCount = assetsFileReader.ReadInt32();
|
||||
|
||||
cb.Add(new ClassMember
|
||||
{
|
||||
Level = level - 1,
|
||||
Type = varType,
|
||||
Name = varName,
|
||||
Size = size,
|
||||
Flag = flag
|
||||
});
|
||||
for (int i = 0; i < childrenCount; i++) { readBase(cb, level + 1); }
|
||||
}
|
||||
|
||||
private void readBase5()
|
||||
{
|
||||
int classID = assetsFileReader.ReadInt32();
|
||||
if (fileGen > 15)//5.5.0 and up
|
||||
{
|
||||
assetsFileReader.ReadByte();
|
||||
int type1;
|
||||
if ((type1 = assetsFileReader.ReadInt16()) >= 0)
|
||||
{
|
||||
type1 = -1 - type1;
|
||||
}
|
||||
else
|
||||
{
|
||||
type1 = classID;
|
||||
}
|
||||
classIDs.Add(new[] { type1, classID });
|
||||
if (classID == 114)
|
||||
{
|
||||
assetsFileReader.Position += 16;
|
||||
}
|
||||
classID = type1;
|
||||
}
|
||||
else if (classID < 0)
|
||||
{
|
||||
assetsFileReader.Position += 16;
|
||||
}
|
||||
assetsFileReader.Position += 16;
|
||||
|
||||
if (baseDefinitions)
|
||||
{
|
||||
int varCount = assetsFileReader.ReadInt32();
|
||||
int stringSize = assetsFileReader.ReadInt32();
|
||||
|
||||
assetsFileReader.Position += varCount * 24;
|
||||
var stringReader = new EndianBinaryReader(new MemoryStream(assetsFileReader.ReadBytes(stringSize)));
|
||||
string className = "";
|
||||
var classVar = new List<ClassMember>();
|
||||
//build Class Structures
|
||||
assetsFileReader.Position -= varCount * 24 + stringSize;
|
||||
for (int i = 0; i < varCount; i++)
|
||||
{
|
||||
ushort num0 = assetsFileReader.ReadUInt16();
|
||||
byte level = assetsFileReader.ReadByte();
|
||||
bool isArray = assetsFileReader.ReadBoolean();
|
||||
|
||||
ushort varTypeIndex = assetsFileReader.ReadUInt16();
|
||||
ushort test = assetsFileReader.ReadUInt16();
|
||||
string varTypeStr;
|
||||
if (test == 0) //varType is an offset in the string block
|
||||
{
|
||||
stringReader.Position = varTypeIndex;
|
||||
varTypeStr = stringReader.ReadStringToNull();
|
||||
}
|
||||
else //varType is an index in an internal strig array
|
||||
{
|
||||
varTypeStr = baseStrings.ContainsKey(varTypeIndex) ? baseStrings[varTypeIndex] : varTypeIndex.ToString();
|
||||
}
|
||||
|
||||
ushort varNameIndex = assetsFileReader.ReadUInt16();
|
||||
test = assetsFileReader.ReadUInt16();
|
||||
string varNameStr;
|
||||
if (test == 0)
|
||||
{
|
||||
stringReader.Position = varNameIndex;
|
||||
varNameStr = stringReader.ReadStringToNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
varNameStr = baseStrings.ContainsKey(varNameIndex) ? baseStrings[varNameIndex] : varNameIndex.ToString();
|
||||
}
|
||||
|
||||
int size = assetsFileReader.ReadInt32();
|
||||
int index = assetsFileReader.ReadInt32();
|
||||
int flag = assetsFileReader.ReadInt32();
|
||||
|
||||
if (index == 0) { className = varTypeStr + " " + varNameStr; }
|
||||
else
|
||||
{
|
||||
classVar.Add(new ClassMember
|
||||
{
|
||||
Level = level - 1,
|
||||
Type = varTypeStr,
|
||||
Name = varNameStr,
|
||||
Size = size,
|
||||
Flag = flag
|
||||
});
|
||||
}
|
||||
}
|
||||
stringReader.Dispose();
|
||||
assetsFileReader.Position += stringSize;
|
||||
|
||||
var aClass = new ClassStruct { ID = classID, Text = className, members = classVar };
|
||||
aClass.SubItems.Add(classID.ToString());
|
||||
ClassStructures[classID] = aClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
217
UnityStudio/StudioClasses/BundleFile.cs
Normal file
217
UnityStudio/StudioClasses/BundleFile.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Lz4;
|
||||
using SevenZip.Compression.LZMA;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
public class MemoryFile
|
||||
{
|
||||
public string fileName;
|
||||
public MemoryStream stream;
|
||||
}
|
||||
|
||||
public class BundleFile
|
||||
{
|
||||
public int format;
|
||||
public string versionPlayer;
|
||||
public string versionEngine;
|
||||
public List<MemoryFile> fileList = new List<MemoryFile>();
|
||||
|
||||
public BundleFile(EndianBinaryReader bundleReader)
|
||||
{
|
||||
var signature = bundleReader.ReadStringToNull();
|
||||
switch (signature)
|
||||
{
|
||||
case "UnityWeb":
|
||||
case "UnityRaw":
|
||||
case "\xFA\xFA\xFA\xFA\xFA\xFA\xFA\xFA":
|
||||
{
|
||||
format = bundleReader.ReadInt32();
|
||||
versionPlayer = bundleReader.ReadStringToNull();
|
||||
versionEngine = bundleReader.ReadStringToNull();
|
||||
if (format < 6)
|
||||
{
|
||||
int bundleSize = bundleReader.ReadInt32();
|
||||
}
|
||||
else if (format == 6)
|
||||
{
|
||||
ReadFormat6(bundleReader, true);
|
||||
return;
|
||||
}
|
||||
short dummy2 = bundleReader.ReadInt16();
|
||||
int offset = bundleReader.ReadInt16();
|
||||
int dummy3 = bundleReader.ReadInt32();
|
||||
int lzmaChunks = bundleReader.ReadInt32();
|
||||
|
||||
int lzmaSize = 0;
|
||||
long streamSize = 0;
|
||||
|
||||
for (int i = 0; i < lzmaChunks; i++)
|
||||
{
|
||||
lzmaSize = bundleReader.ReadInt32();
|
||||
streamSize = bundleReader.ReadInt32();
|
||||
}
|
||||
|
||||
bundleReader.Position = offset;
|
||||
switch (signature)
|
||||
{
|
||||
case "\xFA\xFA\xFA\xFA\xFA\xFA\xFA\xFA": //.bytes
|
||||
case "UnityWeb":
|
||||
{
|
||||
var lzmaBuffer = bundleReader.ReadBytes(lzmaSize);
|
||||
using (var lzmaStream = new EndianBinaryReader(SevenZipHelper.StreamDecompress(new MemoryStream(lzmaBuffer))))
|
||||
{
|
||||
GetAssetsFiles(lzmaStream, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "UnityRaw":
|
||||
{
|
||||
GetAssetsFiles(bundleReader, offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "UnityFS":
|
||||
format = bundleReader.ReadInt32();
|
||||
versionPlayer = bundleReader.ReadStringToNull();
|
||||
versionEngine = bundleReader.ReadStringToNull();
|
||||
if (format == 6)
|
||||
{
|
||||
ReadFormat6(bundleReader);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void GetAssetsFiles(EndianBinaryReader reader, int offset)
|
||||
{
|
||||
int fileCount = reader.ReadInt32();
|
||||
for (int i = 0; i < fileCount; i++)
|
||||
{
|
||||
var file = new MemoryFile();
|
||||
file.fileName = reader.ReadStringToNull();
|
||||
int fileOffset = reader.ReadInt32();
|
||||
fileOffset += offset;
|
||||
int fileSize = reader.ReadInt32();
|
||||
long nextFile = reader.Position;
|
||||
reader.Position = fileOffset;
|
||||
var buffer = reader.ReadBytes(fileSize);
|
||||
file.stream = new MemoryStream(buffer);
|
||||
fileList.Add(file);
|
||||
reader.Position = nextFile;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadFormat6(EndianBinaryReader bundleReader, bool padding = false)
|
||||
{
|
||||
var bundleSize = bundleReader.ReadInt64();
|
||||
int compressedSize = bundleReader.ReadInt32();
|
||||
int uncompressedSize = bundleReader.ReadInt32();
|
||||
int flag = bundleReader.ReadInt32();
|
||||
if (padding)
|
||||
bundleReader.ReadByte();
|
||||
byte[] blocksInfoBytes;
|
||||
if ((flag & 0x80) != 0)//at end of file
|
||||
{
|
||||
var position = bundleReader.Position;
|
||||
bundleReader.Position = bundleReader.BaseStream.Length - compressedSize;
|
||||
blocksInfoBytes = bundleReader.ReadBytes(compressedSize);
|
||||
bundleReader.Position = position;
|
||||
}
|
||||
else
|
||||
{
|
||||
blocksInfoBytes = bundleReader.ReadBytes(compressedSize);
|
||||
}
|
||||
MemoryStream blocksInfoStream;
|
||||
switch (flag & 0x3F)
|
||||
{
|
||||
default://None
|
||||
{
|
||||
blocksInfoStream = new MemoryStream(blocksInfoBytes);
|
||||
break;
|
||||
}
|
||||
case 1://LZMA
|
||||
{
|
||||
blocksInfoStream = SevenZipHelper.StreamDecompress(new MemoryStream(blocksInfoBytes));
|
||||
break;
|
||||
}
|
||||
case 2://LZ4
|
||||
case 3://LZ4HC
|
||||
{
|
||||
byte[] uncompressedBytes = new byte[uncompressedSize];
|
||||
using (var decoder = new Lz4DecoderStream(new MemoryStream(blocksInfoBytes)))
|
||||
{
|
||||
decoder.Read(uncompressedBytes, 0, uncompressedSize);
|
||||
}
|
||||
blocksInfoStream = new MemoryStream(uncompressedBytes);
|
||||
break;
|
||||
}
|
||||
//case 4:LZHAM?
|
||||
}
|
||||
using (var blocksInfo = new EndianBinaryReader(blocksInfoStream))
|
||||
{
|
||||
blocksInfo.Position = 0x10;
|
||||
int blockcount = blocksInfo.ReadInt32();
|
||||
var assetsDataStream = new MemoryStream();
|
||||
for (int i = 0; i < blockcount; i++)
|
||||
{
|
||||
uncompressedSize = blocksInfo.ReadInt32();
|
||||
compressedSize = blocksInfo.ReadInt32();
|
||||
flag = blocksInfo.ReadInt16();
|
||||
var compressedBytes = bundleReader.ReadBytes(compressedSize);
|
||||
switch (flag & 0x3F)
|
||||
{
|
||||
default://None
|
||||
{
|
||||
assetsDataStream.Write(compressedBytes, 0, compressedSize);
|
||||
break;
|
||||
}
|
||||
case 1://LZMA
|
||||
{
|
||||
var uncompressedBytes = new byte[uncompressedSize];
|
||||
using (var mstream = new MemoryStream(compressedBytes))
|
||||
{
|
||||
var decoder = SevenZipHelper.StreamDecompress(mstream, uncompressedSize);
|
||||
decoder.Read(uncompressedBytes, 0, uncompressedSize);
|
||||
decoder.Dispose();
|
||||
}
|
||||
assetsDataStream.Write(uncompressedBytes, 0, uncompressedSize);
|
||||
break;
|
||||
}
|
||||
case 2://LZ4
|
||||
case 3://LZ4HC
|
||||
{
|
||||
var uncompressedBytes = new byte[uncompressedSize];
|
||||
using (var decoder = new Lz4DecoderStream(new MemoryStream(compressedBytes)))
|
||||
{
|
||||
decoder.Read(uncompressedBytes, 0, uncompressedSize);
|
||||
}
|
||||
assetsDataStream.Write(uncompressedBytes, 0, uncompressedSize);
|
||||
break;
|
||||
}
|
||||
//case 4:LZHAM?
|
||||
}
|
||||
}
|
||||
using (var assetsDataReader = new EndianBinaryReader(assetsDataStream))
|
||||
{
|
||||
var entryinfo_count = blocksInfo.ReadInt32();
|
||||
for (int i = 0; i < entryinfo_count; i++)
|
||||
{
|
||||
var file = new MemoryFile();
|
||||
var entryinfo_offset = blocksInfo.ReadInt64();
|
||||
var entryinfo_size = blocksInfo.ReadInt64();
|
||||
flag = blocksInfo.ReadInt32();
|
||||
file.fileName = Path.GetFileName(blocksInfo.ReadStringToNull());
|
||||
assetsDataReader.Position = entryinfo_offset;
|
||||
var buffer = assetsDataReader.ReadBytes((int)entryinfo_size);
|
||||
file.stream = new MemoryStream(buffer);
|
||||
fileList.Add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
277
UnityStudio/StudioClasses/ClassIDReference.cs
Normal file
277
UnityStudio/StudioClasses/ClassIDReference.cs
Normal file
@@ -0,0 +1,277 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
public static class ClassIDReference
|
||||
{
|
||||
public static Dictionary<int, string> Names = new Dictionary<int, string>()
|
||||
{
|
||||
{1, "GameObject"},
|
||||
{2, "Component"},
|
||||
{3, "LevelGameManager"},
|
||||
{4, "Transform"},
|
||||
{5, "TimeManager"},
|
||||
{6, "GlobalGameManager"},
|
||||
{8, "Behaviour"},
|
||||
{9, "GameManager"},
|
||||
{11, "AudioManager"},
|
||||
{12, "ParticleAnimator"},
|
||||
{13, "InputManager"},
|
||||
{15, "EllipsoidParticleEmitter"},
|
||||
{17, "Pipeline"},
|
||||
{18, "EditorExtension"},
|
||||
{19, "Physics2DSettings"},
|
||||
{20, "Camera"},
|
||||
{21, "Material"},
|
||||
{23, "MeshRenderer"},
|
||||
{25, "Renderer"},
|
||||
{26, "ParticleRenderer"},
|
||||
{27, "Texture"},
|
||||
{28, "Texture2D"},
|
||||
{29, "SceneSettings"},
|
||||
{30, "GraphicsSettings"},
|
||||
{33, "MeshFilter"},
|
||||
{41, "OcclusionPortal"},
|
||||
{43, "Mesh"},
|
||||
{45, "Skybox"},
|
||||
{47, "QualitySettings"},
|
||||
{48, "Shader"},
|
||||
{49, "TextAsset"},
|
||||
{50, "Rigidbody2D"},
|
||||
{51, "Physics2DManager"},
|
||||
{53, "Collider2D"},
|
||||
{54, "Rigidbody"},
|
||||
{55, "PhysicsManager"},
|
||||
{56, "Collider"},
|
||||
{57, "Joint"},
|
||||
{58, "CircleCollider2D"},
|
||||
{59, "HingeJoint"},
|
||||
{60, "PolygonCollider2D"},
|
||||
{61, "BoxCollider2D"},
|
||||
{62, "PhysicsMaterial2D"},
|
||||
{64, "MeshCollider"},
|
||||
{65, "BoxCollider"},
|
||||
{66, "SpriteCollider2D"},
|
||||
{68, "EdgeCollider2D"},
|
||||
{70, "CapsuleCollider2D"},
|
||||
{72, "ComputeShader"},
|
||||
{74, "AnimationClip"},
|
||||
{75, "ConstantForce"},
|
||||
{76, "WorldParticleCollider"},
|
||||
{78, "TagManager"},
|
||||
{81, "AudioListener"},
|
||||
{82, "AudioSource"},
|
||||
{83, "AudioClip"},
|
||||
{84, "RenderTexture"},
|
||||
{86, "CustomRenderTexture"},
|
||||
{87, "MeshParticleEmitter"},
|
||||
{88, "ParticleEmitter"},
|
||||
{89, "Cubemap"},
|
||||
{90, "Avatar"},
|
||||
{91, "AnimatorController"},
|
||||
{92, "GUILayer"},
|
||||
{93, "RuntimeAnimatorController"},
|
||||
{94, "ScriptMapper"},
|
||||
{95, "Animator"},
|
||||
{96, "TrailRenderer"},
|
||||
{98, "DelayedCallManager"},
|
||||
{102, "TextMesh"},
|
||||
{104, "RenderSettings"},
|
||||
{108, "Light"},
|
||||
{109, "CGProgram"},
|
||||
{110, "BaseAnimationTrack"},
|
||||
{111, "Animation"},
|
||||
{114, "MonoBehaviour"},
|
||||
{115, "MonoScript"},
|
||||
{116, "MonoManager"},
|
||||
{117, "Texture3D"},
|
||||
{118, "NewAnimationTrack"},
|
||||
{119, "Projector"},
|
||||
{120, "LineRenderer"},
|
||||
{121, "Flare"},
|
||||
{122, "Halo"},
|
||||
{123, "LensFlare"},
|
||||
{124, "FlareLayer"},
|
||||
{125, "HaloLayer"},
|
||||
{126, "NavMeshAreas"},
|
||||
{127, "HaloManager"},
|
||||
{128, "Font"},
|
||||
{129, "PlayerSettings"},
|
||||
{130, "NamedObject"},
|
||||
{131, "GUITexture"},
|
||||
{132, "GUIText"},
|
||||
{133, "GUIElement"},
|
||||
{134, "PhysicMaterial"},
|
||||
{135, "SphereCollider"},
|
||||
{136, "CapsuleCollider"},
|
||||
{137, "SkinnedMeshRenderer"},
|
||||
{138, "FixedJoint"},
|
||||
{140, "RaycastCollider"},
|
||||
{141, "BuildSettings"},
|
||||
{142, "AssetBundle"},
|
||||
{143, "CharacterController"},
|
||||
{144, "CharacterJoint"},
|
||||
{145, "SpringJoint"},
|
||||
{146, "WheelCollider"},
|
||||
{147, "ResourceManager"},
|
||||
{148, "NetworkView"},
|
||||
{149, "NetworkManager"},
|
||||
{150, "PreloadData"},
|
||||
{152, "MovieTexture"},
|
||||
{153, "ConfigurableJoint"},
|
||||
{154, "TerrainCollider"},
|
||||
{155, "MasterServerInterface"},
|
||||
{156, "TerrainData"},
|
||||
{157, "LightmapSettings"},
|
||||
{158, "WebCamTexture"},
|
||||
{159, "EditorSettings"},
|
||||
{160, "InteractiveCloth"},
|
||||
{161, "ClothRenderer"},
|
||||
{162, "EditorUserSettings"},
|
||||
{163, "SkinnedCloth"},
|
||||
{164, "AudioReverbFilter"},
|
||||
{165, "AudioHighPassFilter"},
|
||||
{166, "AudioChorusFilter"},
|
||||
{167, "AudioReverbZone"},
|
||||
{168, "AudioEchoFilter"},
|
||||
{169, "AudioLowPassFilter"},
|
||||
{170, "AudioDistortionFilter"},
|
||||
{171, "SparseTexture"},
|
||||
{180, "AudioBehaviour"},
|
||||
{181, "AudioFilter"},
|
||||
{182, "WindZone"},
|
||||
{183, "Cloth"},
|
||||
{184, "SubstanceArchive"},
|
||||
{185, "ProceduralMaterial"},
|
||||
{186, "ProceduralTexture"},
|
||||
{187, "Texture2DArray"},
|
||||
{188, "CubemapArray"},
|
||||
{191, "OffMeshLink"},
|
||||
{192, "OcclusionArea"},
|
||||
{193, "Tree"},
|
||||
{194, "NavMeshObsolete"},
|
||||
{195, "NavMeshAgent"},
|
||||
{196, "NavMeshSettings"},
|
||||
{197, "LightProbesLegacy"},
|
||||
{198, "ParticleSystem"},
|
||||
{199, "ParticleSystemRenderer"},
|
||||
{200, "ShaderVariantCollection"},
|
||||
{205, "LODGroup"},
|
||||
{206, "BlendTree"},
|
||||
{207, "Motion"},
|
||||
{208, "NavMeshObstacle"},
|
||||
{210, "TerrainInstance"},
|
||||
{212, "SpriteRenderer"},
|
||||
{213, "Sprite"},
|
||||
{214, "CachedSpriteAtlas"},
|
||||
{215, "ReflectionProbe"},
|
||||
{216, "ReflectionProbes"},
|
||||
{218, "Terrain"},
|
||||
{220, "LightProbeGroup"},
|
||||
{221, "AnimatorOverrideController"},
|
||||
{222, "CanvasRenderer"},
|
||||
{223, "Canvas"},
|
||||
{224, "RectTransform"},
|
||||
{225, "CanvasGroup"},
|
||||
{226, "BillboardAsset"},
|
||||
{227, "BillboardRenderer"},
|
||||
{228, "SpeedTreeWindAsset"},
|
||||
{229, "AnchoredJoint2D"},
|
||||
{230, "Joint2D"},
|
||||
{231, "SpringJoint2D"},
|
||||
{232, "DistanceJoint2D"},
|
||||
{233, "HingeJoint2D"},
|
||||
{234, "SliderJoint2D"},
|
||||
{235, "WheelJoint2D"},
|
||||
{236, "ClusterInputManager"},
|
||||
{237, "BaseVideoTexture"},
|
||||
{238, "NavMeshData"},
|
||||
{240, "AudioMixer"},
|
||||
{241, "AudioMixerController"},
|
||||
{243, "AudioMixerGroupController"},
|
||||
{244, "AudioMixerEffectController"},
|
||||
{245, "AudioMixerSnapshotController"},
|
||||
{246, "PhysicsUpdateBehaviour2D"},
|
||||
{247, "ConstantForce2D"},
|
||||
{248, "Effector2D"},
|
||||
{249, "AreaEffector2D"},
|
||||
{250, "PointEffector2D"},
|
||||
{251, "PlatformEffector2D"},
|
||||
{252, "SurfaceEffector2D"},
|
||||
{253, "BuoyancyEffector2D"},
|
||||
{254, "RelativeJoint2D"},
|
||||
{255, "FixedJoint2D"},
|
||||
{256, "FrictionJoint2D"},
|
||||
{257, "TargetJoint2D"},
|
||||
{258, "LightProbes"},
|
||||
{259, "LightProbeProxyVolume"},
|
||||
{271, "SampleClip"},
|
||||
{272, "AudioMixerSnapshot"},
|
||||
{273, "AudioMixerGroup"},
|
||||
{280, "NScreenBridge"},
|
||||
{290, "AssetBundleManifest"},
|
||||
{292, "UnityAdsManager"},
|
||||
{300, "RuntimeInitializeOnLoadManager"},
|
||||
{301, "CloudWebServicesManager"},
|
||||
{303, "UnityAnalyticsManager"},
|
||||
{304, "CrashReportManager"},
|
||||
{305, "PerformanceReportingManager"},
|
||||
{310, "UnityConnectSettings"},
|
||||
{319, "AvatarMask"},
|
||||
{328, "VideoPlayer"},
|
||||
{329, "VideoClip"},
|
||||
{363, "OcclusionCullingData"},
|
||||
{1001, "Prefab"},
|
||||
{1002, "EditorExtensionImpl"},
|
||||
{1003, "AssetImporter"},
|
||||
{1004, "AssetDatabase"},
|
||||
{1005, "Mesh3DSImporter"},
|
||||
{1006, "TextureImporter"},
|
||||
{1007, "ShaderImporter"},
|
||||
{1008, "ComputeShaderImporter"},
|
||||
{1011, "AvatarMask"},
|
||||
{1020, "AudioImporter"},
|
||||
{1026, "HierarchyState"},
|
||||
{1027, "GUIDSerializer"},
|
||||
{1028, "AssetMetaData"},
|
||||
{1029, "DefaultAsset"},
|
||||
{1030, "DefaultImporter"},
|
||||
{1031, "TextScriptImporter"},
|
||||
{1032, "SceneAsset"},
|
||||
{1034, "NativeFormatImporter"},
|
||||
{1035, "MonoImporter"},
|
||||
{1037, "AssetServerCache"},
|
||||
{1038, "LibraryAssetImporter"},
|
||||
{1040, "ModelImporter"},
|
||||
{1041, "FBXImporter"},
|
||||
{1042, "TrueTypeFontImporter"},
|
||||
{1044, "MovieImporter"},
|
||||
{1045, "EditorBuildSettings"},
|
||||
{1046, "DDSImporter"},
|
||||
{1048, "InspectorExpandedState"},
|
||||
{1049, "AnnotationManager"},
|
||||
{1050, "PluginImporter"},
|
||||
{1051, "EditorUserBuildSettings"},
|
||||
{1052, "PVRImporter"},
|
||||
{1053, "ASTCImporter"},
|
||||
{1054, "KTXImporter"},
|
||||
{1101, "AnimatorStateTransition"},
|
||||
{1102, "AnimatorState"},
|
||||
{1105, "HumanTemplate"},
|
||||
{1107, "AnimatorStateMachine"},
|
||||
{1108, "PreviewAssetType"},
|
||||
{1109, "AnimatorTransition"},
|
||||
{1110, "SpeedTreeImporter"},
|
||||
{1111, "AnimatorTransitionBase"},
|
||||
{1112, "SubstanceImporter"},
|
||||
{1113, "LightmapParameters"},
|
||||
{1120, "LightmapSnapshot"},
|
||||
{367388927, "SubDerived"},
|
||||
{334799969, "SiblingDerived"},
|
||||
{687078895, "SpriteAtlas"},
|
||||
{1091556383, "Derived"},
|
||||
{1480428607, "LowerResBlitTexture"},
|
||||
{1571458007, "RenderPassAttachment"}
|
||||
};
|
||||
}
|
||||
}
|
||||
189
UnityStudio/StudioClasses/ClassStruct.cs
Normal file
189
UnityStudio/StudioClasses/ClassStruct.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
public class ClassMember
|
||||
{
|
||||
public int Level;
|
||||
public string Type;
|
||||
public string Name;
|
||||
public int Size;
|
||||
public int Flag;
|
||||
|
||||
//use for read
|
||||
public bool alignBefore;
|
||||
}
|
||||
|
||||
public class ClassStruct : ListViewItem
|
||||
{
|
||||
public int ID;
|
||||
public List<ClassMember> members;
|
||||
|
||||
public string membersstr
|
||||
{
|
||||
get
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var i in members)
|
||||
{
|
||||
sb.AppendFormat("{0}{1} {2} {3} {4}\r\n", new string('\t', i.Level), i.Type, i.Name, i.Size, (i.Flag & 0x4000) != 0);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClassStructHelper
|
||||
{
|
||||
public static string ViewStruct(this AssetPreloadData asset)
|
||||
{
|
||||
var reader = asset.Reader;
|
||||
if (asset.sourceFile.ClassStructures.TryGetValue(asset.Type1, out var classStructure))
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
ReadClassStruct(sb, classStructure.members, reader);
|
||||
return sb.ToString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void ReadClassStruct(StringBuilder sb, List<ClassMember> members, EndianBinaryReader reader)
|
||||
{
|
||||
for (int i = 0; i < members.Count; i++)
|
||||
{
|
||||
var member = members[i];
|
||||
var level = member.Level;
|
||||
var varTypeStr = member.Type;
|
||||
var varNameStr = member.Name;
|
||||
object value = null;
|
||||
var align = (member.Flag & 0x4000) != 0;
|
||||
var append = true;
|
||||
if (member.alignBefore)
|
||||
reader.AlignStream(4);
|
||||
switch (varTypeStr)
|
||||
{
|
||||
case "SInt8":
|
||||
value = reader.ReadSByte();
|
||||
break;
|
||||
case "UInt8":
|
||||
value = reader.ReadByte();
|
||||
break;
|
||||
case "short":
|
||||
case "SInt16":
|
||||
value = reader.ReadInt16();
|
||||
break;
|
||||
case "UInt16":
|
||||
case "unsigned short":
|
||||
value = reader.ReadUInt16();
|
||||
break;
|
||||
case "int":
|
||||
case "SInt32":
|
||||
value = reader.ReadInt32();
|
||||
break;
|
||||
case "UInt32":
|
||||
case "unsigned int":
|
||||
case "Type*":
|
||||
value = reader.ReadUInt32();
|
||||
break;
|
||||
case "long long":
|
||||
case "SInt64":
|
||||
value = reader.ReadInt64();
|
||||
break;
|
||||
case "UInt64":
|
||||
case "unsigned long long":
|
||||
value = reader.ReadUInt64();
|
||||
break;
|
||||
case "float":
|
||||
value = reader.ReadSingle();
|
||||
break;
|
||||
case "double":
|
||||
value = reader.ReadDouble();
|
||||
break;
|
||||
case "bool":
|
||||
value = reader.ReadBoolean();
|
||||
break;
|
||||
case "string":
|
||||
append = false;
|
||||
var str = reader.ReadAlignedString(reader.ReadInt32());
|
||||
sb.AppendFormat("{0}{1} {2} = \"{3}\"\r\n", (new string('\t', level)), varTypeStr, varNameStr, str);
|
||||
i += 3;//skip
|
||||
break;
|
||||
case "Array":
|
||||
{
|
||||
append = false;
|
||||
if ((members[i - 1].Flag & 0x4000) != 0)
|
||||
align = true;
|
||||
sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level)), varTypeStr, varNameStr);
|
||||
var size = reader.ReadInt32();
|
||||
sb.AppendFormat("{0}{1} {2} = {3}\r\n", (new string('\t', level)), "int", "size", size);
|
||||
var array = ReadArray(members, level, i);
|
||||
for (int j = 0; j < size; j++)
|
||||
{
|
||||
sb.AppendFormat("{0}[{1}]\r\n", (new string('\t', level + 1)), j);
|
||||
ReadClassStruct(sb, array, reader);
|
||||
}
|
||||
i += array.Count + 1;//skip
|
||||
break;
|
||||
}
|
||||
case "TypelessData":
|
||||
{
|
||||
append = false;
|
||||
var size = reader.ReadInt32();
|
||||
reader.ReadBytes(size);
|
||||
i += 2;
|
||||
sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level)), varTypeStr, varNameStr);
|
||||
sb.AppendFormat("{0}{1} {2} = {3}\r\n", (new string('\t', level)), "int", "size", size);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
append = false;
|
||||
if (align)
|
||||
{
|
||||
align = false;
|
||||
SetAlignBefore(members, level, i + 1);
|
||||
}
|
||||
sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level)), varTypeStr, varNameStr);
|
||||
break;
|
||||
}
|
||||
if (append)
|
||||
sb.AppendFormat("{0}{1} {2} = {3}\r\n", (new string('\t', level)), varTypeStr, varNameStr, value);
|
||||
if (align)
|
||||
reader.AlignStream(4);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<ClassMember> ReadArray(List<ClassMember> members, int level, int index)
|
||||
{
|
||||
var member2 = new List<ClassMember>();
|
||||
for (int i = index + 2; i < members.Count; i++)//skip int size
|
||||
{
|
||||
var member = members[i];
|
||||
var level2 = member.Level;
|
||||
if (level2 <= level)
|
||||
{
|
||||
return member2;
|
||||
}
|
||||
member2.Add(member);
|
||||
}
|
||||
return member2;
|
||||
}
|
||||
|
||||
public static void SetAlignBefore(List<ClassMember> members, int level, int index)
|
||||
{
|
||||
for (int i = index; i < members.Count; i++)
|
||||
{
|
||||
var member = members[i];
|
||||
var level2 = member.Level;
|
||||
if (level2 <= level)
|
||||
{
|
||||
member.alignBefore = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
152
UnityStudio/StudioClasses/EndianBinaryReader.cs
Normal file
152
UnityStudio/StudioClasses/EndianBinaryReader.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
public enum EndianType
|
||||
{
|
||||
BigEndian,
|
||||
LittleEndian
|
||||
}
|
||||
|
||||
public class EndianBinaryReader : BinaryReader
|
||||
{
|
||||
public EndianType endian;
|
||||
private byte[] a16 = new byte[2];
|
||||
private byte[] a32 = new byte[4];
|
||||
private byte[] a64 = new byte[8];
|
||||
|
||||
public EndianBinaryReader(Stream stream, EndianType endian = EndianType.BigEndian)
|
||||
: base(stream)
|
||||
{ this.endian = endian; }
|
||||
|
||||
public long Position
|
||||
{
|
||||
get => BaseStream.Position;
|
||||
set => BaseStream.Position = value;
|
||||
}
|
||||
|
||||
public override short ReadInt16()
|
||||
{
|
||||
if (endian == EndianType.BigEndian)
|
||||
{
|
||||
a16 = ReadBytes(2);
|
||||
Array.Reverse(a16);
|
||||
return BitConverter.ToInt16(a16, 0);
|
||||
}
|
||||
return base.ReadInt16();
|
||||
}
|
||||
|
||||
public override int ReadInt32()
|
||||
{
|
||||
if (endian == EndianType.BigEndian)
|
||||
{
|
||||
a32 = ReadBytes(4);
|
||||
Array.Reverse(a32);
|
||||
return BitConverter.ToInt32(a32, 0);
|
||||
}
|
||||
return base.ReadInt32();
|
||||
}
|
||||
|
||||
public override long ReadInt64()
|
||||
{
|
||||
if (endian == EndianType.BigEndian)
|
||||
{
|
||||
a64 = ReadBytes(8);
|
||||
Array.Reverse(a64);
|
||||
return BitConverter.ToInt64(a64, 0);
|
||||
}
|
||||
return base.ReadInt64();
|
||||
}
|
||||
|
||||
public override ushort ReadUInt16()
|
||||
{
|
||||
if (endian == EndianType.BigEndian)
|
||||
{
|
||||
a16 = ReadBytes(2);
|
||||
Array.Reverse(a16);
|
||||
return BitConverter.ToUInt16(a16, 0);
|
||||
}
|
||||
return base.ReadUInt16();
|
||||
}
|
||||
|
||||
public override uint ReadUInt32()
|
||||
{
|
||||
if (endian == EndianType.BigEndian)
|
||||
{
|
||||
a32 = ReadBytes(4);
|
||||
Array.Reverse(a32);
|
||||
return BitConverter.ToUInt32(a32, 0);
|
||||
}
|
||||
return base.ReadUInt32();
|
||||
}
|
||||
|
||||
public override ulong ReadUInt64()
|
||||
{
|
||||
if (endian == EndianType.BigEndian)
|
||||
{
|
||||
a64 = ReadBytes(8);
|
||||
Array.Reverse(a64);
|
||||
return BitConverter.ToUInt64(a64, 0);
|
||||
}
|
||||
return base.ReadUInt64();
|
||||
}
|
||||
|
||||
public override float ReadSingle()
|
||||
{
|
||||
if (endian == EndianType.BigEndian)
|
||||
{
|
||||
a32 = ReadBytes(4);
|
||||
Array.Reverse(a32);
|
||||
return BitConverter.ToSingle(a32, 0);
|
||||
}
|
||||
return base.ReadSingle();
|
||||
}
|
||||
|
||||
public override double ReadDouble()
|
||||
{
|
||||
if (endian == EndianType.BigEndian)
|
||||
{
|
||||
a64 = ReadBytes(8);
|
||||
Array.Reverse(a64);
|
||||
return BitConverter.ToUInt64(a64, 0);
|
||||
}
|
||||
return base.ReadDouble();
|
||||
}
|
||||
|
||||
public string ReadASCII(int length)
|
||||
{
|
||||
return Encoding.ASCII.GetString(ReadBytes(length));
|
||||
}
|
||||
|
||||
public void AlignStream(int alignment)
|
||||
{
|
||||
var pos = BaseStream.Position;
|
||||
var mod = pos % alignment;
|
||||
if (mod != 0) { BaseStream.Position += alignment - mod; }
|
||||
}
|
||||
|
||||
public string ReadAlignedString(int length)
|
||||
{
|
||||
if (length > 0 && length < (BaseStream.Length - BaseStream.Position))
|
||||
{
|
||||
var stringData = ReadBytes(length);
|
||||
var result = Encoding.UTF8.GetString(stringData);
|
||||
AlignStream(4);
|
||||
return result;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public string ReadStringToNull()
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
byte b;
|
||||
while (BaseStream.Position != BaseStream.Length && (b = ReadByte()) != 0)
|
||||
bytes.Add(b);
|
||||
return Encoding.UTF8.GetString(bytes.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
135
UnityStudio/StudioClasses/Enums.cs
Normal file
135
UnityStudio/StudioClasses/Enums.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
public enum TextureFormat
|
||||
{
|
||||
Alpha8 = 1,
|
||||
ARGB4444,
|
||||
RGB24,
|
||||
RGBA32,
|
||||
ARGB32,
|
||||
RGB565 = 7,
|
||||
R16 = 9,
|
||||
DXT1,
|
||||
DXT5 = 12,
|
||||
RGBA4444,
|
||||
BGRA32,
|
||||
RHalf,
|
||||
RGHalf,
|
||||
RGBAHalf,
|
||||
RFloat,
|
||||
RGFloat,
|
||||
RGBAFloat,
|
||||
YUY2,
|
||||
RGB9e5Float,
|
||||
BC4 = 26,
|
||||
BC5,
|
||||
BC6H = 24,
|
||||
BC7,
|
||||
DXT1Crunched = 28,
|
||||
DXT5Crunched,
|
||||
PVRTC_RGB2,
|
||||
PVRTC_RGBA2,
|
||||
PVRTC_RGB4,
|
||||
PVRTC_RGBA4,
|
||||
ETC_RGB4,
|
||||
ATC_RGB4,
|
||||
ATC_RGBA8,
|
||||
EAC_R = 41,
|
||||
EAC_R_SIGNED,
|
||||
EAC_RG,
|
||||
EAC_RG_SIGNED,
|
||||
ETC2_RGB,
|
||||
ETC2_RGBA1,
|
||||
ETC2_RGBA8,
|
||||
ASTC_RGB_4x4,
|
||||
ASTC_RGB_5x5,
|
||||
ASTC_RGB_6x6,
|
||||
ASTC_RGB_8x8,
|
||||
ASTC_RGB_10x10,
|
||||
ASTC_RGB_12x12,
|
||||
ASTC_RGBA_4x4,
|
||||
ASTC_RGBA_5x5,
|
||||
ASTC_RGBA_6x6,
|
||||
ASTC_RGBA_8x8,
|
||||
ASTC_RGBA_10x10,
|
||||
ASTC_RGBA_12x12,
|
||||
ETC_RGB4_3DS,
|
||||
ETC_RGBA8_3DS,
|
||||
RG16,
|
||||
R8,
|
||||
ETC_RGB4Crunched,
|
||||
ETC2_RGBA8Crunched,
|
||||
}
|
||||
|
||||
public enum AudioType
|
||||
{
|
||||
UNKNOWN,
|
||||
ACC,
|
||||
AIFF,
|
||||
IT = 10,
|
||||
MOD = 12,
|
||||
MPEG,
|
||||
OGGVORBIS,
|
||||
S3M = 17,
|
||||
WAV = 20,
|
||||
XM,
|
||||
XMA,
|
||||
VAG,
|
||||
AUDIOQUEUE
|
||||
}
|
||||
|
||||
public enum AudioCompressionFormat
|
||||
{
|
||||
PCM,
|
||||
Vorbis,
|
||||
ADPCM,
|
||||
MP3,
|
||||
VAG,
|
||||
HEVAG,
|
||||
XMA,
|
||||
AAC,
|
||||
GCADPCM,
|
||||
ATRAC9
|
||||
}
|
||||
|
||||
public enum BuildTarget
|
||||
{
|
||||
StandaloneOSX = 2,
|
||||
StandaloneOSXIntel = 4,
|
||||
StandaloneWindows,
|
||||
WebPlayer,
|
||||
WebPlayerStreamed,
|
||||
iOS = 9,
|
||||
PS3,
|
||||
XBOX360,
|
||||
Android = 13,
|
||||
StandaloneLinux = 17,
|
||||
StandaloneWindows64 = 19,
|
||||
WebGL,
|
||||
WSAPlayer,
|
||||
StandaloneLinux64 = 24,
|
||||
StandaloneLinuxUniversal,
|
||||
WP8Player,
|
||||
StandaloneOSXIntel64,
|
||||
BlackBerry,
|
||||
Tizen,
|
||||
PSP2,
|
||||
PS4,
|
||||
PSM,
|
||||
XboxOne,
|
||||
SamsungTV,
|
||||
N3DS,
|
||||
WiiU,
|
||||
tvOS,
|
||||
Switch,
|
||||
iPhone = -1,
|
||||
BB10 = -1,
|
||||
MetroPlayer = -1,
|
||||
NoTarget = -2
|
||||
}
|
||||
}
|
||||
320
UnityStudio/StudioClasses/Exporter.cs
Normal file
320
UnityStudio/StudioClasses/Exporter.cs
Normal file
@@ -0,0 +1,320 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using static UnityStudio.SpriteHelper;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
static class Exporter
|
||||
{
|
||||
public static bool ExportTexture2D(AssetPreloadData asset, string exportPathName, bool flip)
|
||||
{
|
||||
var m_Texture2D = new Texture2D(asset, true);
|
||||
if (m_Texture2D.image_data == null)
|
||||
return false;
|
||||
var convert = (bool)Properties.Settings.Default["convertTexture"];
|
||||
var bitmap = m_Texture2D.ConvertToBitmap(flip);
|
||||
if (convert && bitmap != null)
|
||||
{
|
||||
ImageFormat format = null;
|
||||
var ext = (string)Properties.Settings.Default["convertType"];
|
||||
switch (ext)
|
||||
{
|
||||
case "BMP":
|
||||
format = ImageFormat.Bmp;
|
||||
break;
|
||||
case "PNG":
|
||||
format = ImageFormat.Png;
|
||||
break;
|
||||
case "JPEG":
|
||||
format = ImageFormat.Jpeg;
|
||||
break;
|
||||
}
|
||||
var exportFullName = exportPathName + asset.Text + "." + ext.ToLower();
|
||||
if (ExportFileExists(exportFullName))
|
||||
return false;
|
||||
bitmap.Save(exportFullName, format);
|
||||
bitmap.Dispose();
|
||||
return true;
|
||||
}
|
||||
if (!convert)
|
||||
{
|
||||
var exportFullName = exportPathName + asset.Text + asset.extension;
|
||||
if (ExportFileExists(exportFullName))
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullName, m_Texture2D.ConvertToContainer());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ExportAudioClip(AssetPreloadData asset, string exportPath)
|
||||
{
|
||||
var m_AudioClip = new AudioClip(asset, true);
|
||||
if (m_AudioClip.m_AudioData == null)
|
||||
return false;
|
||||
var convertAudio = (bool)Properties.Settings.Default["convertAudio"];
|
||||
if (convertAudio && m_AudioClip.IsFMODSupport)
|
||||
{
|
||||
var exportFullName = exportPath + asset.Text + ".wav";
|
||||
if (ExportFileExists(exportFullName))
|
||||
return false;
|
||||
FMOD.CREATESOUNDEXINFO exinfo = new FMOD.CREATESOUNDEXINFO();
|
||||
var result = FMOD.Factory.System_Create(out var system);
|
||||
if (result != FMOD.RESULT.OK)
|
||||
return false;
|
||||
result = system.init(1, FMOD.INITFLAGS.NORMAL, IntPtr.Zero);
|
||||
if (result != FMOD.RESULT.OK)
|
||||
return false;
|
||||
exinfo.cbsize = Marshal.SizeOf(exinfo);
|
||||
exinfo.length = (uint)m_AudioClip.m_Size;
|
||||
result = system.createSound(m_AudioClip.m_AudioData, FMOD.MODE.OPENMEMORY, ref exinfo, out var sound);
|
||||
if (result != FMOD.RESULT.OK)
|
||||
return false;
|
||||
result = sound.getSubSound(0, out var subsound);
|
||||
if (result != FMOD.RESULT.OK)
|
||||
return false;
|
||||
result = subsound.getFormat(out var type, out var format, out int NumChannels, out int BitsPerSample);
|
||||
if (result != FMOD.RESULT.OK)
|
||||
return false;
|
||||
result = subsound.getDefaults(out var frequency, out int priority);
|
||||
if (result != FMOD.RESULT.OK)
|
||||
return false;
|
||||
var SampleRate = (int)frequency;
|
||||
result = subsound.getLength(out var length, FMOD.TIMEUNIT.PCMBYTES);
|
||||
if (result != FMOD.RESULT.OK)
|
||||
return false;
|
||||
result = subsound.@lock(0, length, out var ptr1, out var ptr2, out var len1, out var len2);
|
||||
if (result != FMOD.RESULT.OK)
|
||||
return false;
|
||||
byte[] buffer = new byte[len1 + 44];
|
||||
//添加wav头
|
||||
Encoding.UTF8.GetBytes("RIFF").CopyTo(buffer, 0);
|
||||
BitConverter.GetBytes(len1 + 36).CopyTo(buffer, 4);
|
||||
Encoding.UTF8.GetBytes("WAVEfmt ").CopyTo(buffer, 8);
|
||||
BitConverter.GetBytes(16).CopyTo(buffer, 16);
|
||||
BitConverter.GetBytes((short)1).CopyTo(buffer, 20);
|
||||
BitConverter.GetBytes((short)NumChannels).CopyTo(buffer, 22);
|
||||
BitConverter.GetBytes(SampleRate).CopyTo(buffer, 24);
|
||||
BitConverter.GetBytes(SampleRate * NumChannels * BitsPerSample / 8).CopyTo(buffer, 28);
|
||||
BitConverter.GetBytes((short)(NumChannels * BitsPerSample / 8)).CopyTo(buffer, 32);
|
||||
BitConverter.GetBytes((short)BitsPerSample).CopyTo(buffer, 34);
|
||||
Encoding.UTF8.GetBytes("data").CopyTo(buffer, 36);
|
||||
BitConverter.GetBytes(len1).CopyTo(buffer, 40);
|
||||
Marshal.Copy(ptr1, buffer, 44, (int)len1);
|
||||
File.WriteAllBytes(exportFullName, buffer);
|
||||
result = subsound.unlock(ptr1, ptr2, len1, len2);
|
||||
if (result != FMOD.RESULT.OK)
|
||||
return false;
|
||||
subsound.release();
|
||||
sound.release();
|
||||
system.release();
|
||||
}
|
||||
else
|
||||
{
|
||||
var exportFullName = exportPath + asset.Text + asset.extension;
|
||||
if (ExportFileExists(exportFullName))
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullName, m_AudioClip.m_AudioData);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ExportShader(AssetPreloadData asset, string exportPath)
|
||||
{
|
||||
var m_Shader = new Shader(asset, true);
|
||||
var exportFullName = exportPath + asset.Text + asset.extension;
|
||||
if (ExportFileExists(exportFullName))
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullName, m_Shader.m_Script);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ExportTextAsset(AssetPreloadData asset, string exportPath)
|
||||
{
|
||||
var m_TextAsset = new TextAsset(asset, true);
|
||||
var exportFullName = exportPath + asset.Text + asset.extension;
|
||||
if (ExportFileExists(exportFullName))
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullName, m_TextAsset.m_Script);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ExportMonoBehaviour(AssetPreloadData asset, string exportPath)
|
||||
{
|
||||
var m_MonoBehaviour = new MonoBehaviour(asset, true);
|
||||
var exportFullName = exportPath + asset.Text + asset.extension;
|
||||
if (ExportFileExists(exportFullName))
|
||||
return false;
|
||||
File.WriteAllText(exportFullName, m_MonoBehaviour.serializedText);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ExportFont(AssetPreloadData asset, string exportPath)
|
||||
{
|
||||
var m_Font = new UnityFont(asset, true);
|
||||
if (m_Font.m_FontData != null)
|
||||
{
|
||||
var exportFullName = exportPath + asset.Text + asset.extension;
|
||||
if (ExportFileExists(exportFullName))
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullName, m_Font.m_FontData);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ExportMesh(AssetPreloadData asset, string exportPath)
|
||||
{
|
||||
var m_Mesh = new Mesh(asset, true);
|
||||
if (m_Mesh.m_VertexCount <= 0)
|
||||
return false;
|
||||
var exportFullName = exportPath + asset.Text + asset.extension;
|
||||
if (ExportFileExists(exportFullName))
|
||||
return false;
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("g " + m_Mesh.m_Name);
|
||||
#region Vertices
|
||||
int c = 3;
|
||||
if (m_Mesh.m_Vertices.Length == m_Mesh.m_VertexCount * 4)
|
||||
{
|
||||
c = 4;
|
||||
}
|
||||
for (int v = 0; v < m_Mesh.m_VertexCount; v++)
|
||||
{
|
||||
sb.AppendFormat("v {0} {1} {2}\r\n", -m_Mesh.m_Vertices[v * c], m_Mesh.m_Vertices[v * c + 1], m_Mesh.m_Vertices[v * c + 2]);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region UV
|
||||
if (m_Mesh.m_UV1 != null && m_Mesh.m_UV1.Length == m_Mesh.m_VertexCount * 2)
|
||||
{
|
||||
for (int v = 0; v < m_Mesh.m_VertexCount; v++)
|
||||
{
|
||||
sb.AppendFormat("vt {0} {1}\r\n", m_Mesh.m_UV1[v * 2], m_Mesh.m_UV1[v * 2 + 1]);
|
||||
}
|
||||
}
|
||||
else if (m_Mesh.m_UV2 != null && m_Mesh.m_UV2.Length == m_Mesh.m_VertexCount * 2)
|
||||
{
|
||||
for (int v = 0; v < m_Mesh.m_VertexCount; v++)
|
||||
{
|
||||
sb.AppendFormat("vt {0} {1}\r\n", m_Mesh.m_UV2[v * 2], m_Mesh.m_UV2[v * 2 + 1]);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Normals
|
||||
if (m_Mesh.m_Normals != null && m_Mesh.m_Normals.Length > 0)
|
||||
{
|
||||
if (m_Mesh.m_Normals.Length == m_Mesh.m_VertexCount * 3)
|
||||
{
|
||||
c = 3;
|
||||
}
|
||||
else if (m_Mesh.m_Normals.Length == m_Mesh.m_VertexCount * 4)
|
||||
{
|
||||
c = 4;
|
||||
}
|
||||
for (int v = 0; v < m_Mesh.m_VertexCount; v++)
|
||||
{
|
||||
sb.AppendFormat("vn {0} {1} {2}\r\n", -m_Mesh.m_Normals[v * c], m_Mesh.m_Normals[v * c + 1], m_Mesh.m_Normals[v * c + 2]);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Face
|
||||
int sum = 0;
|
||||
for (var i = 0; i < m_Mesh.m_SubMeshes.Count; i++)
|
||||
{
|
||||
sb.AppendLine($"g {m_Mesh.m_Name}_{i}");
|
||||
int indexCount = (int)m_Mesh.m_SubMeshes[i].indexCount;
|
||||
var end = sum + indexCount / 3;
|
||||
for (int f = sum; f < end; f++)
|
||||
{
|
||||
sb.AppendFormat("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\r\n", m_Mesh.m_Indices[f * 3 + 2] + 1, m_Mesh.m_Indices[f * 3 + 1] + 1, m_Mesh.m_Indices[f * 3] + 1);
|
||||
}
|
||||
sum = end;
|
||||
}
|
||||
#endregion
|
||||
|
||||
sb.Replace("NaN", "0");
|
||||
File.WriteAllText(exportFullName, sb.ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ExportVideoClip(AssetPreloadData asset, string exportPath)
|
||||
{
|
||||
var m_VideoClip = new VideoClip(asset, true);
|
||||
if (m_VideoClip.m_VideoData != null)
|
||||
{
|
||||
var exportFullName = exportPath + asset.Text + asset.extension;
|
||||
if (ExportFileExists(exportFullName))
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullName, m_VideoClip.m_VideoData);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ExportMovieTexture(AssetPreloadData asset, string exportPath)
|
||||
{
|
||||
var m_MovieTexture = new MovieTexture(asset, true);
|
||||
var exportFullName = exportPath + asset.Text + asset.extension;
|
||||
if (ExportFileExists(exportFullName))
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullName, m_MovieTexture.m_MovieData);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ExportSprite(AssetPreloadData asset, string exportPath)
|
||||
{
|
||||
ImageFormat format = null;
|
||||
var type = (string)Properties.Settings.Default["convertType"];
|
||||
switch (type)
|
||||
{
|
||||
case "BMP":
|
||||
format = ImageFormat.Bmp;
|
||||
break;
|
||||
case "PNG":
|
||||
format = ImageFormat.Png;
|
||||
break;
|
||||
case "JPEG":
|
||||
format = ImageFormat.Jpeg;
|
||||
break;
|
||||
}
|
||||
var exportFullName = exportPath + asset.Text + "." + type.ToLower();
|
||||
if (ExportFileExists(exportFullName))
|
||||
return false;
|
||||
var bitmap = GetImageFromSprite(asset);
|
||||
if (bitmap != null)
|
||||
{
|
||||
bitmap.Save(exportFullName, format);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ExportRawFile(AssetPreloadData asset, string exportPath)
|
||||
{
|
||||
var exportFullName = exportPath + asset.Text + asset.extension;
|
||||
if (ExportFileExists(exportFullName))
|
||||
return false;
|
||||
var bytes = asset.Reader.ReadBytes(asset.Size);
|
||||
File.WriteAllBytes(exportFullName, bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ExportFileExists(string filename)
|
||||
{
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filename));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
1137
UnityStudio/StudioClasses/FBXExporter.cs
Normal file
1137
UnityStudio/StudioClasses/FBXExporter.cs
Normal file
File diff suppressed because it is too large
Load Diff
180
UnityStudio/StudioClasses/Importer.cs
Normal file
180
UnityStudio/StudioClasses/Importer.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using static UnityStudio.Studio;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
static class Importer
|
||||
{
|
||||
public static List<string> unityFiles = new List<string>(); //files to load
|
||||
public static HashSet<string> unityFilesHash = new HashSet<string>(); //to improve the loading speed
|
||||
public static HashSet<string> assetsfileListHash = new HashSet<string>(); //to improve the loading speed
|
||||
|
||||
public static void LoadFile(string fullName)
|
||||
{
|
||||
switch (CheckFileType(fullName, out var reader))
|
||||
{
|
||||
case FileType.AssetsFile:
|
||||
LoadAssetsFile(fullName, reader);
|
||||
break;
|
||||
case FileType.BundleFile:
|
||||
LoadBundleFile(fullName, reader);
|
||||
break;
|
||||
case FileType.WebFile:
|
||||
LoadWebFile(fullName, reader);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadAssetsFile(string fullName, EndianBinaryReader reader)
|
||||
{
|
||||
var fileName = Path.GetFileName(fullName);
|
||||
StatusStripUpdate("Loading " + fileName);
|
||||
if (!assetsfileListHash.Contains(fileName.ToUpper()))
|
||||
{
|
||||
var assetsFile = new AssetsFile(fullName, reader);
|
||||
if (assetsFile.valid)
|
||||
{
|
||||
assetsfileList.Add(assetsFile);
|
||||
assetsfileListHash.Add(assetsFile.upperFileName);
|
||||
|
||||
#region for 2.6.x find mainData and get string version
|
||||
if (assetsFile.fileGen == 6 && fileName != "mainData")
|
||||
{
|
||||
var mainDataFile = assetsfileList.Find(aFile => aFile.fileName == "mainData");
|
||||
if (mainDataFile != null)
|
||||
{
|
||||
assetsFile.m_Version = mainDataFile.m_Version;
|
||||
assetsFile.version = mainDataFile.version;
|
||||
assetsFile.buildType = mainDataFile.buildType;
|
||||
}
|
||||
else if (File.Exists(Path.GetDirectoryName(fullName) + "\\mainData"))
|
||||
{
|
||||
mainDataFile = new AssetsFile(Path.GetDirectoryName(fullName) + "\\mainData", new EndianBinaryReader(File.OpenRead(Path.GetDirectoryName(fullName) + "\\mainData")));
|
||||
assetsFile.m_Version = mainDataFile.m_Version;
|
||||
assetsFile.version = mainDataFile.version;
|
||||
assetsFile.buildType = mainDataFile.buildType;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
int value = 0;
|
||||
foreach (var sharedFile in assetsFile.sharedAssetsList)
|
||||
{
|
||||
var sharedFilePath = Path.GetDirectoryName(fullName) + "\\" + sharedFile.fileName;
|
||||
var sharedFileName = sharedFile.fileName;
|
||||
|
||||
if (!unityFilesHash.Contains(sharedFileName.ToUpper()))
|
||||
{
|
||||
if (!File.Exists(sharedFilePath))
|
||||
{
|
||||
var findFiles = Directory.GetFiles(Path.GetDirectoryName(fullName), sharedFileName, SearchOption.AllDirectories);
|
||||
if (findFiles.Length > 0)
|
||||
{
|
||||
sharedFilePath = findFiles[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (File.Exists(sharedFilePath))
|
||||
{
|
||||
unityFiles.Add(sharedFilePath);
|
||||
unityFilesHash.Add(sharedFileName.ToUpper());
|
||||
value++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value > 0)
|
||||
ProgressBarMaximumAdd(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadBundleFile(string fullName, EndianBinaryReader reader)
|
||||
{
|
||||
var fileName = Path.GetFileName(fullName);
|
||||
StatusStripUpdate("Decompressing " + fileName);
|
||||
var bundleFile = new BundleFile(reader);
|
||||
foreach (var file in bundleFile.fileList)
|
||||
{
|
||||
if (!assetsfileListHash.Contains(file.fileName.ToUpper()))
|
||||
{
|
||||
StatusStripUpdate("Loading " + file.fileName);
|
||||
var assetsFile = new AssetsFile(Path.GetDirectoryName(fullName) + "\\" + file.fileName, new EndianBinaryReader(file.stream));
|
||||
if (assetsFile.valid)
|
||||
{
|
||||
assetsFile.bundlePath = fullName;
|
||||
|
||||
if (assetsFile.fileGen == 6) //2.6.x and earlier don't have a string version before the preload table
|
||||
{
|
||||
//make use of the bundle file version
|
||||
assetsFile.m_Version = bundleFile.versionEngine;
|
||||
assetsFile.version = Array.ConvertAll((from Match m in Regex.Matches(assetsFile.m_Version, @"[0-9]") select m.Value).ToArray(), int.Parse);
|
||||
assetsFile.buildType = bundleFile.versionEngine.Split(AssetsFile.buildTypeSplit, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
assetsfileList.Add(assetsFile);
|
||||
assetsfileListHash.Add(assetsFile.upperFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
resourceFileReaders.Add(assetsFile.upperFileName, assetsFile.assetsFileReader);
|
||||
}
|
||||
}
|
||||
}
|
||||
reader.Dispose();
|
||||
}
|
||||
|
||||
private static void LoadWebFile(string fullName, EndianBinaryReader reader)
|
||||
{
|
||||
var fileName = Path.GetFileName(fullName);
|
||||
StatusStripUpdate("Loading " + fileName);
|
||||
var bundleFile = new WebFile(reader);
|
||||
reader.Dispose();
|
||||
foreach (var file in bundleFile.fileList)
|
||||
{
|
||||
var dummyName = Path.GetDirectoryName(fullName) + "\\" + file.fileName;
|
||||
switch (CheckFileType(file.stream, out reader))
|
||||
{
|
||||
case FileType.AssetsFile:
|
||||
LoadAssetsFile(dummyName, reader);
|
||||
break;
|
||||
case FileType.BundleFile:
|
||||
LoadBundleFile(dummyName, reader);
|
||||
break;
|
||||
case FileType.WebFile:
|
||||
LoadWebFile(dummyName, reader);
|
||||
break;
|
||||
}
|
||||
resourceFileReaders.Add(file.fileName.ToUpper(), reader);
|
||||
}
|
||||
}
|
||||
|
||||
public static void MergeSplitAssets(string dirPath)
|
||||
{
|
||||
string[] splitFiles = Directory.GetFiles(dirPath, "*.split0");
|
||||
foreach (var splitFile in splitFiles)
|
||||
{
|
||||
string destFile = Path.GetFileNameWithoutExtension(splitFile);
|
||||
string destPath = Path.GetDirectoryName(splitFile) + "\\";
|
||||
var destFull = destPath + destFile;
|
||||
if (!File.Exists(destFull))
|
||||
{
|
||||
string[] splitParts = Directory.GetFiles(destPath, destFile + ".split*");
|
||||
using (var destStream = File.Create(destFull))
|
||||
{
|
||||
for (int i = 0; i < splitParts.Length; i++)
|
||||
{
|
||||
string splitPart = destFull + ".split" + i;
|
||||
using (var sourceStream = File.OpenRead(splitPart))
|
||||
sourceStream.CopyTo(destStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
90
UnityStudio/StudioClasses/PPtrHelpers.cs
Normal file
90
UnityStudio/StudioClasses/PPtrHelpers.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using static UnityStudio.Studio;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
public class PPtr
|
||||
{
|
||||
//m_FileID 0 means current file
|
||||
public int m_FileID;
|
||||
//m_PathID acts more like a hash in some games
|
||||
public long m_PathID;
|
||||
}
|
||||
|
||||
public static class PPtrHelpers
|
||||
{
|
||||
public static PPtr ReadPPtr(this AssetsFile sourceFile)
|
||||
{
|
||||
var result = new PPtr();
|
||||
var reader = sourceFile.assetsFileReader;
|
||||
|
||||
int FileID = reader.ReadInt32();
|
||||
if (FileID >= 0 && FileID < sourceFile.sharedAssetsList.Count)
|
||||
{
|
||||
var sharedFile = sourceFile.sharedAssetsList[FileID];
|
||||
var index = sharedFile.Index;
|
||||
if (index == -2)
|
||||
{
|
||||
var name = sharedFile.fileName.ToUpper();
|
||||
if (!sharedFileIndex.TryGetValue(name, out index))
|
||||
{
|
||||
index = assetsfileList.FindIndex(aFile => aFile.upperFileName == name);
|
||||
sharedFileIndex.Add(name, index);
|
||||
}
|
||||
sharedFile.Index = index;
|
||||
}
|
||||
result.m_FileID = index;
|
||||
}
|
||||
|
||||
result.m_PathID = sourceFile.fileGen < 14 ? reader.ReadInt32() : reader.ReadInt64();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool TryGetPD(this List<AssetsFile> assetsfileList, PPtr m_elm, out AssetPreloadData result)
|
||||
{
|
||||
result = null;
|
||||
|
||||
if (m_elm != null && m_elm.m_FileID >= 0 && m_elm.m_FileID < assetsfileList.Count)
|
||||
{
|
||||
AssetsFile sourceFile = assetsfileList[m_elm.m_FileID];
|
||||
|
||||
//TryGetValue should be safe because m_PathID is 0 when initialized and PathID values range from 1
|
||||
if (sourceFile.preloadTable.TryGetValue(m_elm.m_PathID, out result)) { return true; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryGetTransform(this List<AssetsFile> assetsfileList, PPtr m_elm, out Transform m_Transform)
|
||||
{
|
||||
m_Transform = null;
|
||||
|
||||
if (m_elm != null && m_elm.m_FileID >= 0 && m_elm.m_FileID < assetsfileList.Count)
|
||||
{
|
||||
AssetsFile sourceFile = assetsfileList[m_elm.m_FileID];
|
||||
|
||||
if (sourceFile.TransformList.TryGetValue(m_elm.m_PathID, out m_Transform)) { return true; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryGetGameObject(this List<AssetsFile> assetsfileList, PPtr m_elm, out GameObject m_GameObject)
|
||||
{
|
||||
m_GameObject = null;
|
||||
|
||||
if (m_elm != null && m_elm.m_FileID >= 0 && m_elm.m_FileID < assetsfileList.Count)
|
||||
{
|
||||
AssetsFile sourceFile = assetsfileList[m_elm.m_FileID];
|
||||
|
||||
if (sourceFile.GameObjectList.TryGetValue(m_elm.m_PathID, out m_GameObject)) { return true; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
108
UnityStudio/StudioClasses/ShaderResource.Designer.cs
generated
Normal file
108
UnityStudio/StudioClasses/ShaderResource.Designer.cs
generated
Normal file
@@ -0,0 +1,108 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 此代码由工具生成。
|
||||
// 运行时版本:4.0.30319.42000
|
||||
//
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果
|
||||
// 重新生成代码,这些更改将会丢失。
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace UnityStudio {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 一个强类型的资源类,用于查找本地化的字符串等。
|
||||
/// </summary>
|
||||
// 此类是由 StronglyTypedResourceBuilder
|
||||
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
||||
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
||||
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class ShaderResource {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal ShaderResource() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回此类使用的缓存的 ResourceManager 实例。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityStudio.StudioClasses.ShaderResource", typeof(ShaderResource).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用此强类型资源类,为所有资源查找
|
||||
/// 重写当前线程的 CurrentUICulture 属性。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 [{"Level":0,"Type":"string","Name":"m_Name","Size":-1,"Flag":32769},{"Level":1,"Type":"Array","Name":"Array","Size":-1,"Flag":16385},{"Level":2,"Type":"int","Name":"size","Size":4,"Flag":1},{"Level":2,"Type":"char","Name":"data","Size":1,"Flag":1},{"Level":0,"Type":"SerializedShader","Name":"m_ParsedForm","Size":-1,"Flag":32768},{"Level":1,"Type":"SerializedProperties","Name":"m_PropInfo","Size":-1,"Flag":32768},{"Level":2,"Type":"vector","Name":"m_Props","Size":-1,"Flag":32768},{"Level":3,"Type":"Arr... 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string Shader20171 {
|
||||
get {
|
||||
return ResourceManager.GetString("Shader20171", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 [{"Level":0,"Type":"string","Name":"m_Name","Size":-1,"Flag":32769},{"Level":1,"Type":"Array","Name":"Array","Size":-1,"Flag":16385},{"Level":2,"Type":"int","Name":"size","Size":4,"Flag":1},{"Level":2,"Type":"char","Name":"data","Size":1,"Flag":1},{"Level":0,"Type":"SerializedShader","Name":"m_ParsedForm","Size":-1,"Flag":32768},{"Level":1,"Type":"SerializedProperties","Name":"m_PropInfo","Size":-1,"Flag":32768},{"Level":2,"Type":"vector","Name":"m_Props","Size":-1,"Flag":32768},{"Level":3,"Type":"Arr... 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string Shader20172 {
|
||||
get {
|
||||
return ResourceManager.GetString("Shader20172", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 [{"Level":0,"Type":"string","Name":"m_Name","Size":-1,"Flag":32769},{"Level":1,"Type":"Array","Name":"Array","Size":-1,"Flag":16385},{"Level":2,"Type":"int","Name":"size","Size":4,"Flag":1},{"Level":2,"Type":"char","Name":"data","Size":1,"Flag":1},{"Level":0,"Type":"SerializedShader","Name":"m_ParsedForm","Size":-1,"Flag":32768},{"Level":1,"Type":"SerializedProperties","Name":"m_PropInfo","Size":-1,"Flag":32768},{"Level":2,"Type":"vector","Name":"m_Props","Size":-1,"Flag":32768},{"Level":3,"Type":"Arr... 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string Shader20173 {
|
||||
get {
|
||||
return ResourceManager.GetString("Shader20173", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 [{"Level":0,"Type":"string","Name":"m_Name","Size":-1,"Flag":32769},{"Level":1,"Type":"Array","Name":"Array","Size":-1,"Flag":16385},{"Level":2,"Type":"int","Name":"size","Size":4,"Flag":1},{"Level":2,"Type":"char","Name":"data","Size":1,"Flag":1},{"Level":0,"Type":"SerializedShader","Name":"m_ParsedForm","Size":-1,"Flag":32768},{"Level":1,"Type":"SerializedProperties","Name":"m_PropInfo","Size":-1,"Flag":32768},{"Level":2,"Type":"vector","Name":"m_Props","Size":-1,"Flag":32768},{"Level":3,"Type":"Arr... 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string Shader55 {
|
||||
get {
|
||||
return ResourceManager.GetString("Shader55", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 [{"Level":0,"Type":"string","Name":"m_Name","Size":-1,"Flag":32769},{"Level":1,"Type":"Array","Name":"Array","Size":-1,"Flag":16385},{"Level":2,"Type":"int","Name":"size","Size":4,"Flag":1},{"Level":2,"Type":"char","Name":"data","Size":1,"Flag":1},{"Level":0,"Type":"SerializedShader","Name":"m_ParsedForm","Size":-1,"Flag":32768},{"Level":1,"Type":"SerializedProperties","Name":"m_PropInfo","Size":-1,"Flag":32768},{"Level":2,"Type":"vector","Name":"m_Props","Size":-1,"Flag":32768},{"Level":3,"Type":"Arr... 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string Shader56 {
|
||||
get {
|
||||
return ResourceManager.GetString("Shader56", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
135
UnityStudio/StudioClasses/ShaderResource.resx
Normal file
135
UnityStudio/StudioClasses/ShaderResource.resx
Normal file
File diff suppressed because one or more lines are too long
102
UnityStudio/StudioClasses/SpriteHelper.cs
Normal file
102
UnityStudio/StudioClasses/SpriteHelper.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using static UnityStudio.Studio;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
static class SpriteHelper
|
||||
{
|
||||
private static Dictionary<AssetPreloadData, Bitmap> spriteCache = new Dictionary<AssetPreloadData, Bitmap>();
|
||||
|
||||
public static Bitmap GetImageFromSprite(AssetPreloadData asset)
|
||||
{
|
||||
if (spriteCache.TryGetValue(asset, out var bitmap))
|
||||
return (Bitmap)bitmap.Clone();
|
||||
var m_Sprite = new Sprite(asset, true);
|
||||
if (assetsfileList.TryGetPD(m_Sprite.m_SpriteAtlas, out var assetPreloadData))
|
||||
{
|
||||
var m_SpriteAtlas = new SpriteAtlas(assetPreloadData);
|
||||
var index = m_SpriteAtlas.guids.FindIndex(x => x == m_Sprite.first);
|
||||
if (index >= 0 && assetsfileList.TryGetPD(m_SpriteAtlas.textures[index], out assetPreloadData))
|
||||
{
|
||||
return CutImage(asset, assetPreloadData, m_SpriteAtlas.textureRects[index], m_Sprite);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (assetsfileList.TryGetPD(m_Sprite.texture, out assetPreloadData))
|
||||
{
|
||||
return CutImage(asset, assetPreloadData, m_Sprite.textureRect);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Bitmap CutImage(AssetPreloadData asset, AssetPreloadData texture2DAsset, RectangleF textureRect)
|
||||
{
|
||||
var texture2D = new Texture2D(texture2DAsset, true);
|
||||
using (var originalImage = texture2D.ConvertToBitmap(false))
|
||||
{
|
||||
if (originalImage != null)
|
||||
{
|
||||
var info = texture2DAsset.InfoText;
|
||||
var start = info.IndexOf("Format");
|
||||
info = info.Substring(start, info.Length - start);
|
||||
asset.InfoText = $"Width: {textureRect.Width}\nHeight: {textureRect.Height}\n" + info;
|
||||
var spriteImage = originalImage.Clone(textureRect, PixelFormat.Format32bppArgb);
|
||||
spriteImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
|
||||
spriteCache.Add(asset, spriteImage);
|
||||
return (Bitmap)spriteImage.Clone();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Bitmap CutImage(AssetPreloadData asset, AssetPreloadData texture2DAsset, RectangleF textureRect, Sprite sprite)
|
||||
{
|
||||
var texture2D = new Texture2D(texture2DAsset, true);
|
||||
using (var originalImage = texture2D.ConvertToBitmap(false))
|
||||
{
|
||||
if (originalImage != null)
|
||||
{
|
||||
var info = texture2DAsset.InfoText;
|
||||
var start = info.IndexOf("Format");
|
||||
info = info.Substring(start, info.Length - start);
|
||||
asset.InfoText = $"Width: {textureRect.Width}\nHeight: {textureRect.Height}\n" + info;
|
||||
var spriteImage = originalImage.Clone(textureRect, PixelFormat.Format32bppArgb);
|
||||
using (var brush = new TextureBrush(spriteImage))
|
||||
{
|
||||
using (var path = new GraphicsPath())
|
||||
{
|
||||
foreach (var p in sprite.m_PhysicsShape)
|
||||
path.AddPolygon(p);
|
||||
using (var matr = new Matrix())
|
||||
{
|
||||
matr.Translate(sprite.m_Rect.Width * sprite.m_Pivot.X, sprite.m_Rect.Height * sprite.m_Pivot.Y);
|
||||
matr.Scale(sprite.m_PixelsToUnits, sprite.m_PixelsToUnits);
|
||||
path.Transform(matr);
|
||||
var bitmap = new Bitmap((int)textureRect.Width, (int)textureRect.Height);
|
||||
using (var graphic = Graphics.FromImage(bitmap))
|
||||
{
|
||||
graphic.FillPath(brush, path);
|
||||
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
|
||||
spriteCache.Add(asset, bitmap);
|
||||
return (Bitmap)bitmap.Clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
UnityStudio/StudioClasses/StringExtensions.cs
Normal file
25
UnityStudio/StudioClasses/StringExtensions.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares the string against a given pattern.
|
||||
/// </summary>
|
||||
/// <param name="str">The string.</param>
|
||||
/// <param name="pattern">The pattern to match, where "*" means any sequence of characters, and "?" means any single character.</param>
|
||||
/// <returns><c>true</c> if the string matches the given pattern; otherwise <c>false</c>.</returns>
|
||||
public static bool Like(this string str, string pattern)
|
||||
{
|
||||
return new Regex(
|
||||
"^" + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\?", ".") + "$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Singleline
|
||||
).IsMatch(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
426
UnityStudio/StudioClasses/Studio.cs
Normal file
426
UnityStudio/StudioClasses/Studio.cs
Normal file
@@ -0,0 +1,426 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web.Script.Serialization;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
internal static class Studio
|
||||
{
|
||||
public static List<AssetsFile> assetsfileList = new List<AssetsFile>(); //loaded files
|
||||
public static Dictionary<string, int> sharedFileIndex = new Dictionary<string, int>(); //to improve the loading speed
|
||||
public static Dictionary<string, EndianBinaryReader> resourceFileReaders = new Dictionary<string, EndianBinaryReader>(); //use for read res files
|
||||
public static List<AssetPreloadData> exportableAssets = new List<AssetPreloadData>(); //used to hold all assets while the ListView is filtered
|
||||
private static HashSet<string> exportableAssetsHash = new HashSet<string>(); //avoid the same name asset
|
||||
public static List<AssetPreloadData> visibleAssets = new List<AssetPreloadData>(); //used to build the ListView from all or filtered assets
|
||||
|
||||
public static string productName = "";
|
||||
public static string mainPath = "";
|
||||
public static List<GameObject> fileNodes = new List<GameObject>();
|
||||
|
||||
public static Dictionary<string, Dictionary<string, string>> jsonMats;
|
||||
public static Dictionary<string, SortedDictionary<int, ClassStruct>> AllClassStructures = new Dictionary<string, SortedDictionary<int, ClassStruct>>();
|
||||
|
||||
//UI
|
||||
public static Action<int> SetProgressBarValue;
|
||||
public static Action<int> SetProgressBarMaximum;
|
||||
public static Action ProgressBarPerformStep;
|
||||
public static Action<string> StatusStripUpdate;
|
||||
public static Action<int> ProgressBarMaximumAdd;
|
||||
|
||||
public enum FileType
|
||||
{
|
||||
AssetsFile,
|
||||
BundleFile,
|
||||
WebFile
|
||||
}
|
||||
|
||||
public static int ExtractBundleFile(string bundleFileName)
|
||||
{
|
||||
int extractedCount = 0;
|
||||
if (CheckFileType(bundleFileName, out var reader) == FileType.BundleFile)
|
||||
{
|
||||
StatusStripUpdate($"Decompressing {Path.GetFileName(bundleFileName)} ...");
|
||||
var extractPath = bundleFileName + "_unpacked\\";
|
||||
Directory.CreateDirectory(extractPath);
|
||||
var bundleFile = new BundleFile(reader);
|
||||
foreach (var memFile in bundleFile.fileList)
|
||||
{
|
||||
var filePath = extractPath + memFile.fileName.Replace('/', '\\');
|
||||
if (!Directory.Exists(Path.GetDirectoryName(filePath)))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
|
||||
}
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
StatusStripUpdate($"Extracting {Path.GetFileName(memFile.fileName)}");
|
||||
extractedCount += 1;
|
||||
using (var file = File.Create(filePath))
|
||||
{
|
||||
memFile.stream.WriteTo(file);
|
||||
memFile.stream.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reader.Dispose();
|
||||
return extractedCount;
|
||||
}
|
||||
|
||||
public static void BuildAssetStructures(bool loadAssetsMenuItem, bool displayAll, bool buildHierarchyMenuItem, bool buildClassStructuresMenuItem, bool displayOriginalName)
|
||||
{
|
||||
#region first loop - read asset data & create list
|
||||
if (loadAssetsMenuItem)
|
||||
{
|
||||
SetProgressBarValue(0);
|
||||
SetProgressBarMaximum(assetsfileList.Sum(x => x.preloadTable.Values.Count));
|
||||
string fileIDfmt = "D" + assetsfileList.Count.ToString().Length;
|
||||
|
||||
for (var i = 0; i < assetsfileList.Count; i++)
|
||||
{
|
||||
var assetsFile = assetsfileList[i];
|
||||
StatusStripUpdate("Building asset list from " + Path.GetFileName(assetsFile.filePath));
|
||||
|
||||
string fileID = i.ToString(fileIDfmt);
|
||||
AssetBundle ab = null;
|
||||
foreach (var asset in assetsFile.preloadTable.Values)
|
||||
{
|
||||
asset.uniqueID = fileID + asset.uniqueID;
|
||||
var exportable = false;
|
||||
switch (asset.Type2)
|
||||
{
|
||||
case 1: //GameObject
|
||||
{
|
||||
GameObject m_GameObject = new GameObject(asset);
|
||||
assetsFile.GameObjectList.Add(asset.m_PathID, m_GameObject);
|
||||
//totalTreeNodes++;
|
||||
break;
|
||||
}
|
||||
case 4: //Transform
|
||||
{
|
||||
Transform m_Transform = new Transform(asset);
|
||||
assetsFile.TransformList.Add(asset.m_PathID, m_Transform);
|
||||
break;
|
||||
}
|
||||
case 224: //RectTransform
|
||||
{
|
||||
RectTransform m_Rect = new RectTransform(asset);
|
||||
assetsFile.TransformList.Add(asset.m_PathID, m_Rect.m_Transform);
|
||||
break;
|
||||
}
|
||||
case 28: //Texture2D
|
||||
{
|
||||
Texture2D m_Texture2D = new Texture2D(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case 48: //Shader
|
||||
{
|
||||
Shader m_Shader = new Shader(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case 49: //TextAsset
|
||||
{
|
||||
TextAsset m_TextAsset = new TextAsset(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case 83: //AudioClip
|
||||
{
|
||||
AudioClip m_AudioClip = new AudioClip(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case 114: //MonoBehaviour
|
||||
{
|
||||
var m_MonoBehaviour = new MonoBehaviour(asset, false);
|
||||
if (asset.Type1 != asset.Type2 && assetsFile.ClassStructures.ContainsKey(asset.Type1))
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case 128: //Font
|
||||
{
|
||||
UnityFont m_Font = new UnityFont(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case 129: //PlayerSettings
|
||||
{
|
||||
var plSet = new PlayerSettings(asset);
|
||||
productName = plSet.productName;
|
||||
break;
|
||||
}
|
||||
case 43: //Mesh
|
||||
{
|
||||
Mesh m_Mesh = new Mesh(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case 142: //AssetBundle
|
||||
{
|
||||
ab = new AssetBundle(asset);
|
||||
break;
|
||||
}
|
||||
case 329: //VideoClip
|
||||
{
|
||||
var m_VideoClip = new VideoClip(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case 152: //MovieTexture
|
||||
{
|
||||
var m_MovieTexture = new MovieTexture(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case 213: //Sprite
|
||||
{
|
||||
var m_Sprite = new Sprite(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
/*case 21: //Material
|
||||
case 74: //AnimationClip
|
||||
case 90: //Avatar
|
||||
case 91: //AnimatorController
|
||||
case 115: //MonoScript
|
||||
case 687078895: //SpriteAtlas
|
||||
{
|
||||
if (asset.Offset + 4 > asset.sourceFile.a_Stream.BaseStream.Length)
|
||||
break;
|
||||
asset.sourceFile.a_Stream.Position = asset.Offset;
|
||||
var len = asset.sourceFile.a_Stream.ReadInt32();
|
||||
if (len > 0 && len < asset.Size - 4)
|
||||
{
|
||||
var bytes = asset.sourceFile.a_Stream.ReadBytes(len);
|
||||
asset.Text = Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
if (!exportable && displayAll)
|
||||
{
|
||||
asset.extension = ".dat";
|
||||
exportable = true;
|
||||
}
|
||||
if (exportable)
|
||||
{
|
||||
if (asset.Text == "")
|
||||
{
|
||||
asset.Text = asset.TypeString + " #" + asset.uniqueID;
|
||||
}
|
||||
asset.SubItems.AddRange(new[] { asset.TypeString, asset.fullSize.ToString() });
|
||||
//处理同名文件
|
||||
if (!exportableAssetsHash.Add((asset.TypeString + asset.Text).ToUpper()))
|
||||
{
|
||||
asset.Text += " #" + asset.uniqueID;
|
||||
}
|
||||
//处理非法文件名
|
||||
asset.Text = FixFileName(asset.Text);
|
||||
assetsFile.exportableAssets.Add(asset);
|
||||
}
|
||||
ProgressBarPerformStep();
|
||||
}
|
||||
if (displayOriginalName)
|
||||
{
|
||||
assetsFile.exportableAssets.ForEach(x =>
|
||||
{
|
||||
var replacename = ab?.m_Container.Find(y => y.second.asset.m_PathID == x.m_PathID)?.first;
|
||||
if (!string.IsNullOrEmpty(replacename))
|
||||
{
|
||||
var ex = Path.GetExtension(replacename);
|
||||
x.Text = !string.IsNullOrEmpty(ex) ? replacename.Replace(ex, "") : replacename;
|
||||
}
|
||||
});
|
||||
}
|
||||
exportableAssets.AddRange(assetsFile.exportableAssets);
|
||||
}
|
||||
|
||||
visibleAssets = exportableAssets;
|
||||
exportableAssetsHash.Clear();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region second loop - build tree structure
|
||||
fileNodes = new List<GameObject>();
|
||||
if (buildHierarchyMenuItem)
|
||||
{
|
||||
SetProgressBarMaximum(1);
|
||||
SetProgressBarValue(1);
|
||||
SetProgressBarMaximum(assetsfileList.Sum(x => x.GameObjectList.Values.Count) + 1);
|
||||
foreach (var assetsFile in assetsfileList)
|
||||
{
|
||||
StatusStripUpdate("Building tree structure from " + Path.GetFileName(assetsFile.filePath));
|
||||
GameObject fileNode = new GameObject(null);
|
||||
fileNode.Text = Path.GetFileName(assetsFile.filePath);
|
||||
fileNode.m_Name = "RootNode";
|
||||
|
||||
foreach (var m_GameObject in assetsFile.GameObjectList.Values)
|
||||
{
|
||||
//ParseGameObject
|
||||
foreach (var m_Component in m_GameObject.m_Components)
|
||||
{
|
||||
if (m_Component.m_FileID >= 0 && m_Component.m_FileID < assetsfileList.Count)
|
||||
{
|
||||
var sourceFile = assetsfileList[m_Component.m_FileID];
|
||||
if (sourceFile.preloadTable.TryGetValue(m_Component.m_PathID, out var asset))
|
||||
{
|
||||
switch (asset.Type2)
|
||||
{
|
||||
case 4: //Transform
|
||||
{
|
||||
m_GameObject.m_Transform = m_Component;
|
||||
break;
|
||||
}
|
||||
case 23: //MeshRenderer
|
||||
{
|
||||
m_GameObject.m_MeshRenderer = m_Component;
|
||||
break;
|
||||
}
|
||||
case 33: //MeshFilter
|
||||
{
|
||||
m_GameObject.m_MeshFilter = m_Component;
|
||||
break;
|
||||
}
|
||||
case 137: //SkinnedMeshRenderer
|
||||
{
|
||||
m_GameObject.m_SkinnedMeshRenderer = m_Component;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
var parentNode = fileNode;
|
||||
|
||||
if (assetsfileList.TryGetTransform(m_GameObject.m_Transform, out var m_Transform))
|
||||
{
|
||||
if (assetsfileList.TryGetTransform(m_Transform.m_Father, out var m_Father))
|
||||
{
|
||||
//GameObject Parent;
|
||||
if (assetsfileList.TryGetGameObject(m_Father.m_GameObject, out parentNode))
|
||||
{
|
||||
//parentNode = Parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parentNode.Nodes.Add(m_GameObject);
|
||||
ProgressBarPerformStep();
|
||||
}
|
||||
|
||||
|
||||
if (fileNode.Nodes.Count == 0)
|
||||
{
|
||||
fileNode.Text += " (no children)";
|
||||
}
|
||||
fileNodes.Add(fileNode);
|
||||
}
|
||||
|
||||
if (File.Exists(mainPath + "\\materials.json"))
|
||||
{
|
||||
string matLine;
|
||||
using (StreamReader reader = File.OpenText(mainPath + "\\materials.json"))
|
||||
{ matLine = reader.ReadToEnd(); }
|
||||
|
||||
jsonMats = new JavaScriptSerializer().Deserialize<Dictionary<string, Dictionary<string, string>>>(matLine);
|
||||
//var jsonMats = new JavaScriptSerializer().DeserializeObject(matLine);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region build list of class strucutres
|
||||
if (buildClassStructuresMenuItem)
|
||||
{
|
||||
//group class structures by versionv
|
||||
foreach (var assetsFile in assetsfileList)
|
||||
{
|
||||
if (AllClassStructures.TryGetValue(assetsFile.m_Version, out var curVer))
|
||||
{
|
||||
foreach (var uClass in assetsFile.ClassStructures)
|
||||
{
|
||||
curVer[uClass.Key] = uClass.Value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AllClassStructures.Add(assetsFile.m_Version, assetsFile.ClassStructures);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static string FixFileName(string str)
|
||||
{
|
||||
if (str.Length >= 260) return Path.GetRandomFileName();
|
||||
return Path.GetInvalidFileNameChars().Aggregate(str, (current, c) => current.Replace(c, '_'));
|
||||
}
|
||||
|
||||
public static FileType CheckFileType(MemoryStream stream, out EndianBinaryReader reader)
|
||||
{
|
||||
reader = new EndianBinaryReader(stream);
|
||||
return CheckFileType(reader);
|
||||
}
|
||||
|
||||
public static FileType CheckFileType(string fileName, out EndianBinaryReader reader)
|
||||
{
|
||||
reader = new EndianBinaryReader(File.OpenRead(fileName));
|
||||
return CheckFileType(reader);
|
||||
}
|
||||
|
||||
public static FileType CheckFileType(EndianBinaryReader reader)
|
||||
{
|
||||
var signature = reader.ReadStringToNull();
|
||||
reader.Position = 0;
|
||||
switch (signature)
|
||||
{
|
||||
case "UnityWeb":
|
||||
case "UnityRaw":
|
||||
case "\xFA\xFA\xFA\xFA\xFA\xFA\xFA\xFA":
|
||||
case "UnityFS":
|
||||
return FileType.BundleFile;
|
||||
case "UnityWebData1.0":
|
||||
return FileType.WebFile;
|
||||
default:
|
||||
{
|
||||
var magic = reader.ReadBytes(2);
|
||||
reader.Position = 0;
|
||||
if (WebFile.gzipMagic.SequenceEqual(magic))
|
||||
{
|
||||
return FileType.WebFile;
|
||||
}
|
||||
reader.Position = 0x20;
|
||||
magic = reader.ReadBytes(6);
|
||||
reader.Position = 0;
|
||||
if (WebFile.brotliMagic.SequenceEqual(magic))
|
||||
{
|
||||
return FileType.WebFile;
|
||||
}
|
||||
return FileType.AssetsFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string[] ProcessingSplitFiles(List<string> selectFile)
|
||||
{
|
||||
var splitFiles = selectFile.Where(x => x.Contains(".split"))
|
||||
.Select(x => Path.GetDirectoryName(x) + "\\" + Path.GetFileNameWithoutExtension(x))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
selectFile.RemoveAll(x => x.Contains(".split"));
|
||||
foreach (var file in splitFiles)
|
||||
{
|
||||
if (File.Exists(file))
|
||||
{
|
||||
selectFile.Add(file);
|
||||
}
|
||||
}
|
||||
return selectFile.Distinct().ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
540
UnityStudio/StudioClasses/Texture2DConverter.cs
Normal file
540
UnityStudio/StudioClasses/Texture2DConverter.cs
Normal file
@@ -0,0 +1,540 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
partial class Texture2D
|
||||
{
|
||||
[DllImport("PVRTexLibWrapper.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool DecompressPVR(byte[] buffer, IntPtr bmp, int len);
|
||||
|
||||
[DllImport("TextureConverterWrapper.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool Ponvert(byte[] buffer, IntPtr bmp, int nWidth, int nHeight, int len, int type, int bmpsize, bool fixAlpha);
|
||||
|
||||
[DllImport("crunch.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool DecompressCRN(byte[] pSrc_file_data, int src_file_size, out IntPtr uncompressedData, out int uncompressedSize);
|
||||
|
||||
[DllImport("crunchunity.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool DecompressUnityCRN(byte[] pSrc_file_data, int src_file_size, out IntPtr uncompressedData, out int uncompressedSize);
|
||||
|
||||
[DllImport("texgenpack.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void texgenpackdecode(int texturetype, byte[] texturedata, int width, int height, IntPtr bmp, bool fixAlpha);
|
||||
|
||||
|
||||
public byte[] ConvertToContainer()
|
||||
{
|
||||
if (image_data == null || image_data.Length == 0)
|
||||
return null;
|
||||
switch (m_TextureFormat)
|
||||
{
|
||||
case TextureFormat.Alpha8:
|
||||
case TextureFormat.ARGB4444:
|
||||
case TextureFormat.RGB24:
|
||||
case TextureFormat.RGBA32:
|
||||
case TextureFormat.ARGB32:
|
||||
case TextureFormat.RGB565:
|
||||
case TextureFormat.R16:
|
||||
case TextureFormat.DXT1:
|
||||
case TextureFormat.DXT5:
|
||||
case TextureFormat.RGBA4444:
|
||||
case TextureFormat.BGRA32:
|
||||
case TextureFormat.RG16:
|
||||
case TextureFormat.R8:
|
||||
return ConvertToDDS();
|
||||
case TextureFormat.YUY2:
|
||||
case TextureFormat.PVRTC_RGB2:
|
||||
case TextureFormat.PVRTC_RGBA2:
|
||||
case TextureFormat.PVRTC_RGB4:
|
||||
case TextureFormat.PVRTC_RGBA4:
|
||||
case TextureFormat.ETC_RGB4:
|
||||
case TextureFormat.ETC2_RGB:
|
||||
case TextureFormat.ETC2_RGBA1:
|
||||
case TextureFormat.ETC2_RGBA8:
|
||||
case TextureFormat.ASTC_RGB_4x4:
|
||||
case TextureFormat.ASTC_RGB_5x5:
|
||||
case TextureFormat.ASTC_RGB_6x6:
|
||||
case TextureFormat.ASTC_RGB_8x8:
|
||||
case TextureFormat.ASTC_RGB_10x10:
|
||||
case TextureFormat.ASTC_RGB_12x12:
|
||||
case TextureFormat.ASTC_RGBA_4x4:
|
||||
case TextureFormat.ASTC_RGBA_5x5:
|
||||
case TextureFormat.ASTC_RGBA_6x6:
|
||||
case TextureFormat.ASTC_RGBA_8x8:
|
||||
case TextureFormat.ASTC_RGBA_10x10:
|
||||
case TextureFormat.ASTC_RGBA_12x12:
|
||||
case TextureFormat.ETC_RGB4_3DS:
|
||||
case TextureFormat.ETC_RGBA8_3DS:
|
||||
return ConvertToPVR();
|
||||
case TextureFormat.RHalf:
|
||||
case TextureFormat.RGHalf:
|
||||
case TextureFormat.RGBAHalf:
|
||||
case TextureFormat.RFloat:
|
||||
case TextureFormat.RGFloat:
|
||||
case TextureFormat.RGBAFloat:
|
||||
case TextureFormat.BC4:
|
||||
case TextureFormat.BC5:
|
||||
case TextureFormat.BC6H:
|
||||
case TextureFormat.BC7:
|
||||
case TextureFormat.ATC_RGB4:
|
||||
case TextureFormat.ATC_RGBA8:
|
||||
case TextureFormat.EAC_R:
|
||||
case TextureFormat.EAC_R_SIGNED:
|
||||
case TextureFormat.EAC_RG:
|
||||
case TextureFormat.EAC_RG_SIGNED:
|
||||
return ConvertToKTX();
|
||||
default:
|
||||
return image_data;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] ConvertToDDS()
|
||||
{
|
||||
var imageBuffer = new byte[128 + image_data_size];
|
||||
dwMagic.CopyTo(imageBuffer, 0);
|
||||
BitConverter.GetBytes(dwFlags).CopyTo(imageBuffer, 8);
|
||||
BitConverter.GetBytes(m_Height).CopyTo(imageBuffer, 12);
|
||||
BitConverter.GetBytes(m_Width).CopyTo(imageBuffer, 16);
|
||||
BitConverter.GetBytes(dwPitchOrLinearSize).CopyTo(imageBuffer, 20);
|
||||
BitConverter.GetBytes(dwMipMapCount).CopyTo(imageBuffer, 28);
|
||||
BitConverter.GetBytes(dwSize).CopyTo(imageBuffer, 76);
|
||||
BitConverter.GetBytes(dwFlags2).CopyTo(imageBuffer, 80);
|
||||
BitConverter.GetBytes(dwFourCC).CopyTo(imageBuffer, 84);
|
||||
BitConverter.GetBytes(dwRGBBitCount).CopyTo(imageBuffer, 88);
|
||||
BitConverter.GetBytes(dwRBitMask).CopyTo(imageBuffer, 92);
|
||||
BitConverter.GetBytes(dwGBitMask).CopyTo(imageBuffer, 96);
|
||||
BitConverter.GetBytes(dwBBitMask).CopyTo(imageBuffer, 100);
|
||||
BitConverter.GetBytes(dwABitMask).CopyTo(imageBuffer, 104);
|
||||
BitConverter.GetBytes(dwCaps).CopyTo(imageBuffer, 108);
|
||||
BitConverter.GetBytes(dwCaps2).CopyTo(imageBuffer, 112);
|
||||
image_data.CopyTo(imageBuffer, 128);
|
||||
return imageBuffer;
|
||||
}
|
||||
|
||||
private byte[] ConvertToPVR()
|
||||
{
|
||||
var mstream = new MemoryStream();
|
||||
using (var writer = new BinaryWriter(mstream))
|
||||
{
|
||||
writer.Write(pvrVersion);
|
||||
writer.Write(pvrFlags);
|
||||
writer.Write(pvrPixelFormat);
|
||||
writer.Write(pvrColourSpace);
|
||||
writer.Write(pvrChannelType);
|
||||
writer.Write(m_Height);
|
||||
writer.Write(m_Width);
|
||||
writer.Write(pvrDepth);
|
||||
writer.Write(pvrNumSurfaces);
|
||||
writer.Write(pvrNumFaces);
|
||||
writer.Write(dwMipMapCount);
|
||||
writer.Write(pvrMetaDataSize);
|
||||
writer.Write(image_data);
|
||||
return mstream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] ConvertToKTX()
|
||||
{
|
||||
var mstream = new MemoryStream();
|
||||
using (var writer = new BinaryWriter(mstream))
|
||||
{
|
||||
writer.Write(KTXHeader.IDENTIFIER);
|
||||
writer.Write(KTXHeader.ENDIANESS_LE);
|
||||
writer.Write(glType);
|
||||
writer.Write(glTypeSize);
|
||||
writer.Write(glFormat);
|
||||
writer.Write(glInternalFormat);
|
||||
writer.Write(glBaseInternalFormat);
|
||||
writer.Write(m_Width);
|
||||
writer.Write(m_Height);
|
||||
writer.Write(pixelDepth);
|
||||
writer.Write(numberOfArrayElements);
|
||||
writer.Write(numberOfFaces);
|
||||
writer.Write(numberOfMipmapLevels);
|
||||
writer.Write(bytesOfKeyValueData);
|
||||
writer.Write(image_data_size);
|
||||
writer.Write(image_data);
|
||||
return mstream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap ConvertToBitmap(bool flip)
|
||||
{
|
||||
if (image_data == null || image_data.Length == 0)
|
||||
return null;
|
||||
Bitmap bitmap;
|
||||
switch (m_TextureFormat)
|
||||
{
|
||||
case TextureFormat.Alpha8:
|
||||
case TextureFormat.ARGB4444:
|
||||
case TextureFormat.RGB24:
|
||||
case TextureFormat.RGBA32:
|
||||
case TextureFormat.ARGB32:
|
||||
case TextureFormat.R16:
|
||||
case TextureFormat.RGBA4444:
|
||||
case TextureFormat.BGRA32:
|
||||
case TextureFormat.RG16:
|
||||
case TextureFormat.R8:
|
||||
bitmap = BGRA32ToBitmap();
|
||||
break;
|
||||
case TextureFormat.RGB565:
|
||||
bitmap = RGB565ToBitmap();
|
||||
break;
|
||||
case TextureFormat.YUY2:
|
||||
case TextureFormat.PVRTC_RGB2:
|
||||
case TextureFormat.PVRTC_RGBA2:
|
||||
case TextureFormat.PVRTC_RGB4:
|
||||
case TextureFormat.PVRTC_RGBA4:
|
||||
case TextureFormat.ETC_RGB4:
|
||||
case TextureFormat.ETC2_RGB:
|
||||
case TextureFormat.ETC2_RGBA1:
|
||||
case TextureFormat.ETC2_RGBA8:
|
||||
case TextureFormat.ASTC_RGB_4x4:
|
||||
case TextureFormat.ASTC_RGB_5x5:
|
||||
case TextureFormat.ASTC_RGB_6x6:
|
||||
case TextureFormat.ASTC_RGB_8x8:
|
||||
case TextureFormat.ASTC_RGB_10x10:
|
||||
case TextureFormat.ASTC_RGB_12x12:
|
||||
case TextureFormat.ASTC_RGBA_4x4:
|
||||
case TextureFormat.ASTC_RGBA_5x5:
|
||||
case TextureFormat.ASTC_RGBA_6x6:
|
||||
case TextureFormat.ASTC_RGBA_8x8:
|
||||
case TextureFormat.ASTC_RGBA_10x10:
|
||||
case TextureFormat.ASTC_RGBA_12x12:
|
||||
case TextureFormat.ETC_RGB4_3DS:
|
||||
case TextureFormat.ETC_RGBA8_3DS:
|
||||
bitmap = PVRToBitmap(ConvertToPVR());
|
||||
break;
|
||||
case TextureFormat.DXT1:
|
||||
case TextureFormat.DXT5:
|
||||
case TextureFormat.RHalf:
|
||||
case TextureFormat.RGHalf:
|
||||
case TextureFormat.RGBAHalf:
|
||||
case TextureFormat.RFloat:
|
||||
case TextureFormat.RGFloat:
|
||||
case TextureFormat.RGBAFloat:
|
||||
case TextureFormat.RGB9e5Float:
|
||||
case TextureFormat.ATC_RGB4:
|
||||
case TextureFormat.ATC_RGBA8:
|
||||
case TextureFormat.EAC_R:
|
||||
case TextureFormat.EAC_R_SIGNED:
|
||||
case TextureFormat.EAC_RG:
|
||||
case TextureFormat.EAC_RG_SIGNED:
|
||||
bitmap = TextureConverter();
|
||||
break;
|
||||
case TextureFormat.BC4:
|
||||
case TextureFormat.BC5:
|
||||
case TextureFormat.BC6H:
|
||||
case TextureFormat.BC7:
|
||||
bitmap = Texgenpack();
|
||||
break;
|
||||
case TextureFormat.DXT1Crunched:
|
||||
case TextureFormat.DXT5Crunched:
|
||||
DecompressCRN();
|
||||
bitmap = TextureConverter();
|
||||
break;
|
||||
case TextureFormat.ETC_RGB4Crunched:
|
||||
case TextureFormat.ETC2_RGBA8Crunched:
|
||||
DecompressCRN();
|
||||
bitmap = PVRToBitmap(ConvertToPVR());
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
if (bitmap != null && flip)
|
||||
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private Bitmap BGRA32ToBitmap()
|
||||
{
|
||||
var hObject = GCHandle.Alloc(image_data, GCHandleType.Pinned);
|
||||
var pObject = hObject.AddrOfPinnedObject();
|
||||
var bitmap = new Bitmap(m_Width, m_Height, m_Width * 4, PixelFormat.Format32bppArgb, pObject);
|
||||
hObject.Free();
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private Bitmap RGB565ToBitmap()
|
||||
{
|
||||
//stride = m_Width * 2 + m_Width * 2 % 4
|
||||
//所以m_Width * 2不为4的倍数时,需要在每行补上相应的像素
|
||||
byte[] buff;
|
||||
var padding = m_Width * 2 % 4;
|
||||
var stride = m_Width * 2 + padding;
|
||||
if (padding != 0)
|
||||
{
|
||||
buff = new byte[stride * m_Height];
|
||||
for (int i = 0; i < m_Height; i++)
|
||||
{
|
||||
Array.Copy(image_data, i * m_Width * 2, buff, i * stride, m_Width * 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buff = image_data;
|
||||
}
|
||||
var hObject = GCHandle.Alloc(buff, GCHandleType.Pinned);
|
||||
var pObject = hObject.AddrOfPinnedObject();
|
||||
var bitmap = new Bitmap(m_Width, m_Height, stride, PixelFormat.Format16bppRgb565, pObject);
|
||||
hObject.Free();
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private Bitmap PVRToBitmap(byte[] pvrdata)
|
||||
{
|
||||
var bitmap = new Bitmap(m_Width, m_Height);
|
||||
var rect = new Rectangle(0, 0, m_Width, m_Height);
|
||||
var bmd = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
var len = Math.Abs(bmd.Stride) * bmd.Height;
|
||||
if (!DecompressPVR(pvrdata, bmd.Scan0, len))
|
||||
{
|
||||
bitmap.UnlockBits(bmd);
|
||||
bitmap.Dispose();
|
||||
return null;
|
||||
}
|
||||
bitmap.UnlockBits(bmd);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private Bitmap TextureConverter()
|
||||
{
|
||||
var bitmap = new Bitmap(m_Width, m_Height);
|
||||
var rect = new Rectangle(0, 0, m_Width, m_Height);
|
||||
var bmd = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
var len = Math.Abs(bmd.Stride) * bmd.Height;
|
||||
var fixAlpha = glBaseInternalFormat == KTXHeader.GL_RED || glBaseInternalFormat == KTXHeader.GL_RG;
|
||||
if (!Ponvert(image_data, bmd.Scan0, m_Width, m_Height, image_data_size, (int)q_format, len, fixAlpha))
|
||||
{
|
||||
bitmap.UnlockBits(bmd);
|
||||
bitmap.Dispose();
|
||||
return null;
|
||||
}
|
||||
bitmap.UnlockBits(bmd);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private void DecompressCRN()
|
||||
{
|
||||
IntPtr uncompressedData;
|
||||
int uncompressedSize;
|
||||
bool result;
|
||||
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)) //2017.3 and up
|
||||
{
|
||||
result = DecompressUnityCRN(image_data, image_data_size, out uncompressedData, out uncompressedSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = DecompressCRN(image_data, image_data_size, out uncompressedData, out uncompressedSize);
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
var uncompressedBytes = new byte[uncompressedSize];
|
||||
Marshal.Copy(uncompressedData, uncompressedBytes, 0, uncompressedSize);
|
||||
Marshal.FreeHGlobal(uncompressedData);
|
||||
image_data = uncompressedBytes;
|
||||
image_data_size = uncompressedSize;
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap Texgenpack()
|
||||
{
|
||||
var bitmap = new Bitmap(m_Width, m_Height);
|
||||
var rect = new Rectangle(0, 0, m_Width, m_Height);
|
||||
var bmd = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
var fixAlpha = glBaseInternalFormat == KTXHeader.GL_RED || glBaseInternalFormat == KTXHeader.GL_RG;
|
||||
texgenpackdecode((int)texturetype, image_data, m_Width, m_Height, bmd.Scan0, fixAlpha);
|
||||
bitmap.UnlockBits(bmd);
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
public static class KTXHeader
|
||||
{
|
||||
public static byte[] IDENTIFIER = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
|
||||
public static byte[] ENDIANESS_LE = { 1, 2, 3, 4 };
|
||||
public static byte[] ENDIANESS_BE = { 4, 3, 2, 1 };
|
||||
|
||||
// constants for glInternalFormat
|
||||
public static int GL_ETC1_RGB8_OES = 0x8D64;
|
||||
|
||||
public static int GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00;
|
||||
public static int GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01;
|
||||
public static int GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02;
|
||||
public static int GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03;
|
||||
|
||||
public static int GL_ATC_RGB_AMD = 0x8C92;
|
||||
public static int GL_ATC_RGBA_EXPLICIT_ALPHA_AMD = 0x8C93;
|
||||
public static int GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD = 0x87EE;
|
||||
|
||||
public static int GL_COMPRESSED_RGB8_ETC2 = 0x9274;
|
||||
public static int GL_COMPRESSED_SRGB8_ETC2 = 0x9275;
|
||||
public static int GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276;
|
||||
public static int GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277;
|
||||
public static int GL_COMPRESSED_RGBA8_ETC2_EAC = 0x9278;
|
||||
public static int GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279;
|
||||
public static int GL_COMPRESSED_R11_EAC = 0x9270;
|
||||
public static int GL_COMPRESSED_SIGNED_R11_EAC = 0x9271;
|
||||
public static int GL_COMPRESSED_RG11_EAC = 0x9272;
|
||||
public static int GL_COMPRESSED_SIGNED_RG11_EAC = 0x9273;
|
||||
|
||||
public static int GL_COMPRESSED_RED_RGTC1 = 0x8DBB;
|
||||
public static int GL_COMPRESSED_RG_RGTC2 = 0x8DBD;
|
||||
public static int GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F;
|
||||
public static int GL_COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C;
|
||||
|
||||
public static int GL_R16F = 0x822D;
|
||||
public static int GL_RG16F = 0x822F;
|
||||
public static int GL_RGBA16F = 0x881A;
|
||||
public static int GL_R32F = 0x822E;
|
||||
public static int GL_RG32F = 0x8230;
|
||||
public static int GL_RGBA32F = 0x8814;
|
||||
|
||||
// constants for glBaseInternalFormat
|
||||
public static int GL_RED = 0x1903;
|
||||
public static int GL_GREEN = 0x1904;
|
||||
public static int GL_BLUE = 0x1905;
|
||||
public static int GL_ALPHA = 0x1906;
|
||||
public static int GL_RGB = 0x1907;
|
||||
public static int GL_RGBA = 0x1908;
|
||||
public static int GL_RG = 0x8227;
|
||||
}
|
||||
|
||||
//from TextureConverter.h
|
||||
public enum QFORMAT
|
||||
{
|
||||
// General formats
|
||||
Q_FORMAT_RGBA_8UI = 1,
|
||||
Q_FORMAT_RGBA_8I,
|
||||
Q_FORMAT_RGB5_A1UI,
|
||||
Q_FORMAT_RGBA_4444,
|
||||
Q_FORMAT_RGBA_16UI,
|
||||
Q_FORMAT_RGBA_16I,
|
||||
Q_FORMAT_RGBA_32UI,
|
||||
Q_FORMAT_RGBA_32I,
|
||||
|
||||
Q_FORMAT_PALETTE_8_RGBA_8888,
|
||||
Q_FORMAT_PALETTE_8_RGBA_5551,
|
||||
Q_FORMAT_PALETTE_8_RGBA_4444,
|
||||
Q_FORMAT_PALETTE_4_RGBA_8888,
|
||||
Q_FORMAT_PALETTE_4_RGBA_5551,
|
||||
Q_FORMAT_PALETTE_4_RGBA_4444,
|
||||
Q_FORMAT_PALETTE_1_RGBA_8888,
|
||||
Q_FORMAT_PALETTE_8_RGB_888,
|
||||
Q_FORMAT_PALETTE_8_RGB_565,
|
||||
Q_FORMAT_PALETTE_4_RGB_888,
|
||||
Q_FORMAT_PALETTE_4_RGB_565,
|
||||
|
||||
Q_FORMAT_R2_GBA10UI,
|
||||
Q_FORMAT_RGB10_A2UI,
|
||||
Q_FORMAT_RGB10_A2I,
|
||||
Q_FORMAT_RGBA_F,
|
||||
Q_FORMAT_RGBA_HF,
|
||||
|
||||
Q_FORMAT_RGB9_E5, // Last five bits are exponent bits (Read following section in GLES3 spec: "3.8.17 Shared Exponent Texture Color Conversion")
|
||||
Q_FORMAT_RGB_8UI,
|
||||
Q_FORMAT_RGB_8I,
|
||||
Q_FORMAT_RGB_565,
|
||||
Q_FORMAT_RGB_16UI,
|
||||
Q_FORMAT_RGB_16I,
|
||||
Q_FORMAT_RGB_32UI,
|
||||
Q_FORMAT_RGB_32I,
|
||||
|
||||
Q_FORMAT_RGB_F,
|
||||
Q_FORMAT_RGB_HF,
|
||||
Q_FORMAT_RGB_11_11_10_F,
|
||||
|
||||
Q_FORMAT_RG_F,
|
||||
Q_FORMAT_RG_HF,
|
||||
Q_FORMAT_RG_32UI,
|
||||
Q_FORMAT_RG_32I,
|
||||
Q_FORMAT_RG_16I,
|
||||
Q_FORMAT_RG_16UI,
|
||||
Q_FORMAT_RG_8I,
|
||||
Q_FORMAT_RG_8UI,
|
||||
Q_FORMAT_RG_S88,
|
||||
|
||||
Q_FORMAT_R_32UI,
|
||||
Q_FORMAT_R_32I,
|
||||
Q_FORMAT_R_F,
|
||||
Q_FORMAT_R_16F,
|
||||
Q_FORMAT_R_16I,
|
||||
Q_FORMAT_R_16UI,
|
||||
Q_FORMAT_R_8I,
|
||||
Q_FORMAT_R_8UI,
|
||||
|
||||
Q_FORMAT_LUMINANCE_ALPHA_88,
|
||||
Q_FORMAT_LUMINANCE_8,
|
||||
Q_FORMAT_ALPHA_8,
|
||||
|
||||
Q_FORMAT_LUMINANCE_ALPHA_F,
|
||||
Q_FORMAT_LUMINANCE_F,
|
||||
Q_FORMAT_ALPHA_F,
|
||||
Q_FORMAT_LUMINANCE_ALPHA_HF,
|
||||
Q_FORMAT_LUMINANCE_HF,
|
||||
Q_FORMAT_ALPHA_HF,
|
||||
Q_FORMAT_DEPTH_16,
|
||||
Q_FORMAT_DEPTH_24,
|
||||
Q_FORMAT_DEPTH_24_STENCIL_8,
|
||||
Q_FORMAT_DEPTH_32,
|
||||
|
||||
Q_FORMAT_BGR_565,
|
||||
Q_FORMAT_BGRA_8888,
|
||||
Q_FORMAT_BGRA_5551,
|
||||
Q_FORMAT_BGRX_8888,
|
||||
Q_FORMAT_BGRA_4444,
|
||||
// Compressed formats
|
||||
Q_FORMAT_ATITC_RGBA,
|
||||
Q_FORMAT_ATC_RGBA_EXPLICIT_ALPHA = Q_FORMAT_ATITC_RGBA,
|
||||
Q_FORMAT_ATITC_RGB,
|
||||
Q_FORMAT_ATC_RGB = Q_FORMAT_ATITC_RGB,
|
||||
Q_FORMAT_ATC_RGBA_INTERPOLATED_ALPHA,
|
||||
Q_FORMAT_ETC1_RGB8,
|
||||
Q_FORMAT_3DC_X,
|
||||
Q_FORMAT_3DC_XY,
|
||||
|
||||
Q_FORMAT_ETC2_RGB8,
|
||||
Q_FORMAT_ETC2_RGBA8,
|
||||
Q_FORMAT_ETC2_RGB8_PUNCHTHROUGH_ALPHA1,
|
||||
Q_FORMAT_ETC2_SRGB8,
|
||||
Q_FORMAT_ETC2_SRGB8_ALPHA8,
|
||||
Q_FORMAT_ETC2_SRGB8_PUNCHTHROUGH_ALPHA1,
|
||||
Q_FORMAT_EAC_R_SIGNED,
|
||||
Q_FORMAT_EAC_R_UNSIGNED,
|
||||
Q_FORMAT_EAC_RG_SIGNED,
|
||||
Q_FORMAT_EAC_RG_UNSIGNED,
|
||||
|
||||
Q_FORMAT_S3TC_DXT1_RGB,
|
||||
Q_FORMAT_S3TC_DXT1_RGBA,
|
||||
Q_FORMAT_S3TC_DXT3_RGBA,
|
||||
Q_FORMAT_S3TC_DXT5_RGBA,
|
||||
|
||||
// YUV formats
|
||||
Q_FORMAT_AYUV_32,
|
||||
Q_FORMAT_I444_24,
|
||||
Q_FORMAT_YUYV_16,
|
||||
Q_FORMAT_UYVY_16,
|
||||
Q_FORMAT_I420_12,
|
||||
Q_FORMAT_YV12_12,
|
||||
Q_FORMAT_NV21_12,
|
||||
Q_FORMAT_NV12_12,
|
||||
|
||||
// ASTC Format
|
||||
Q_FORMAT_ASTC_8,
|
||||
Q_FORMAT_ASTC_16,
|
||||
};
|
||||
|
||||
public enum texgenpack_texturetype
|
||||
{
|
||||
RGTC1,
|
||||
RGTC2,
|
||||
BPTC_FLOAT,
|
||||
BPTC
|
||||
}
|
||||
}
|
||||
93
UnityStudio/StudioClasses/WebFile.cs
Normal file
93
UnityStudio/StudioClasses/WebFile.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BrotliSharpLib;
|
||||
|
||||
namespace UnityStudio
|
||||
{
|
||||
public class WebFile
|
||||
{
|
||||
public static byte[] gzipMagic = { 0x1f, 0x8b };
|
||||
public static byte[] brotliMagic = { 0x62, 0x72, 0x6F, 0x74, 0x6C, 0x69 };
|
||||
public List<MemoryFile> fileList = new List<MemoryFile>();
|
||||
|
||||
|
||||
public class WebData
|
||||
{
|
||||
public int dataOffset;
|
||||
public int dataLength;
|
||||
public string path;
|
||||
}
|
||||
|
||||
|
||||
public WebFile(EndianBinaryReader reader)
|
||||
{
|
||||
var magic = reader.ReadBytes(2);
|
||||
reader.Position = 0;
|
||||
if (gzipMagic.SequenceEqual(magic))
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
using (var gs = new GZipStream(reader.BaseStream, CompressionMode.Decompress))
|
||||
{
|
||||
gs.CopyTo(stream);
|
||||
}
|
||||
stream.Position = 0;
|
||||
using (reader = new EndianBinaryReader(stream, EndianType.LittleEndian))
|
||||
{
|
||||
ReadUnityWebData(reader);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Position = 0x20;
|
||||
magic = reader.ReadBytes(6);
|
||||
reader.Position = 0;
|
||||
if (brotliMagic.SequenceEqual(magic))
|
||||
{
|
||||
var buff = reader.ReadBytes((int)reader.BaseStream.Length);
|
||||
var uncompressedData = Brotli.DecompressBuffer(buff, 0, buff.Length);
|
||||
var stream = new MemoryStream(uncompressedData);
|
||||
using (reader = new EndianBinaryReader(stream, EndianType.LittleEndian))
|
||||
{
|
||||
ReadUnityWebData(reader);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.endian = EndianType.LittleEndian;
|
||||
ReadUnityWebData(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadUnityWebData(EndianBinaryReader reader)
|
||||
{
|
||||
var signature = reader.ReadStringToNull();
|
||||
if (signature != "UnityWebData1.0")
|
||||
return;
|
||||
var headLength = reader.ReadInt32();
|
||||
var dataList = new List<WebData>();
|
||||
while (reader.Position < headLength)
|
||||
{
|
||||
var data = new WebData();
|
||||
data.dataOffset = reader.ReadInt32();
|
||||
data.dataLength = reader.ReadInt32();
|
||||
var pathLength = reader.ReadInt32();
|
||||
data.path = Encoding.UTF8.GetString(reader.ReadBytes(pathLength));
|
||||
dataList.Add(data);
|
||||
}
|
||||
|
||||
foreach (var data in dataList)
|
||||
{
|
||||
var file = new MemoryFile();
|
||||
file.fileName = Path.GetFileName(data.path);
|
||||
reader.Position = data.dataOffset;
|
||||
file.stream = new MemoryStream(reader.ReadBytes(data.dataLength));
|
||||
fileList.Add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user