This commit is contained in:
Perfare 2018-09-27 05:23:10 +08:00
parent 97169ff6dd
commit f2b041741b
19 changed files with 285 additions and 310 deletions

View File

@ -183,9 +183,6 @@
<Compile Include="StudioClasses\BinaryReaderExtensions.cs" /> <Compile Include="StudioClasses\BinaryReaderExtensions.cs" />
<Compile Include="StudioClasses\BinaryWriterExtensions.cs" /> <Compile Include="StudioClasses\BinaryWriterExtensions.cs" />
<Compile Include="StudioClasses\BundleFile.cs" /> <Compile Include="StudioClasses\BundleFile.cs" />
<Compile Include="StudioClasses\ClassMember.cs" />
<Compile Include="StudioClasses\ClassStruct.cs" />
<Compile Include="StudioClasses\ClassStructHelper.cs" />
<Compile Include="StudioClasses\FBXExporter.cs" /> <Compile Include="StudioClasses\FBXExporter.cs" />
<Compile Include="StudioClasses\BuildTarget.cs" /> <Compile Include="StudioClasses\BuildTarget.cs" />
<Compile Include="StudioClasses\GameObjectTreeNode.cs" /> <Compile Include="StudioClasses\GameObjectTreeNode.cs" />
@ -231,6 +228,9 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StudioClasses\AssetsFile.cs" /> <Compile Include="StudioClasses\AssetsFile.cs" />
<Compile Include="StudioClasses\Texture2DConverter.cs" /> <Compile Include="StudioClasses\Texture2DConverter.cs" />
<Compile Include="StudioClasses\TypeItem.cs" />
<Compile Include="StudioClasses\TypeTree.cs" />
<Compile Include="StudioClasses\TypeTreeHelper.cs" />
<Compile Include="StudioClasses\WebFile.cs" /> <Compile Include="StudioClasses\WebFile.cs" />
<Compile Include="TreeViewExtensions.cs" /> <Compile Include="TreeViewExtensions.cs" />
<EmbeddedResource Include="AssetStudioForm.resx"> <EmbeddedResource Include="AssetStudioForm.resx">

View File

@ -174,8 +174,8 @@
<Compile Include="StudioClasses\BinaryWriterExtensions.cs" /> <Compile Include="StudioClasses\BinaryWriterExtensions.cs" />
<Compile Include="StudioClasses\BuildTarget.cs" /> <Compile Include="StudioClasses\BuildTarget.cs" />
<Compile Include="StudioClasses\ClassIDReference.cs" /> <Compile Include="StudioClasses\ClassIDReference.cs" />
<Compile Include="StudioClasses\ClassMember.cs" /> <Compile Include="StudioClasses\TypeTree.cs" />
<Compile Include="StudioClasses\ClassStructHelper.cs" /> <Compile Include="StudioClasses\TypeTreeHelper.cs" />
<Compile Include="StudioClasses\ResourcesHelper.cs" /> <Compile Include="StudioClasses\ResourcesHelper.cs" />
<Compile Include="Classes\AssetBundle.cs" /> <Compile Include="Classes\AssetBundle.cs" />
<Compile Include="Classes\MovieTexture.cs" /> <Compile Include="Classes\MovieTexture.cs" />
@ -186,7 +186,7 @@
<Compile Include="Classes\AudioClip.cs" /> <Compile Include="Classes\AudioClip.cs" />
<Compile Include="Classes\BuildSettings.cs" /> <Compile Include="Classes\BuildSettings.cs" />
<Compile Include="StudioClasses\BundleFile.cs" /> <Compile Include="StudioClasses\BundleFile.cs" />
<Compile Include="StudioClasses\ClassStruct.cs" /> <Compile Include="StudioClasses\TypeItem.cs" />
<Compile Include="StudioClasses\FBXExporter.cs" /> <Compile Include="StudioClasses\FBXExporter.cs" />
<Compile Include="StudioClasses\SpriteHelper.cs" /> <Compile Include="StudioClasses\SpriteHelper.cs" />
<Compile Include="StudioClasses\Exporter.cs" /> <Compile Include="StudioClasses\Exporter.cs" />

View File

@ -178,11 +178,11 @@ namespace AssetStudio
{ {
if (!string.IsNullOrEmpty(productName)) if (!string.IsNullOrEmpty(productName))
{ {
Text = $"AssetStudio - {productName} - {assetsfileList[0].m_Version} - {assetsfileList[0].platformStr}"; Text = $"AssetStudio - {productName} - {assetsfileList[0].unityVersion} - {assetsfileList[0].platformStr}";
} }
else if (assetsfileList.Count > 0) else if (assetsfileList.Count > 0)
{ {
Text = $"AssetStudio - no productName - {assetsfileList[0].m_Version} - {assetsfileList[0].platformStr}"; Text = $"AssetStudio - no productName - {assetsfileList[0].unityVersion} - {assetsfileList[0].platformStr}";
} }
if (!dontLoadAssetsMenuItem.Checked) if (!dontLoadAssetsMenuItem.Checked)
{ {
@ -203,7 +203,7 @@ namespace AssetStudio
if (buildClassStructuresMenuItem.Checked) if (buildClassStructuresMenuItem.Checked)
{ {
classesListView.BeginUpdate(); classesListView.BeginUpdate();
foreach (var version in AllClassStructures) foreach (var version in AllTypeMap)
{ {
ListViewGroup versionGroup = new ListViewGroup(version.Key); ListViewGroup versionGroup = new ListViewGroup(version.Key);
classesListView.Groups.Add(versionGroup); classesListView.Groups.Add(versionGroup);
@ -311,16 +311,16 @@ namespace AssetStudio
private void exportClassStructuresMenuItem_Click(object sender, EventArgs e) private void exportClassStructuresMenuItem_Click(object sender, EventArgs e)
{ {
if (AllClassStructures.Count > 0) if (AllTypeMap.Count > 0)
{ {
var saveFolderDialog1 = new OpenFolderDialog(); var saveFolderDialog1 = new OpenFolderDialog();
if (saveFolderDialog1.ShowDialog(this) == DialogResult.OK) if (saveFolderDialog1.ShowDialog(this) == DialogResult.OK)
{ {
progressBar1.Value = 0; progressBar1.Value = 0;
progressBar1.Maximum = AllClassStructures.Count; progressBar1.Maximum = AllTypeMap.Count;
var savePath = saveFolderDialog1.Folder; var savePath = saveFolderDialog1.Folder;
foreach (var version in AllClassStructures) foreach (var version in AllTypeMap)
{ {
if (version.Value.Count > 0) if (version.Value.Count > 0)
{ {
@ -646,7 +646,7 @@ namespace AssetStudio
{ {
if (e.IsSelected) if (e.IsSelected)
{ {
classTextBox.Text = ((ClassStruct)classesListView.SelectedItems[0]).ToString(); classTextBox.Text = ((TypeItem)classesListView.SelectedItems[0]).ToString();
} }
} }
@ -836,7 +836,7 @@ namespace AssetStudio
case ClassIDReference.MonoBehaviour: case ClassIDReference.MonoBehaviour:
{ {
var m_MonoBehaviour = new MonoBehaviour(asset); var m_MonoBehaviour = new MonoBehaviour(asset);
if (asset.Type1 != asset.Type2 && asset.sourceFile.ClassStructures.ContainsKey(asset.Type1)) if (asset.Type1 != asset.Type2 && asset.sourceFile.m_Type.ContainsKey(asset.Type1))
{ {
textPreviewBox.Text = asset.Dump(); textPreviewBox.Text = asset.Dump();
} }

View File

@ -595,7 +595,7 @@ namespace AssetStudio
} }
} }
if (preloadData.sourceFile.platform == 11 && componentByteSize > 1) //swap bytes for Xbox if (preloadData.sourceFile.m_TargetPlatform == BuildTarget.XBOX360 && componentByteSize > 1) //swap bytes for Xbox
{ {
for (var i = 0; i < componentBytes.Length / componentByteSize; i++) for (var i = 0; i < componentBytes.Length / componentByteSize; i++)
{ {
@ -704,7 +704,7 @@ namespace AssetStudio
} }
} }
if (preloadData.sourceFile.platform == 11 && componentByteSize > 1) //swap bytes for Xbox if (preloadData.sourceFile.m_TargetPlatform == BuildTarget.XBOX360 && componentByteSize > 1) //swap bytes for Xbox
{ {
for (var i = 0; i < componentBytes.Length / componentByteSize; i++) for (var i = 0; i < componentBytes.Length / componentByteSize; i++)
{ {

View File

@ -21,7 +21,7 @@ namespace AssetStudio
reader = preloadData.InitReader(); reader = preloadData.InitReader();
version = sourceFile.version; version = sourceFile.version;
buildType = sourceFile.buildType; buildType = sourceFile.buildType;
platform = (BuildTarget)sourceFile.platform; platform = sourceFile.m_TargetPlatform;
if (platform == BuildTarget.NoTarget) if (platform == BuildTarget.NoTarget)
{ {

View File

@ -33,10 +33,10 @@ namespace AssetStudio
public string Dump() public string Dump()
{ {
var reader = InitReader(); var reader = InitReader();
if (sourceFile.ClassStructures.TryGetValue(Type1, out var classStructure)) if (sourceFile.m_Type.TryGetValue(Type1, out var typeTreeList))
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
ClassStructHelper.ReadClassString(sb, classStructure.members, reader); TypeTreeHelper.ReadTypeString(sb, typeTreeList, reader);
return sb.ToString(); return sb.ToString();
} }
return null; return null;
@ -44,7 +44,7 @@ namespace AssetStudio
public bool HasStructMember(string name) public bool HasStructMember(string name)
{ {
return sourceFile.ClassStructures.TryGetValue(Type1, out var classStructure) && classStructure.members.Any(x => x.Name == name); return sourceFile.m_Type.TryGetValue(Type1, out var typeTreeList) && typeTreeList.Any(x => x.m_Name == name);
} }
} }
} }

View File

@ -8,20 +8,35 @@ using System.Windows.Forms;
namespace AssetStudio namespace AssetStudio
{ {
public class SerializedFileHeader
{
public uint m_MetadataSize;
public uint m_FileSize;
public uint m_Version;
public uint m_DataOffset;
public byte m_Endianess;
public byte[] m_Reserved;
}
public class AssetsFile public class AssetsFile
{ {
public EndianBinaryReader reader; public EndianBinaryReader reader;
public SerializedFileHeader header;
private EndianType m_FileEndianess;
public string unityVersion = "2.5.0f5";
public BuildTarget m_TargetPlatform = BuildTarget.UnknownPlatform;
private bool serializedTypeTrees;
public SortedDictionary<int, List<TypeTree>> m_Type = new SortedDictionary<int, List<TypeTree>>();
private List<int[]> classIDs = new List<int[]>();//use for 5.5.0
public string filePath; public string filePath;
public string parentPath; public string parentPath;
public string fileName; public string fileName;
public string upperFileName; public string upperFileName;
public int fileGen;
public bool valid;
public string m_Version = "2.5.0f5";
public int[] version = { 0, 0, 0, 0 }; public int[] version = { 0, 0, 0, 0 };
public string[] buildType; public string[] buildType;
public int platform = 100663296;
public string platformStr = ""; public string platformStr = "";
public Dictionary<long, AssetPreloadData> preloadTable = new Dictionary<long, AssetPreloadData>(); public Dictionary<long, AssetPreloadData> preloadTable = new Dictionary<long, AssetPreloadData>();
public Dictionary<long, GameObject> GameObjectList = new Dictionary<long, GameObject>(); public Dictionary<long, GameObject> GameObjectList = new Dictionary<long, GameObject>();
public Dictionary<long, Transform> TransformList = new Dictionary<long, Transform>(); public Dictionary<long, Transform> TransformList = new Dictionary<long, Transform>();
@ -29,11 +44,7 @@ namespace AssetStudio
public List<AssetPreloadData> exportableAssets = new List<AssetPreloadData>(); public List<AssetPreloadData> exportableAssets = new List<AssetPreloadData>();
public List<SharedAssets> sharedAssetsList = new List<SharedAssets> { new SharedAssets() }; public List<SharedAssets> sharedAssetsList = new List<SharedAssets> { new SharedAssets() };
public SortedDictionary<int, ClassStruct> ClassStructures = new SortedDictionary<int, ClassStruct>(); public bool valid;
private bool baseDefinitions;
private List<int[]> classIDs = new List<int[]>();//use for 5.5.0
#region cmmon string #region cmmon string
private static Dictionary<int, string> baseStrings = new Dictionary<int, string> private static Dictionary<int, string> baseStrings = new Dictionary<int, string>
@ -157,140 +168,103 @@ namespace AssetStudio
upperFileName = fileName.ToUpper(); upperFileName = fileName.ToUpper();
try try
{ {
int tableSize = this.reader.ReadInt32(); //SerializedFile::ReadHeader
int dataEnd = this.reader.ReadInt32(); header = new SerializedFileHeader();
fileGen = this.reader.ReadInt32(); header.m_MetadataSize = reader.ReadUInt32();
uint dataOffset = this.reader.ReadUInt32(); header.m_FileSize = reader.ReadUInt32();
sharedAssetsList[0].fileName = fileName; //reference itself because sharedFileIDs start from 1 header.m_Version = reader.ReadUInt32();
header.m_DataOffset = reader.ReadUInt32();
switch (fileGen) if (header.m_Version >= 9)
{ {
case 6: //2.5.0 - 2.6.1 header.m_Endianess = reader.ReadByte();
{ header.m_Reserved = reader.ReadBytes(3);
this.reader.Position = (dataEnd - tableSize); m_FileEndianess = (EndianType)header.m_Endianess;
this.reader.Position += 1; }
break; else
} {
case 7: //3.0.0 beta reader.Position = header.m_FileSize - header.m_MetadataSize;
{ m_FileEndianess = (EndianType)reader.ReadByte();
this.reader.Position = (dataEnd - tableSize);
this.reader.Position += 1;
m_Version = this.reader.ReadStringToNull();
break;
}
case 8: //3.0.0 - 3.4.2
{
this.reader.Position = (dataEnd - tableSize);
this.reader.Position += 1;
m_Version = this.reader.ReadStringToNull();
platform = this.reader.ReadInt32();
break;
}
case 9: //3.5.0 - 4.6.x
{
this.reader.Position += 4; //azero
m_Version = this.reader.ReadStringToNull();
platform = this.reader.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
{
this.reader.Position += 4; //azero
m_Version = this.reader.ReadStringToNull();
platform = this.reader.ReadInt32();
baseDefinitions = this.reader.ReadBoolean();
break;
}
default:
{
//MessageBox.Show("Unsupported version!" + fileGen, "AssetStudio Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
} }
if (fileGen > 6 && m_Version == "") //SerializedFile::ReadMetadata
if (m_FileEndianess == EndianType.LittleEndian)
{ {
return; reader.endian = EndianType.LittleEndian;
} }
if (header.m_Version >= 7)
if (platform > 255 || platform < 0)
{ {
byte[] b32 = BitConverter.GetBytes(platform); unityVersion = reader.ReadStringToNull();
Array.Reverse(b32);
platform = BitConverter.ToInt32(b32, 0);
this.reader.endian = EndianType.LittleEndian;
} }
if (header.m_Version >= 8)
platformStr = Enum.IsDefined(typeof(BuildTarget), platform) ? ((BuildTarget)platform).ToString() : "Unknown Platform";
int baseCount = this.reader.ReadInt32();
for (int i = 0; i < baseCount; i++)
{ {
if (fileGen < 14) m_TargetPlatform = (BuildTarget)reader.ReadInt32();
if (!Enum.IsDefined(typeof(BuildTarget), m_TargetPlatform))
{ {
int classID = this.reader.ReadInt32(); m_TargetPlatform = BuildTarget.UnknownPlatform;
string baseType = this.reader.ReadStringToNull(); }
string baseName = this.reader.ReadStringToNull(); }
this.reader.Position += 20; platformStr = m_TargetPlatform.ToString();
int memberCount = this.reader.ReadInt32(); if (header.m_Version >= 14)
{
serializedTypeTrees = reader.ReadBoolean();
}
var cb = new List<ClassMember>(); // Read types
for (int m = 0; m < memberCount; m++) int typeCount = reader.ReadInt32();
{ for (int i = 0; i < typeCount; i++)
readBase(cb, 1); {
} if (header.m_Version < 14)
{
var aClass = new ClassStruct { ID = classID, Text = (baseType + " " + baseName), members = cb }; int classID = reader.ReadInt32();
aClass.SubItems.Add(classID.ToString()); var typeTreeList = new List<TypeTree>();
ClassStructures.Add(classID, aClass); ReadTypeTree(typeTreeList, 0);
m_Type.Add(classID, typeTreeList);
} }
else else
{ {
readBase5(); ReadTypeTree5();
} }
} }
if (fileGen >= 7 && fileGen < 14) if (header.m_Version >= 7 && header.m_Version < 14)
{ {
this.reader.Position += 4; //azero var bigIDEnabled = reader.ReadInt32();
} }
int assetCount = this.reader.ReadInt32(); // Read Objects
int objectCount = reader.ReadInt32();
#region asset preload table string assetIDfmt = "D" + objectCount.ToString().Length; //format for unique ID
string assetIDfmt = "D" + assetCount.ToString().Length; //format for unique ID
for (int i = 0; i < assetCount; i++) for (int i = 0; i < objectCount; i++)
{ {
//each table entry is aligned individually, not the whole table //each table entry is aligned individually, not the whole table
if (fileGen >= 14) if (header.m_Version >= 14)
{ {
this.reader.AlignStream(4); reader.AlignStream(4);
} }
AssetPreloadData asset = new AssetPreloadData(); AssetPreloadData asset = new AssetPreloadData();
asset.m_PathID = fileGen < 14 ? this.reader.ReadInt32() : this.reader.ReadInt64(); asset.m_PathID = header.m_Version < 14 ? reader.ReadInt32() : reader.ReadInt64();
asset.Offset = this.reader.ReadUInt32(); asset.Offset = reader.ReadUInt32();
asset.Offset += dataOffset; asset.Offset += header.m_DataOffset;
asset.Size = this.reader.ReadInt32(); asset.Size = reader.ReadInt32();
if (fileGen > 15) if (header.m_Version > 15)
{ {
int index = this.reader.ReadInt32(); int index = reader.ReadInt32();
asset.Type1 = classIDs[index][0]; asset.Type1 = classIDs[index][0];
asset.Type2 = classIDs[index][1]; asset.Type2 = classIDs[index][1];
} }
else else
{ {
asset.Type1 = this.reader.ReadInt32(); asset.Type1 = reader.ReadInt32();
asset.Type2 = this.reader.ReadUInt16(); asset.Type2 = reader.ReadUInt16();
this.reader.Position += 2; reader.Position += 2;
} }
if (fileGen == 15) if (header.m_Version == 15)
{ {
byte unknownByte = this.reader.ReadByte(); byte unknownByte = reader.ReadByte();
//this is a single byte, not an int32 //this is a single byte, not an int32
//the next entry is aligned after this //the next entry is aligned after this
//but not the last! //but not the last!
@ -315,22 +289,45 @@ namespace AssetStudio
preloadTable.Add(asset.m_PathID, asset); preloadTable.Add(asset.m_PathID, asset);
#region read BuildSettings to get version for version 2.x files #region read BuildSettings to get version for version 2.x files
if (asset.Type == ClassIDReference.BuildSettings && fileGen == 6) if (asset.Type == ClassIDReference.BuildSettings && header.m_Version == 6)
{ {
long nextAsset = this.reader.Position; long nextAsset = reader.Position;
BuildSettings BSettings = new BuildSettings(asset); BuildSettings BSettings = new BuildSettings(asset);
m_Version = BSettings.m_Version; unityVersion = BSettings.m_Version;
this.reader.Position = nextAsset; reader.Position = nextAsset;
} }
#endregion #endregion
} }
#endregion
buildType = Regex.Replace(m_Version, @"\d", "").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries); if (header.m_Version >= 14)
var firstVersion = int.Parse(m_Version.Split('.')[0]); {
version = Regex.Matches(m_Version, @"\d").Cast<Match>().Select(m => int.Parse(m.Value)).ToArray(); //this looks like a list of assets that need to be preloaded in memory before anytihng else
int someCount = reader.ReadInt32();
for (int i = 0; i < someCount; i++)
{
int num1 = reader.ReadInt32();
reader.AlignStream(4);
long m_PathID = reader.ReadInt64();
}
}
sharedAssetsList[0].fileName = fileName; //reference itself because sharedFileIDs start from 1
int sharedFileCount = reader.ReadInt32();
for (int i = 0; i < sharedFileCount; i++)
{
var shared = new SharedAssets();
shared.aName = reader.ReadStringToNull();
reader.Position += 20;
var sharedFilePath = reader.ReadStringToNull(); //relative path
shared.fileName = Path.GetFileName(sharedFilePath);
sharedAssetsList.Add(shared);
}
buildType = Regex.Replace(unityVersion, @"\d", "").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries);
var firstVersion = int.Parse(unityVersion.Split('.')[0]);
version = Regex.Matches(unityVersion, @"\d").Cast<Match>().Select(m => int.Parse(m.Value)).ToArray();
if (firstVersion > 5)//2017 and up if (firstVersion > 5)//2017 and up
{ {
var nversion = new int[version.Length - 3]; var nversion = new int[version.Length - 3];
@ -338,28 +335,7 @@ namespace AssetStudio
Array.Copy(version, 4, nversion, 1, version.Length - 4); Array.Copy(version, 4, nversion, 1, version.Length - 4);
version = nversion; version = nversion;
} }
if (fileGen >= 14)
{
//this looks like a list of assets that need to be preloaded in memory before anytihng else
int someCount = this.reader.ReadInt32();
for (int i = 0; i < someCount; i++)
{
int num1 = this.reader.ReadInt32();
this.reader.AlignStream(4);
long m_PathID = this.reader.ReadInt64();
}
}
int sharedFileCount = this.reader.ReadInt32();
for (int i = 0; i < sharedFileCount; i++)
{
var shared = new SharedAssets();
shared.aName = this.reader.ReadStringToNull();
this.reader.Position += 20;
var sharedFilePath = this.reader.ReadStringToNull(); //relative path
shared.fileName = Path.GetFileName(sharedFilePath);
sharedAssetsList.Add(shared);
}
valid = true; valid = true;
} }
catch catch
@ -367,49 +343,58 @@ namespace AssetStudio
} }
} }
private void readBase(List<ClassMember> cb, int level) private void ReadTypeTree(List<TypeTree> typeTreeList, int depth)
{ {
string varType = reader.ReadStringToNull(); var typeTree = new TypeTree();
string varName = reader.ReadStringToNull(); typeTreeList.Add(typeTree);
int size = reader.ReadInt32(); typeTree.m_Depth = depth;
int index = reader.ReadInt32(); typeTree.m_Type = reader.ReadStringToNull();
int isArray = reader.ReadInt32(); typeTree.m_Name = reader.ReadStringToNull();
int num0 = reader.ReadInt32(); typeTree.m_ByteSize = reader.ReadInt32();
int flag = reader.ReadInt32(); if (header.m_Version == 2)
int childrenCount = reader.ReadInt32();
cb.Add(new ClassMember
{ {
Level = level - 1, var variableCount = reader.ReadInt32();
Type = varType, }
Name = varName, if (header.m_Version != 3)
Size = size, {
Flag = flag typeTree.m_Index = reader.ReadInt32();
}); }
for (int i = 0; i < childrenCount; i++) { readBase(cb, level + 1); } typeTree.m_IsArray = reader.ReadInt32();
typeTree.m_Version = reader.ReadInt32();
if (header.m_Version != 3)
{
typeTree.m_MetaFlag = reader.ReadInt32();
}
int childrenCount = reader.ReadInt32();
for (int i = 0; i < childrenCount; i++)
{
ReadTypeTree(typeTreeList, depth + 1);
}
} }
private void readBase5() private void ReadTypeTree5()
{ {
int classID = reader.ReadInt32(); int classID = reader.ReadInt32();
if (fileGen > 15)//5.5.0 and up if (header.m_Version > 15)//5.5.0 and up
{ {
reader.ReadByte(); reader.ReadByte();
int type1; int typeID = reader.ReadInt16();
if ((type1 = reader.ReadInt16()) >= 0) if (typeID >= 0)
{ {
type1 = -1 - type1; typeID = -1 - typeID;
} }
else else
{ {
type1 = classID; typeID = classID;
} }
classIDs.Add(new[] { type1, classID }); classIDs.Add(new[] { typeID, classID });
if (classID == 114) if (classID == 114)
{ {
reader.Position += 16; reader.Position += 16;
} }
classID = type1; classID = typeID;
} }
else if (classID < 0) else if (classID < 0)
{ {
@ -417,7 +402,7 @@ namespace AssetStudio
} }
reader.Position += 16; reader.Position += 16;
if (baseDefinitions) if (serializedTypeTrees)
{ {
int varCount = reader.ReadInt32(); int varCount = reader.ReadInt32();
int stringSize = reader.ReadInt32(); int stringSize = reader.ReadInt32();
@ -425,66 +410,46 @@ namespace AssetStudio
reader.Position += varCount * 24; reader.Position += varCount * 24;
using (var stringReader = new BinaryReader(new MemoryStream(reader.ReadBytes(stringSize)))) using (var stringReader = new BinaryReader(new MemoryStream(reader.ReadBytes(stringSize))))
{ {
string className = ""; var typeTreeList = new List<TypeTree>();
var classVar = new List<ClassMember>();
//build Class Structures
reader.Position -= varCount * 24 + stringSize; reader.Position -= varCount * 24 + stringSize;
for (int i = 0; i < varCount; i++) for (int i = 0; i < varCount; i++)
{ {
ushort num0 = reader.ReadUInt16(); var typeTree = new TypeTree();
byte level = reader.ReadByte(); typeTreeList.Add(typeTree);
bool isArray = reader.ReadBoolean(); typeTree.m_Version = reader.ReadUInt16();
typeTree.m_Depth = reader.ReadByte();
typeTree.m_IsArray = reader.ReadBoolean() ? 1 : 0;
ushort varTypeIndex = reader.ReadUInt16(); ushort varTypeIndex = reader.ReadUInt16();
ushort test = reader.ReadUInt16(); ushort test = reader.ReadUInt16();
string varTypeStr;
if (test == 0) //varType is an offset in the string block if (test == 0) //varType is an offset in the string block
{ {
stringReader.BaseStream.Position = varTypeIndex; stringReader.BaseStream.Position = varTypeIndex;
varTypeStr = stringReader.ReadStringToNull(); typeTree.m_Type = stringReader.ReadStringToNull();
} }
else //varType is an index in an internal strig array else //varType is an index in an internal strig array
{ {
varTypeStr = baseStrings.ContainsKey(varTypeIndex) ? baseStrings[varTypeIndex] : varTypeIndex.ToString(); typeTree.m_Type = baseStrings.ContainsKey(varTypeIndex) ? baseStrings[varTypeIndex] : varTypeIndex.ToString();
} }
ushort varNameIndex = reader.ReadUInt16(); ushort varNameIndex = reader.ReadUInt16();
test = reader.ReadUInt16(); test = reader.ReadUInt16();
string varNameStr;
if (test == 0) if (test == 0)
{ {
stringReader.BaseStream.Position = varNameIndex; stringReader.BaseStream.Position = varNameIndex;
varNameStr = stringReader.ReadStringToNull(); typeTree.m_Name = stringReader.ReadStringToNull();
} }
else else
{ {
varNameStr = baseStrings.ContainsKey(varNameIndex) ? baseStrings[varNameIndex] : varNameIndex.ToString(); typeTree.m_Name = baseStrings.ContainsKey(varNameIndex) ? baseStrings[varNameIndex] : varNameIndex.ToString();
} }
int size = reader.ReadInt32(); typeTree.m_ByteSize = reader.ReadInt32();
int index = reader.ReadInt32(); typeTree.m_Index = reader.ReadInt32();
int flag = reader.ReadInt32(); typeTree.m_MetaFlag = reader.ReadInt32();
if (index == 0)
{
className = varTypeStr + " " + varNameStr;
}
else
{
classVar.Add(new ClassMember
{
Level = level - 1,
Type = varTypeStr,
Name = varNameStr,
Size = size,
Flag = flag
});
}
} }
reader.Position += stringSize; reader.Position += stringSize;
var aClass = new ClassStruct { ID = classID, Text = className, members = classVar }; m_Type[classID] = typeTreeList;
aClass.SubItems.Add(classID.ToString());
ClassStructures[classID] = aClass;
} }
} }
} }

View File

@ -7,6 +7,7 @@ namespace AssetStudio
{ {
public enum BuildTarget public enum BuildTarget
{ {
UnknownPlatform = 3716,
DashboardWidget = 1, DashboardWidget = 1,
StandaloneOSX = 2, StandaloneOSX = 2,
StandaloneOSXPPC = 3, StandaloneOSXPPC = 3,

View File

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
{
public class ClassMember
{
public int Level;
public string Type;
public string Name;
public int Size;
public int Flag;
}
}

View File

@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace AssetStudio
{
public class ClassStruct : ListViewItem
{
public int ID;
public List<ClassMember> members;
public override string ToString()
{
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();
}
}
}

View File

@ -7,8 +7,8 @@ namespace AssetStudio
{ {
public enum EndianType public enum EndianType
{ {
BigEndian, LittleEndian,
LittleEndian BigEndian
} }
public class EndianBinaryReader : BinaryReader public class EndianBinaryReader : BinaryReader

View File

@ -107,7 +107,7 @@ namespace AssetStudio
return false; return false;
var m_MonoBehaviour = new MonoBehaviour(asset); var m_MonoBehaviour = new MonoBehaviour(asset);
string str; string str;
if (asset.Type1 != asset.Type2 && asset.sourceFile.ClassStructures.ContainsKey(asset.Type1)) if (asset.Type1 != asset.Type2 && asset.sourceFile.m_Type.ContainsKey(asset.Type1))
{ {
str = asset.Dump(); str = asset.Dump();
} }

View File

@ -44,19 +44,19 @@ namespace AssetStudio
assetsfileListHash.Add(assetsFile.upperFileName); assetsfileListHash.Add(assetsFile.upperFileName);
#region for 2.6.x find mainData and get string version #region for 2.6.x find mainData and get string version
if (assetsFile.fileGen == 6 && fileName != "mainData") if (assetsFile.header.m_Version == 6 && fileName != "mainData")
{ {
var mainDataFile = assetsfileList.Find(aFile => aFile.fileName == "mainData"); var mainDataFile = assetsfileList.Find(aFile => aFile.fileName == "mainData");
if (mainDataFile != null) if (mainDataFile != null)
{ {
assetsFile.m_Version = mainDataFile.m_Version; assetsFile.unityVersion = mainDataFile.unityVersion;
assetsFile.version = mainDataFile.version; assetsFile.version = mainDataFile.version;
assetsFile.buildType = mainDataFile.buildType; assetsFile.buildType = mainDataFile.buildType;
} }
else if (File.Exists(Path.GetDirectoryName(fullName) + "\\mainData")) else if (File.Exists(Path.GetDirectoryName(fullName) + "\\mainData"))
{ {
mainDataFile = new AssetsFile(Path.GetDirectoryName(fullName) + "\\mainData", new EndianBinaryReader(File.OpenRead(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.unityVersion = mainDataFile.unityVersion;
assetsFile.version = mainDataFile.version; assetsFile.version = mainDataFile.version;
assetsFile.buildType = mainDataFile.buildType; assetsFile.buildType = mainDataFile.buildType;
} }
@ -112,10 +112,10 @@ namespace AssetStudio
{ {
assetsFile.parentPath = parentPath ?? fullName; assetsFile.parentPath = parentPath ?? fullName;
if (assetsFile.fileGen == 6) //2.6.x and earlier don't have a string version before the preload table if (assetsFile.header.m_Version == 6) //2.6.x and earlier don't have a string version before the preload table
{ {
//make use of the bundle file version //make use of the bundle file version
assetsFile.m_Version = bundleFile.versionEngine; assetsFile.unityVersion = bundleFile.versionEngine;
assetsFile.version = Regex.Matches(bundleFile.versionEngine, @"\d").Cast<Match>().Select(m => int.Parse(m.Value)).ToArray(); 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); assetsFile.buildType = Regex.Replace(bundleFile.versionEngine, @"\d", "").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries);
} }

View File

@ -39,7 +39,7 @@ namespace AssetStudio
result.m_FileID = index; result.m_FileID = index;
} }
result.m_PathID = sourceFile.fileGen < 14 ? reader.ReadInt32() : reader.ReadInt64(); result.m_PathID = sourceFile.header.m_Version < 14 ? reader.ReadInt32() : reader.ReadInt64();
return result; return result;
} }

View File

@ -20,7 +20,7 @@ namespace AssetStudio
public static List<AssetPreloadData> exportableAssets = new List<AssetPreloadData>(); //used to hold all assets while the ListView is filtered public static List<AssetPreloadData> exportableAssets = new List<AssetPreloadData>(); //used to hold all assets while the ListView is filtered
private static HashSet<string> assetsNameHash = new HashSet<string>(); //avoid the same name asset private static HashSet<string> assetsNameHash = 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 List<AssetPreloadData> visibleAssets = new List<AssetPreloadData>(); //used to build the ListView from all or filtered assets
public static Dictionary<string, SortedDictionary<int, ClassStruct>> AllClassStructures = new Dictionary<string, SortedDictionary<int, ClassStruct>>(); public static Dictionary<string, Dictionary<int, TypeItem>> AllTypeMap = new Dictionary<string, Dictionary<int, TypeItem>>();
public static string mainPath; public static string mainPath;
public static string productName = ""; public static string productName = "";
public static bool moduleLoaded; public static bool moduleLoaded;
@ -432,16 +432,16 @@ namespace AssetStudio
//group class structures by versionv //group class structures by versionv
foreach (var assetsFile in assetsfileList) foreach (var assetsFile in assetsfileList)
{ {
if (AllClassStructures.TryGetValue(assetsFile.m_Version, out var curVer)) if (AllTypeMap.TryGetValue(assetsFile.unityVersion, out var curVer))
{ {
foreach (var uClass in assetsFile.ClassStructures) foreach (var type in assetsFile.m_Type)
{ {
curVer[uClass.Key] = uClass.Value; curVer[type.Key] = new TypeItem(type.Key, type.Value);
} }
} }
else else
{ {
AllClassStructures.Add(assetsFile.m_Version, assetsFile.ClassStructures); AllTypeMap.Add(assetsFile.unityVersion, assetsFile.m_Type.ToDictionary(x => x.Key, y => new TypeItem(y.Key, y.Value)));
} }
} }
} }

View File

@ -95,7 +95,7 @@ namespace AssetStudio
m_TextureFormat = m_Texture2D.m_TextureFormat; m_TextureFormat = m_Texture2D.m_TextureFormat;
var mMipMap = m_Texture2D.m_MipMap; var mMipMap = m_Texture2D.m_MipMap;
version = m_Texture2D.sourceFile.version; version = m_Texture2D.sourceFile.version;
var platform = m_Texture2D.sourceFile.platform; var platform = m_Texture2D.sourceFile.m_TargetPlatform;
if (version[0] < 5 || (version[0] == 5 && version[1] < 2))//5.2 down if (version[0] < 5 || (version[0] == 5 && version[1] < 2))//5.2 down
{ {
@ -587,9 +587,9 @@ namespace AssetStudio
dwABitMask = -16777216; dwABitMask = -16777216;
} }
private void SwapBytesForXbox(int platform) private void SwapBytesForXbox(BuildTarget platform)
{ {
if (platform == 11) //swap bytes for Xbox confirmed, PS3 not encountered if (platform == BuildTarget.XBOX360) //swap bytes for Xbox confirmed, PS3 not encountered
{ {
for (var i = 0; i < image_data_size / 2; i++) for (var i = 0; i < image_data_size / 2; i++)
{ {

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace AssetStudio
{
public class TypeItem : ListViewItem
{
public List<TypeTree> typeTreeList;
public TypeItem(int classID, List<TypeTree> typeTreeList)
{
this.typeTreeList = typeTreeList;
Text = typeTreeList[0].m_Type + " " + typeTreeList[0].m_Name;
SubItems.Add(classID.ToString());
}
public override string ToString()
{
var sb = new StringBuilder();
foreach (var i in typeTreeList)
{
sb.AppendFormat("{0}{1} {2} {3} {4}\r\n", new string('\t', i.m_Depth), i.m_Type, i.m_Name, i.m_ByteSize, (i.m_MetaFlag & 0x4000) != 0);
}
return sb.ToString();
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
{
public class TypeTree
{
public string m_Type;
public string m_Name;
public int m_ByteSize;
public int m_Index;
public int m_IsArray;
public int m_Version;
public int m_MetaFlag;
public int m_Depth;
}
}

View File

@ -7,9 +7,9 @@ using System.Text;
namespace AssetStudio namespace AssetStudio
{ {
public static class ClassStructHelper public static class TypeTreeHelper
{ {
public static void ReadClassString(StringBuilder sb, List<ClassMember> members, EndianBinaryReader reader) public static void ReadTypeString(StringBuilder sb, List<TypeTree> members, EndianBinaryReader reader)
{ {
for (int i = 0; i < members.Count; i++) for (int i = 0; i < members.Count; i++)
{ {
@ -17,15 +17,15 @@ namespace AssetStudio
} }
} }
private static void ReadStringValue(StringBuilder sb, List<ClassMember> members, EndianBinaryReader reader, ref int i) private static void ReadStringValue(StringBuilder sb, List<TypeTree> members, EndianBinaryReader reader, ref int i)
{ {
var member = members[i]; var member = members[i];
var level = member.Level; var level = member.m_Depth;
var varTypeStr = member.Type; var varTypeStr = member.m_Type;
var varNameStr = member.Name; var varNameStr = member.m_Name;
object value = null; object value = null;
var append = true; var append = true;
var align = (member.Flag & 0x4000) != 0; var align = (member.m_MetaFlag & 0x4000) != 0;
switch (varTypeStr) switch (varTypeStr)
{ {
case "SInt8": case "SInt8":
@ -76,7 +76,7 @@ namespace AssetStudio
break; break;
case "vector": case "vector":
{ {
if ((members[i + 1].Flag & 0x4000) != 0) if ((members[i + 1].m_MetaFlag & 0x4000) != 0)
align = true; align = true;
append = false; append = false;
sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level)), varTypeStr, varNameStr); sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level)), varTypeStr, varNameStr);
@ -96,7 +96,7 @@ namespace AssetStudio
} }
case "map": case "map":
{ {
if ((members[i + 1].Flag & 0x4000) != 0) if ((members[i + 1].m_MetaFlag & 0x4000) != 0)
align = true; align = true;
append = false; append = false;
sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level)), varTypeStr, varNameStr); sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level)), varTypeStr, varNameStr);
@ -106,7 +106,7 @@ namespace AssetStudio
var map = GetMembers(members, level, i); var map = GetMembers(members, level, i);
i += map.Count - 1; i += map.Count - 1;
map.RemoveRange(0, 4); map.RemoveRange(0, 4);
var first = GetMembers(map, map[0].Level, 0); var first = GetMembers(map, map[0].m_Depth, 0);
map.RemoveRange(0, first.Count); map.RemoveRange(0, first.Count);
var second = map; var second = map;
for (int j = 0; j < size; j++) for (int j = 0; j < size; j++)
@ -132,7 +132,7 @@ namespace AssetStudio
} }
default: default:
{ {
if (i != members.Count && members[i + 1].Type == "Array") if (i != members.Count && members[i + 1].m_Type == "Array")
{ {
goto case "vector"; goto case "vector";
} }
@ -154,26 +154,26 @@ namespace AssetStudio
reader.AlignStream(4); reader.AlignStream(4);
} }
public static ExpandoObject ReadDynamicClass(List<ClassMember> members, EndianBinaryReader reader) public static ExpandoObject ReadDynamicType(List<TypeTree> members, EndianBinaryReader reader)
{ {
var obj = new ExpandoObject(); var obj = new ExpandoObject();
var objdic = (IDictionary<string, object>)obj; var objdic = (IDictionary<string, object>)obj;
for (int i = 0; i < members.Count; i++) for (int i = 0; i < members.Count; i++)
{ {
var member = members[i]; var member = members[i];
var varNameStr = member.Name; var varNameStr = member.m_Name;
objdic[varNameStr] = ReadValue(members, reader, ref i); objdic[varNameStr] = ReadValue(members, reader, ref i);
} }
return obj; return obj;
} }
private static object ReadValue(List<ClassMember> members, EndianBinaryReader reader, ref int i) private static object ReadValue(List<TypeTree> members, EndianBinaryReader reader, ref int i)
{ {
var member = members[i]; var member = members[i];
var level = member.Level; var level = member.m_Depth;
var varTypeStr = member.Type; var varTypeStr = member.m_Type;
object value; object value;
var align = (member.Flag & 0x4000) != 0; var align = (member.m_MetaFlag & 0x4000) != 0;
switch (varTypeStr) switch (varTypeStr)
{ {
case "SInt8": case "SInt8":
@ -222,7 +222,7 @@ namespace AssetStudio
break; break;
case "vector": case "vector":
{ {
if ((members[i + 1].Flag & 0x4000) != 0) if ((members[i + 1].m_MetaFlag & 0x4000) != 0)
align = true; align = true;
var size = reader.ReadInt32(); var size = reader.ReadInt32();
var list = new List<object>(size); var list = new List<object>(size);
@ -239,14 +239,14 @@ namespace AssetStudio
} }
case "map": case "map":
{ {
if ((members[i + 1].Flag & 0x4000) != 0) if ((members[i + 1].m_MetaFlag & 0x4000) != 0)
align = true; align = true;
var size = reader.ReadInt32(); var size = reader.ReadInt32();
var dic = new List<KeyValuePair<object, object>>(size); var dic = new List<KeyValuePair<object, object>>(size);
var map = GetMembers(members, level, i); var map = GetMembers(members, level, i);
i += map.Count - 1; i += map.Count - 1;
map.RemoveRange(0, 4); map.RemoveRange(0, 4);
var first = GetMembers(map, map[0].Level, 0); var first = GetMembers(map, map[0].m_Depth, 0);
map.RemoveRange(0, first.Count); map.RemoveRange(0, first.Count);
var second = map; var second = map;
for (int j = 0; j < size; j++) for (int j = 0; j < size; j++)
@ -267,7 +267,7 @@ namespace AssetStudio
} }
default: default:
{ {
if (i != members.Count && members[i + 1].Type == "Array") if (i != members.Count && members[i + 1].m_Type == "Array")
{ {
goto case "vector"; goto case "vector";
} }
@ -279,7 +279,7 @@ namespace AssetStudio
for (int j = 0; j < @class.Count; j++) for (int j = 0; j < @class.Count; j++)
{ {
var classmember = @class[j]; var classmember = @class[j];
var name = classmember.Name; var name = classmember.m_Name;
objdic[name] = ReadValue(@class, reader, ref j); objdic[name] = ReadValue(@class, reader, ref j);
} }
value = obj; value = obj;
@ -291,14 +291,14 @@ namespace AssetStudio
return value; return value;
} }
private static List<ClassMember> GetMembers(List<ClassMember> members, int level, int index) private static List<TypeTree> GetMembers(List<TypeTree> members, int level, int index)
{ {
var member2 = new List<ClassMember>(); var member2 = new List<TypeTree>();
member2.Add(members[0]); member2.Add(members[0]);
for (int i = index + 1; i < members.Count; i++) for (int i = index + 1; i < members.Count; i++)
{ {
var member = members[i]; var member = members[i];
var level2 = member.Level; var level2 = member.m_Depth;
if (level2 <= level) if (level2 <= level)
{ {
return member2; return member2;
@ -308,7 +308,7 @@ namespace AssetStudio
return member2; return member2;
} }
public static byte[] WriteDynamicClass(ExpandoObject obj, List<ClassMember> members) public static byte[] WriteDynamicType(ExpandoObject obj, List<TypeTree> members)
{ {
var stream = new MemoryStream(); var stream = new MemoryStream();
var write = new BinaryWriter(stream); var write = new BinaryWriter(stream);
@ -316,18 +316,18 @@ namespace AssetStudio
for (int i = 0; i < members.Count; i++) for (int i = 0; i < members.Count; i++)
{ {
var member = members[i]; var member = members[i];
var varNameStr = member.Name; var varNameStr = member.m_Name;
WriteValue(objdic[varNameStr], members, write, ref i); WriteValue(objdic[varNameStr], members, write, ref i);
} }
return stream.ToArray(); return stream.ToArray();
} }
private static void WriteValue(object value, List<ClassMember> members, BinaryWriter write, ref int i) private static void WriteValue(object value, List<TypeTree> members, BinaryWriter write, ref int i)
{ {
var member = members[i]; var member = members[i];
var level = member.Level; var level = member.m_Depth;
var varTypeStr = member.Type; var varTypeStr = member.m_Type;
var align = (member.Flag & 0x4000) != 0; var align = (member.m_MetaFlag & 0x4000) != 0;
switch (varTypeStr) switch (varTypeStr)
{ {
case "SInt8": case "SInt8":
@ -376,7 +376,7 @@ namespace AssetStudio
break; break;
case "vector": case "vector":
{ {
if ((members[i + 1].Flag & 0x4000) != 0) if ((members[i + 1].m_MetaFlag & 0x4000) != 0)
align = true; align = true;
var list = (List<object>)value; var list = (List<object>)value;
var size = list.Count; var size = list.Count;
@ -393,7 +393,7 @@ namespace AssetStudio
} }
case "map": case "map":
{ {
if ((members[i + 1].Flag & 0x4000) != 0) if ((members[i + 1].m_MetaFlag & 0x4000) != 0)
align = true; align = true;
var dic = (List<KeyValuePair<object, object>>)value; var dic = (List<KeyValuePair<object, object>>)value;
var size = dic.Count; var size = dic.Count;
@ -401,7 +401,7 @@ namespace AssetStudio
var map = GetMembers(members, level, i); var map = GetMembers(members, level, i);
i += map.Count - 1; i += map.Count - 1;
map.RemoveRange(0, 4); map.RemoveRange(0, 4);
var first = GetMembers(map, map[0].Level, 0); var first = GetMembers(map, map[0].m_Depth, 0);
map.RemoveRange(0, first.Count); map.RemoveRange(0, first.Count);
var second = map; var second = map;
for (int j = 0; j < size; j++) for (int j = 0; j < size; j++)
@ -424,7 +424,7 @@ namespace AssetStudio
} }
default: default:
{ {
if (i != members.Count && members[i + 1].Type == "Array") if (i != members.Count && members[i + 1].m_Type == "Array")
{ {
goto case "vector"; goto case "vector";
} }
@ -436,7 +436,7 @@ namespace AssetStudio
for (int j = 0; j < @class.Count; j++) for (int j = 0; j < @class.Count; j++)
{ {
var classmember = @class[j]; var classmember = @class[j];
var name = classmember.Name; var name = classmember.m_Name;
WriteValue(objdic[name], @class, write, ref j); WriteValue(objdic[name], @class, write, ref j);
} }
break; break;