New project name
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
public class AssetPreloadData : ListViewItem
|
||||
{
|
||||
public long m_PathID;
|
||||
public uint Offset;
|
||||
public int Size;
|
||||
public ClassIDReference Type;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 AssetStudio
|
||||
{
|
||||
public class AssetsFile
|
||||
{
|
||||
public EndianBinaryReader assetsFileReader;
|
||||
public string filePath;
|
||||
public string parentPath;
|
||||
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<SharedAssets> sharedAssetsList = new List<SharedAssets> { new SharedAssets() };
|
||||
|
||||
public SortedDictionary<int, ClassStruct> ClassStructures = new SortedDictionary<int, ClassStruct>();
|
||||
|
||||
private bool baseDefinitions;
|
||||
private List<int[]> classIDs = new List<int[]>();//use for 5.5.0
|
||||
|
||||
|
||||
#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 SharedAssets
|
||||
{
|
||||
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 version!" + fileGen, "AssetStudio 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.IsDefined(typeof(BuildTarget), platform) ? ((BuildTarget)platform).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 (Enum.IsDefined(typeof(ClassIDReference), asset.Type2))
|
||||
{
|
||||
asset.Type = (ClassIDReference)asset.Type2;
|
||||
asset.TypeString = asset.Type.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
asset.Type = ClassIDReference.UnknownType;
|
||||
asset.TypeString = "UnknownType " + 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 version 2.x files
|
||||
if (asset.Type == ClassIDReference.BuildSettings && fileGen == 6)
|
||||
{
|
||||
long nextAsset = assetsFileReader.Position;
|
||||
|
||||
BuildSettings BSettings = new BuildSettings(asset);
|
||||
m_Version = BSettings.m_Version;
|
||||
|
||||
assetsFileReader.Position = nextAsset;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
buildType = Regex.Replace(m_Version, @"\d", "").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries);
|
||||
version = Regex.Matches(m_Version, @"\d").Cast<Match>().Select(m => int.Parse(m.Value)).ToArray();
|
||||
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 SharedAssets();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
public enum BuildTarget
|
||||
{
|
||||
DashboardWidget = 1,
|
||||
StandaloneOSX = 2,
|
||||
StandaloneOSXPPC = 3,
|
||||
StandaloneOSXIntel = 4,
|
||||
StandaloneWindows,
|
||||
WebPlayer,
|
||||
WebPlayerStreamed,
|
||||
Wii = 8,
|
||||
iOS = 9,
|
||||
PS3,
|
||||
XBOX360,
|
||||
Android = 13,
|
||||
StandaloneGLESEmu = 14,
|
||||
NaCl = 16,
|
||||
StandaloneLinux = 17,
|
||||
FlashPlayer = 18,
|
||||
StandaloneWindows64 = 19,
|
||||
WebGL,
|
||||
WSAPlayer,
|
||||
StandaloneLinux64 = 24,
|
||||
StandaloneLinuxUniversal,
|
||||
WP8Player,
|
||||
StandaloneOSXIntel64,
|
||||
BlackBerry,
|
||||
Tizen,
|
||||
PSP2,
|
||||
PS4,
|
||||
PSM,
|
||||
XboxOne,
|
||||
SamsungTV,
|
||||
N3DS,
|
||||
WiiU,
|
||||
tvOS,
|
||||
Switch,
|
||||
NoTarget = -2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Lz4;
|
||||
using SevenZip.Compression.LZMA;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
namespace AssetStudio
|
||||
{
|
||||
public enum ClassIDReference
|
||||
{
|
||||
UnknownType = -1,
|
||||
GameObject = 1,
|
||||
Component = 2,
|
||||
LevelGameManager = 3,
|
||||
Transform = 4,
|
||||
TimeManager = 5,
|
||||
GlobalGameManager = 6,
|
||||
Behaviour = 8,
|
||||
GameManager = 9,
|
||||
AudioManager = 11,
|
||||
ParticleAnimator = 12,
|
||||
InputManager = 13,
|
||||
EllipsoidParticleEmitter = 15,
|
||||
Pipeline = 17,
|
||||
EditorExtension = 18,
|
||||
Physics2DSettings = 19,
|
||||
Camera = 20,
|
||||
Material = 21,
|
||||
MeshRenderer = 23,
|
||||
Renderer = 25,
|
||||
ParticleRenderer = 26,
|
||||
Texture = 27,
|
||||
Texture2D = 28,
|
||||
SceneSettings = 29,
|
||||
GraphicsSettings = 30,
|
||||
MeshFilter = 33,
|
||||
OcclusionPortal = 41,
|
||||
Mesh = 43,
|
||||
Skybox = 45,
|
||||
QualitySettings = 47,
|
||||
Shader = 48,
|
||||
TextAsset = 49,
|
||||
Rigidbody2D = 50,
|
||||
Physics2DManager = 51,
|
||||
Collider2D = 53,
|
||||
Rigidbody = 54,
|
||||
PhysicsManager = 55,
|
||||
Collider = 56,
|
||||
Joint = 57,
|
||||
CircleCollider2D = 58,
|
||||
HingeJoint = 59,
|
||||
PolygonCollider2D = 60,
|
||||
BoxCollider2D = 61,
|
||||
PhysicsMaterial2D = 62,
|
||||
MeshCollider = 64,
|
||||
BoxCollider = 65,
|
||||
SpriteCollider2D = 66,
|
||||
EdgeCollider2D = 68,
|
||||
CapsuleCollider2D = 70,
|
||||
ComputeShader = 72,
|
||||
AnimationClip = 74,
|
||||
ConstantForce = 75,
|
||||
WorldParticleCollider = 76,
|
||||
TagManager = 78,
|
||||
AudioListener = 81,
|
||||
AudioSource = 82,
|
||||
AudioClip = 83,
|
||||
RenderTexture = 84,
|
||||
CustomRenderTexture = 86,
|
||||
MeshParticleEmitter = 87,
|
||||
ParticleEmitter = 88,
|
||||
Cubemap = 89,
|
||||
Avatar = 90,
|
||||
AnimatorController = 91,
|
||||
GUILayer = 92,
|
||||
RuntimeAnimatorController = 93,
|
||||
ScriptMapper = 94,
|
||||
Animator = 95,
|
||||
TrailRenderer = 96,
|
||||
DelayedCallManager = 98,
|
||||
TextMesh = 102,
|
||||
RenderSettings = 104,
|
||||
Light = 108,
|
||||
CGProgram = 109,
|
||||
BaseAnimationTrack = 110,
|
||||
Animation = 111,
|
||||
MonoBehaviour = 114,
|
||||
MonoScript = 115,
|
||||
MonoManager = 116,
|
||||
Texture3D = 117,
|
||||
NewAnimationTrack = 118,
|
||||
Projector = 119,
|
||||
LineRenderer = 120,
|
||||
Flare = 121,
|
||||
Halo = 122,
|
||||
LensFlare = 123,
|
||||
FlareLayer = 124,
|
||||
HaloLayer = 125,
|
||||
NavMeshAreas = 126,
|
||||
HaloManager = 127,
|
||||
Font = 128,
|
||||
PlayerSettings = 129,
|
||||
NamedObject = 130,
|
||||
GUITexture = 131,
|
||||
GUIText = 132,
|
||||
GUIElement = 133,
|
||||
PhysicMaterial = 134,
|
||||
SphereCollider = 135,
|
||||
CapsuleCollider = 136,
|
||||
SkinnedMeshRenderer = 137,
|
||||
FixedJoint = 138,
|
||||
RaycastCollider = 140,
|
||||
BuildSettings = 141,
|
||||
AssetBundle = 142,
|
||||
CharacterController = 143,
|
||||
CharacterJoint = 144,
|
||||
SpringJoint = 145,
|
||||
WheelCollider = 146,
|
||||
ResourceManager = 147,
|
||||
NetworkView = 148,
|
||||
NetworkManager = 149,
|
||||
PreloadData = 150,
|
||||
MovieTexture = 152,
|
||||
ConfigurableJoint = 153,
|
||||
TerrainCollider = 154,
|
||||
MasterServerInterface = 155,
|
||||
TerrainData = 156,
|
||||
LightmapSettings = 157,
|
||||
WebCamTexture = 158,
|
||||
EditorSettings = 159,
|
||||
InteractiveCloth = 160,
|
||||
ClothRenderer = 161,
|
||||
EditorUserSettings = 162,
|
||||
SkinnedCloth = 163,
|
||||
AudioReverbFilter = 164,
|
||||
AudioHighPassFilter = 165,
|
||||
AudioChorusFilter = 166,
|
||||
AudioReverbZone = 167,
|
||||
AudioEchoFilter = 168,
|
||||
AudioLowPassFilter = 169,
|
||||
AudioDistortionFilter = 170,
|
||||
SparseTexture = 171,
|
||||
AudioBehaviour = 180,
|
||||
AudioFilter = 181,
|
||||
WindZone = 182,
|
||||
Cloth = 183,
|
||||
SubstanceArchive = 184,
|
||||
ProceduralMaterial = 185,
|
||||
ProceduralTexture = 186,
|
||||
Texture2DArray = 187,
|
||||
CubemapArray = 188,
|
||||
OffMeshLink = 191,
|
||||
OcclusionArea = 192,
|
||||
Tree = 193,
|
||||
NavMeshObsolete = 194,
|
||||
NavMeshAgent = 195,
|
||||
NavMeshSettings = 196,
|
||||
LightProbesLegacy = 197,
|
||||
ParticleSystem = 198,
|
||||
ParticleSystemRenderer = 199,
|
||||
ShaderVariantCollection = 200,
|
||||
LODGroup = 205,
|
||||
BlendTree = 206,
|
||||
Motion = 207,
|
||||
NavMeshObstacle = 208,
|
||||
TerrainInstance = 210,
|
||||
SpriteRenderer = 212,
|
||||
Sprite = 213,
|
||||
CachedSpriteAtlas = 214,
|
||||
ReflectionProbe = 215,
|
||||
ReflectionProbes = 216,
|
||||
Terrain = 218,
|
||||
LightProbeGroup = 220,
|
||||
AnimatorOverrideController = 221,
|
||||
CanvasRenderer = 222,
|
||||
Canvas = 223,
|
||||
RectTransform = 224,
|
||||
CanvasGroup = 225,
|
||||
BillboardAsset = 226,
|
||||
BillboardRenderer = 227,
|
||||
SpeedTreeWindAsset = 228,
|
||||
AnchoredJoint2D = 229,
|
||||
Joint2D = 230,
|
||||
SpringJoint2D = 231,
|
||||
DistanceJoint2D = 232,
|
||||
HingeJoint2D = 233,
|
||||
SliderJoint2D = 234,
|
||||
WheelJoint2D = 235,
|
||||
ClusterInputManager = 236,
|
||||
BaseVideoTexture = 237,
|
||||
NavMeshData = 238,
|
||||
AudioMixer = 240,
|
||||
AudioMixerController = 241,
|
||||
AudioMixerGroupController = 243,
|
||||
AudioMixerEffectController = 244,
|
||||
AudioMixerSnapshotController = 245,
|
||||
PhysicsUpdateBehaviour2D = 246,
|
||||
ConstantForce2D = 247,
|
||||
Effector2D = 248,
|
||||
AreaEffector2D = 249,
|
||||
PointEffector2D = 250,
|
||||
PlatformEffector2D = 251,
|
||||
SurfaceEffector2D = 252,
|
||||
BuoyancyEffector2D = 253,
|
||||
RelativeJoint2D = 254,
|
||||
FixedJoint2D = 255,
|
||||
FrictionJoint2D = 256,
|
||||
TargetJoint2D = 257,
|
||||
LightProbes = 258,
|
||||
LightProbeProxyVolume = 259,
|
||||
SampleClip = 271,
|
||||
AudioMixerSnapshot = 272,
|
||||
AudioMixerGroup = 273,
|
||||
NScreenBridge = 280,
|
||||
AssetBundleManifest = 290,
|
||||
UnityAdsManager = 292,
|
||||
RuntimeInitializeOnLoadManager = 300,
|
||||
CloudWebServicesManager = 301,
|
||||
UnityAnalyticsManager = 303,
|
||||
CrashReportManager = 304,
|
||||
PerformanceReportingManager = 305,
|
||||
UnityConnectSettings = 310,
|
||||
AvatarMask = 319,
|
||||
VideoPlayer = 328,
|
||||
VideoClip = 329,
|
||||
OcclusionCullingData = 363,
|
||||
Prefab = 1001,
|
||||
EditorExtensionImpl = 1002,
|
||||
AssetImporter = 1003,
|
||||
AssetDatabase = 1004,
|
||||
Mesh3DSImporter = 1005,
|
||||
TextureImporter = 1006,
|
||||
ShaderImporter = 1007,
|
||||
ComputeShaderImporter = 1008,
|
||||
AudioImporter = 1020,
|
||||
HierarchyState = 1026,
|
||||
GUIDSerializer = 1027,
|
||||
AssetMetaData = 1028,
|
||||
DefaultAsset = 1029,
|
||||
DefaultImporter = 1030,
|
||||
TextScriptImporter = 1031,
|
||||
SceneAsset = 1032,
|
||||
NativeFormatImporter = 1034,
|
||||
MonoImporter = 1035,
|
||||
AssetServerCache = 1037,
|
||||
LibraryAssetImporter = 1038,
|
||||
ModelImporter = 1040,
|
||||
FBXImporter = 1041,
|
||||
TrueTypeFontImporter = 1042,
|
||||
MovieImporter = 1044,
|
||||
EditorBuildSettings = 1045,
|
||||
DDSImporter = 1046,
|
||||
InspectorExpandedState = 1048,
|
||||
AnnotationManager = 1049,
|
||||
PluginImporter = 1050,
|
||||
EditorUserBuildSettings = 1051,
|
||||
PVRImporter = 1052,
|
||||
ASTCImporter = 1053,
|
||||
KTXImporter = 1054,
|
||||
AnimatorStateTransition = 1101,
|
||||
AnimatorState = 1102,
|
||||
HumanTemplate = 1105,
|
||||
AnimatorStateMachine = 1107,
|
||||
PreviewAssetType = 1108,
|
||||
AnimatorTransition = 1109,
|
||||
SpeedTreeImporter = 1110,
|
||||
AnimatorTransitionBase = 1111,
|
||||
SubstanceImporter = 1112,
|
||||
LightmapParameters = 1113,
|
||||
LightmapSnapshot = 1120,
|
||||
SubDerived = 367388927,
|
||||
SiblingDerived = 334799969,
|
||||
SpriteAtlas = 687078895,
|
||||
Derived = 1091556383,
|
||||
LowerResBlitTexture = 1480428607,
|
||||
RenderPassAttachment = 1571458007
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 AssetStudio.SpriteHelper;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
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 UFont(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using static AssetStudio.Studio;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
static class Importer
|
||||
{
|
||||
public static List<string> importFiles = new List<string>(); //files to load
|
||||
public static HashSet<string> importFilesHash = 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, string parentPath = null)
|
||||
{
|
||||
var fileName = Path.GetFileName(fullName);
|
||||
StatusStripUpdate("Loading " + fileName);
|
||||
if (!assetsfileListHash.Contains(fileName.ToUpper()))
|
||||
{
|
||||
var assetsFile = new AssetsFile(fullName, reader);
|
||||
if (assetsFile.valid)
|
||||
{
|
||||
assetsFile.parentPath = parentPath;
|
||||
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 (!importFilesHash.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))
|
||||
{
|
||||
importFiles.Add(sharedFilePath);
|
||||
importFilesHash.Add(sharedFileName.ToUpper());
|
||||
value++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value > 0)
|
||||
ProgressBarMaximumAdd(value);
|
||||
}
|
||||
else
|
||||
reader.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadBundleFile(string fullName, EndianBinaryReader reader, string parentPath = null)
|
||||
{
|
||||
var fileName = Path.GetFileName(fullName);
|
||||
StatusStripUpdate("Decompressing " + fileName);
|
||||
var bundleFile = new BundleFile(reader);
|
||||
reader.Dispose();
|
||||
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.parentPath = parentPath ?? 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 = Regex.Matches(bundleFile.versionEngine, @"\d").Cast<Match>().Select(m => int.Parse(m.Value)).ToArray();
|
||||
assetsFile.buildType = Regex.Replace(bundleFile.versionEngine, @"\d", "").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
assetsfileList.Add(assetsFile);
|
||||
assetsfileListHash.Add(assetsFile.upperFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
resourceFileReaders.Add(assetsFile.upperFileName, assetsFile.assetsFileReader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadWebFile(string fullName, EndianBinaryReader reader)
|
||||
{
|
||||
var fileName = Path.GetFileName(fullName);
|
||||
StatusStripUpdate("Loading " + fileName);
|
||||
var webFile = new WebFile(reader);
|
||||
reader.Dispose();
|
||||
foreach (var file in webFile.fileList)
|
||||
{
|
||||
var dummyName = Path.GetDirectoryName(fullName) + "\\" + file.fileName;
|
||||
switch (CheckFileType(file.stream, out reader))
|
||||
{
|
||||
case FileType.AssetsFile:
|
||||
LoadAssetsFile(dummyName, reader, fullName);
|
||||
break;
|
||||
case FileType.BundleFile:
|
||||
LoadBundleFile(dummyName, reader, fullName);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using static AssetStudio.Studio;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
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
@@ -0,0 +1,108 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 此代码由工具生成。
|
||||
// 运行时版本:4.0.30319.42000
|
||||
//
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果
|
||||
// 重新生成代码,这些更改将会丢失。
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace AssetStudio {
|
||||
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("AssetStudio.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -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 AssetStudio.Studio;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web.Script.Serialization;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
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.Type)
|
||||
{
|
||||
case ClassIDReference.GameObject:
|
||||
{
|
||||
GameObject m_GameObject = new GameObject(asset);
|
||||
assetsFile.GameObjectList.Add(asset.m_PathID, m_GameObject);
|
||||
//totalTreeNodes++;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.Transform:
|
||||
{
|
||||
Transform m_Transform = new Transform(asset);
|
||||
assetsFile.TransformList.Add(asset.m_PathID, m_Transform);
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.RectTransform:
|
||||
{
|
||||
RectTransform m_Rect = new RectTransform(asset);
|
||||
assetsFile.TransformList.Add(asset.m_PathID, m_Rect.m_Transform);
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.Texture2D:
|
||||
{
|
||||
Texture2D m_Texture2D = new Texture2D(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.Shader:
|
||||
{
|
||||
Shader m_Shader = new Shader(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.TextAsset:
|
||||
{
|
||||
TextAsset m_TextAsset = new TextAsset(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.AudioClip:
|
||||
{
|
||||
AudioClip m_AudioClip = new AudioClip(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.MonoBehaviour:
|
||||
{
|
||||
var m_MonoBehaviour = new MonoBehaviour(asset, false);
|
||||
if (asset.Type1 != asset.Type2 && assetsFile.ClassStructures.ContainsKey(asset.Type1))
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.Font:
|
||||
{
|
||||
UFont m_Font = new UFont(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.PlayerSettings:
|
||||
{
|
||||
var plSet = new PlayerSettings(asset);
|
||||
productName = plSet.productName;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.Mesh:
|
||||
{
|
||||
Mesh m_Mesh = new Mesh(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.AssetBundle:
|
||||
{
|
||||
ab = new AssetBundle(asset);
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.VideoClip:
|
||||
{
|
||||
var m_VideoClip = new VideoClip(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.MovieTexture:
|
||||
{
|
||||
var m_MovieTexture = new MovieTexture(asset, false);
|
||||
exportable = true;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.Sprite:
|
||||
{
|
||||
var m_Sprite = new Sprite(asset, false);
|
||||
exportable = true;
|
||||
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.Type)
|
||||
{
|
||||
case ClassIDReference.Transform:
|
||||
{
|
||||
m_GameObject.m_Transform = m_Component;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.MeshRenderer:
|
||||
{
|
||||
m_GameObject.m_MeshRenderer = m_Component;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.MeshFilter:
|
||||
{
|
||||
m_GameObject.m_MeshFilter = m_Component;
|
||||
break;
|
||||
}
|
||||
case ClassIDReference.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 AssetStudio
|
||||
{
|
||||
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))
|
||||
{
|
||||
ReadWebData(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))
|
||||
{
|
||||
ReadWebData(reader);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.endian = EndianType.LittleEndian;
|
||||
ReadWebData(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadWebData(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