move files
improve Sprite read
This commit is contained in:
Perfare
2018-11-24 23:02:05 +08:00
parent 58d5a3fc37
commit 0a5b866a03
24 changed files with 316 additions and 151 deletions

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{9131C403-7FE8-444D-9AF5-5FE5DF76FF24}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>AssetStudio</RootNamespace>
<AssemblyName>AssetStudioUtility</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="dnlib">
<HintPath>..\AssetStudio\Libraries\dnlib.dll</HintPath>
</Reference>
<Reference Include="SharpDX, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\AssetStudio\Libraries\SharpDX.dll</HintPath>
</Reference>
<Reference Include="SharpDX.D3DCompiler">
<HintPath>..\AssetStudio\Libraries\SharpDX.D3DCompiler.dll</HintPath>
</Reference>
<Reference Include="SharpDX.Mathematics">
<HintPath>..\AssetStudio\Libraries\SharpDX.Mathematics.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Half">
<HintPath>..\AssetStudio\Libraries\System.Half.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AudioClipConverter.cs" />
<Compile Include="FMOD Studio API\fmod.cs" />
<Compile Include="FMOD Studio API\fmod_dsp.cs" />
<Compile Include="FMOD Studio API\fmod_errors.cs" />
<Compile Include="ModelConverter.cs" />
<Compile Include="ModelExporter.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ScriptHelper.cs" />
<Compile Include="ShaderConverter.cs" />
<Compile Include="SpriteHelper.cs" />
<Compile Include="Texture2DConverter.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetStudioFBX\AssetStudioFBX.vcxproj">
<Project>{4f8ef5ef-732b-49cf-9eb3-b23e19ae6267}</Project>
<Name>AssetStudioFBX</Name>
</ProjectReference>
<ProjectReference Include="..\AssetStudio\AssetStudio.csproj">
<Project>{af56b63c-1764-41b7-9e60-8d485422ac3b}</Project>
<Name>AssetStudio</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,178 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace AssetStudio
{
public class AudioClipConverter
{
private AudioClip m_AudioClip;
public AudioClipConverter(AudioClip audioClip)
{
m_AudioClip = audioClip;
}
public byte[] ConvertToWav()
{
var m_AudioData = m_AudioClip.m_AudioData.Value;
if (m_AudioData == null || m_AudioData.Length == 0)
return null;
var exinfo = new FMOD.CREATESOUNDEXINFO();
var result = FMOD.Factory.System_Create(out var system);
if (result != FMOD.RESULT.OK)
return null;
result = system.init(1, FMOD.INITFLAGS.NORMAL, IntPtr.Zero);
if (result != FMOD.RESULT.OK)
return null;
exinfo.cbsize = Marshal.SizeOf(exinfo);
exinfo.length = (uint)m_AudioClip.m_Size;
result = system.createSound(m_AudioData, FMOD.MODE.OPENMEMORY, ref exinfo, out var sound);
if (result != FMOD.RESULT.OK)
return null;
result = sound.getSubSound(0, out var subsound);
if (result != FMOD.RESULT.OK)
return null;
result = subsound.getFormat(out var type, out var format, out int channels, out int bits);
if (result != FMOD.RESULT.OK)
return null;
result = subsound.getDefaults(out var frequency, out int priority);
if (result != FMOD.RESULT.OK)
return null;
var sampleRate = (int)frequency;
result = subsound.getLength(out var length, FMOD.TIMEUNIT.PCMBYTES);
if (result != FMOD.RESULT.OK)
return null;
result = subsound.@lock(0, length, out var ptr1, out var ptr2, out var len1, out var len2);
if (result != FMOD.RESULT.OK)
return null;
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)channels).CopyTo(buffer, 22);
BitConverter.GetBytes(sampleRate).CopyTo(buffer, 24);
BitConverter.GetBytes(sampleRate * channels * bits / 8).CopyTo(buffer, 28);
BitConverter.GetBytes((short)(channels * bits / 8)).CopyTo(buffer, 32);
BitConverter.GetBytes((short)bits).CopyTo(buffer, 34);
Encoding.UTF8.GetBytes("data").CopyTo(buffer, 36);
BitConverter.GetBytes(len1).CopyTo(buffer, 40);
Marshal.Copy(ptr1, buffer, 44, (int)len1);
result = subsound.unlock(ptr1, ptr2, len1, len2);
if (result != FMOD.RESULT.OK)
return null;
subsound.release();
sound.release();
system.release();
return buffer;
}
public string GetExtensionName()
{
if (m_AudioClip.version[0] < 5)
{
switch (m_AudioClip.m_Type)
{
case AudioType.ACC:
return ".m4a";
case AudioType.AIFF:
return ".aif";
case AudioType.IT:
return ".it";
case AudioType.MOD:
return ".mod";
case AudioType.MPEG:
return ".mp3";
case AudioType.OGGVORBIS:
return ".ogg";
case AudioType.S3M:
return ".s3m";
case AudioType.WAV:
return ".wav";
case AudioType.XM:
return ".xm";
case AudioType.XMA:
return ".wav";
case AudioType.VAG:
return ".vag";
case AudioType.AUDIOQUEUE:
return ".fsb";
}
}
else
{
switch (m_AudioClip.m_CompressionFormat)
{
case AudioCompressionFormat.PCM:
return ".fsb";
case AudioCompressionFormat.Vorbis:
return ".fsb";
case AudioCompressionFormat.ADPCM:
return ".fsb";
case AudioCompressionFormat.MP3:
return ".fsb";
case AudioCompressionFormat.VAG:
return ".vag";
case AudioCompressionFormat.HEVAG:
return ".vag";
case AudioCompressionFormat.XMA:
return ".wav";
case AudioCompressionFormat.AAC:
return ".m4a";
case AudioCompressionFormat.GCADPCM:
return ".fsb";
case AudioCompressionFormat.ATRAC9:
return ".at9";
}
}
return ".AudioClip";
}
public bool IsFMODSupport
{
get
{
if (m_AudioClip.version[0] < 5)
{
switch (m_AudioClip.m_Type)
{
case AudioType.AIFF:
case AudioType.IT:
case AudioType.MOD:
case AudioType.S3M:
case AudioType.XM:
case AudioType.XMA:
case AudioType.VAG:
case AudioType.AUDIOQUEUE:
return true;
default:
return false;
}
}
else
{
switch (m_AudioClip.m_CompressionFormat)
{
case AudioCompressionFormat.PCM:
case AudioCompressionFormat.Vorbis:
case AudioCompressionFormat.ADPCM:
case AudioCompressionFormat.MP3:
case AudioCompressionFormat.VAG:
case AudioCompressionFormat.HEVAG:
case AudioCompressionFormat.XMA:
case AudioCompressionFormat.GCADPCM:
case AudioCompressionFormat.ATRAC9:
return true;
default:
return false;
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,103 @@
/* =================================================================================================== */
/* FMOD Studio - Error string header file. Copyright (c), Firelight Technologies Pty, Ltd. 2004-2016. */
/* */
/* Use this header if you want to store or display a string version / english explanation of */
/* the FMOD error codes. */
/* */
/* =================================================================================================== */
namespace FMOD
{
public class Error
{
public static string String(FMOD.RESULT errcode)
{
switch (errcode)
{
case FMOD.RESULT.OK: return "No errors.";
case FMOD.RESULT.ERR_BADCOMMAND: return "Tried to call a function on a data type that does not allow this type of functionality (ie calling Sound::lock on a streaming sound).";
case FMOD.RESULT.ERR_CHANNEL_ALLOC: return "Error trying to allocate a channel.";
case FMOD.RESULT.ERR_CHANNEL_STOLEN: return "The specified channel has been reused to play another sound.";
case FMOD.RESULT.ERR_DMA: return "DMA Failure. See debug output for more information.";
case FMOD.RESULT.ERR_DSP_CONNECTION: return "DSP connection error. Connection possibly caused a cyclic dependency or connected dsps with incompatible buffer counts.";
case FMOD.RESULT.ERR_DSP_DONTPROCESS: return "DSP return code from a DSP process query callback. Tells mixer not to call the process callback and therefore not consume CPU. Use this to optimize the DSP graph.";
case FMOD.RESULT.ERR_DSP_FORMAT: return "DSP Format error. A DSP unit may have attempted to connect to this network with the wrong format, or a matrix may have been set with the wrong size if the target unit has a specified channel map.";
case FMOD.RESULT.ERR_DSP_INUSE: return "DSP is already in the mixer's DSP network. It must be removed before being reinserted or released.";
case FMOD.RESULT.ERR_DSP_NOTFOUND: return "DSP connection error. Couldn't find the DSP unit specified.";
case FMOD.RESULT.ERR_DSP_RESERVED: return "DSP operation error. Cannot perform operation on this DSP as it is reserved by the system.";
case FMOD.RESULT.ERR_DSP_SILENCE: return "DSP return code from a DSP process query callback. Tells mixer silence would be produced from read, so go idle and not consume CPU. Use this to optimize the DSP graph.";
case FMOD.RESULT.ERR_DSP_TYPE: return "DSP operation cannot be performed on a DSP of this type.";
case FMOD.RESULT.ERR_FILE_BAD: return "Error loading file.";
case FMOD.RESULT.ERR_FILE_COULDNOTSEEK: return "Couldn't perform seek operation. This is a limitation of the medium (ie netstreams) or the file format.";
case FMOD.RESULT.ERR_FILE_DISKEJECTED: return "Media was ejected while reading.";
case FMOD.RESULT.ERR_FILE_EOF: return "End of file unexpectedly reached while trying to read essential data (truncated?).";
case FMOD.RESULT.ERR_FILE_ENDOFDATA: return "End of current chunk reached while trying to read data.";
case FMOD.RESULT.ERR_FILE_NOTFOUND: return "File not found.";
case FMOD.RESULT.ERR_FORMAT: return "Unsupported file or audio format.";
case FMOD.RESULT.ERR_HEADER_MISMATCH: return "There is a version mismatch between the FMOD header and either the FMOD Studio library or the FMOD Low Level library.";
case FMOD.RESULT.ERR_HTTP: return "A HTTP error occurred. This is a catch-all for HTTP errors not listed elsewhere.";
case FMOD.RESULT.ERR_HTTP_ACCESS: return "The specified resource requires authentication or is forbidden.";
case FMOD.RESULT.ERR_HTTP_PROXY_AUTH: return "Proxy authentication is required to access the specified resource.";
case FMOD.RESULT.ERR_HTTP_SERVER_ERROR: return "A HTTP server error occurred.";
case FMOD.RESULT.ERR_HTTP_TIMEOUT: return "The HTTP request timed out.";
case FMOD.RESULT.ERR_INITIALIZATION: return "FMOD was not initialized correctly to support this function.";
case FMOD.RESULT.ERR_INITIALIZED: return "Cannot call this command after System::init.";
case FMOD.RESULT.ERR_INTERNAL: return "An error occurred that wasn't supposed to. Contact support.";
case FMOD.RESULT.ERR_INVALID_FLOAT: return "Value passed in was a NaN, Inf or denormalized float.";
case FMOD.RESULT.ERR_INVALID_HANDLE: return "An invalid object handle was used.";
case FMOD.RESULT.ERR_INVALID_PARAM: return "An invalid parameter was passed to this function.";
case FMOD.RESULT.ERR_INVALID_POSITION: return "An invalid seek position was passed to this function.";
case FMOD.RESULT.ERR_INVALID_SPEAKER: return "An invalid speaker was passed to this function based on the current speaker mode.";
case FMOD.RESULT.ERR_INVALID_SYNCPOINT: return "The syncpoint did not come from this sound handle.";
case FMOD.RESULT.ERR_INVALID_THREAD: return "Tried to call a function on a thread that is not supported.";
case FMOD.RESULT.ERR_INVALID_VECTOR: return "The vectors passed in are not unit length, or perpendicular.";
case FMOD.RESULT.ERR_MAXAUDIBLE: return "Reached maximum audible playback count for this sound's soundgroup.";
case FMOD.RESULT.ERR_MEMORY: return "Not enough memory or resources.";
case FMOD.RESULT.ERR_MEMORY_CANTPOINT: return "Can't use FMOD_OPENMEMORY_POINT on non PCM source data, or non mp3/xma/adpcm data if FMOD_CREATECOMPRESSEDSAMPLE was used.";
case FMOD.RESULT.ERR_NEEDS3D: return "Tried to call a command on a 2d sound when the command was meant for 3d sound.";
case FMOD.RESULT.ERR_NEEDSHARDWARE: return "Tried to use a feature that requires hardware support.";
case FMOD.RESULT.ERR_NET_CONNECT: return "Couldn't connect to the specified host.";
case FMOD.RESULT.ERR_NET_SOCKET_ERROR: return "A socket error occurred. This is a catch-all for socket-related errors not listed elsewhere.";
case FMOD.RESULT.ERR_NET_URL: return "The specified URL couldn't be resolved.";
case FMOD.RESULT.ERR_NET_WOULD_BLOCK: return "Operation on a non-blocking socket could not complete immediately.";
case FMOD.RESULT.ERR_NOTREADY: return "Operation could not be performed because specified sound/DSP connection is not ready.";
case FMOD.RESULT.ERR_OUTPUT_ALLOCATED: return "Error initializing output device, but more specifically, the output device is already in use and cannot be reused.";
case FMOD.RESULT.ERR_OUTPUT_CREATEBUFFER: return "Error creating hardware sound buffer.";
case FMOD.RESULT.ERR_OUTPUT_DRIVERCALL: return "A call to a standard soundcard driver failed, which could possibly mean a bug in the driver or resources were missing or exhausted.";
case FMOD.RESULT.ERR_OUTPUT_FORMAT: return "Soundcard does not support the specified format.";
case FMOD.RESULT.ERR_OUTPUT_INIT: return "Error initializing output device.";
case FMOD.RESULT.ERR_OUTPUT_NODRIVERS: return "The output device has no drivers installed. If pre-init, FMOD_OUTPUT_NOSOUND is selected as the output mode. If post-init, the function just fails.";
case FMOD.RESULT.ERR_PLUGIN: return "An unspecified error has been returned from a plugin.";
case FMOD.RESULT.ERR_PLUGIN_MISSING: return "A requested output, dsp unit type or codec was not available.";
case FMOD.RESULT.ERR_PLUGIN_RESOURCE: return "A resource that the plugin requires cannot be found. (ie the DLS file for MIDI playback)";
case FMOD.RESULT.ERR_PLUGIN_VERSION: return "A plugin was built with an unsupported SDK version.";
case FMOD.RESULT.ERR_RECORD: return "An error occurred trying to initialize the recording device.";
case FMOD.RESULT.ERR_REVERB_CHANNELGROUP: return "Reverb properties cannot be set on this channel because a parent channelgroup owns the reverb connection.";
case FMOD.RESULT.ERR_REVERB_INSTANCE: return "Specified instance in FMOD_REVERB_PROPERTIES couldn't be set. Most likely because it is an invalid instance number or the reverb doesn't exist.";
case FMOD.RESULT.ERR_SUBSOUNDS: return "The error occurred because the sound referenced contains subsounds when it shouldn't have, or it doesn't contain subsounds when it should have. The operation may also not be able to be performed on a parent sound.";
case FMOD.RESULT.ERR_SUBSOUND_ALLOCATED: return "This subsound is already being used by another sound, you cannot have more than one parent to a sound. Null out the other parent's entry first.";
case FMOD.RESULT.ERR_SUBSOUND_CANTMOVE: return "Shared subsounds cannot be replaced or moved from their parent stream, such as when the parent stream is an FSB file.";
case FMOD.RESULT.ERR_TAGNOTFOUND: return "The specified tag could not be found or there are no tags.";
case FMOD.RESULT.ERR_TOOMANYCHANNELS: return "The sound created exceeds the allowable input channel count. This can be increased using the 'maxinputchannels' parameter in System::setSoftwareFormat.";
case FMOD.RESULT.ERR_TRUNCATED: return "The retrieved string is too long to fit in the supplied buffer and has been truncated.";
case FMOD.RESULT.ERR_UNIMPLEMENTED: return "Something in FMOD hasn't been implemented when it should be! contact support!";
case FMOD.RESULT.ERR_UNINITIALIZED: return "This command failed because System::init or System::setDriver was not called.";
case FMOD.RESULT.ERR_UNSUPPORTED: return "A command issued was not supported by this object. Possibly a plugin without certain callbacks specified.";
case FMOD.RESULT.ERR_VERSION: return "The version number of this file format is not supported.";
case FMOD.RESULT.ERR_EVENT_ALREADY_LOADED: return "The specified bank has already been loaded.";
case FMOD.RESULT.ERR_EVENT_LIVEUPDATE_BUSY: return "The live update connection failed due to the game already being connected.";
case FMOD.RESULT.ERR_EVENT_LIVEUPDATE_MISMATCH: return "The live update connection failed due to the game data being out of sync with the tool.";
case FMOD.RESULT.ERR_EVENT_LIVEUPDATE_TIMEOUT: return "The live update connection timed out.";
case FMOD.RESULT.ERR_EVENT_NOTFOUND: return "The requested event, bus or vca could not be found.";
case FMOD.RESULT.ERR_STUDIO_UNINITIALIZED: return "The Studio::System object is not yet initialized.";
case FMOD.RESULT.ERR_STUDIO_NOT_LOADED: return "The specified resource is not loaded, so it can't be unloaded.";
case FMOD.RESULT.ERR_INVALID_STRING: return "An invalid string was passed to this function.";
case FMOD.RESULT.ERR_ALREADY_LOCKED: return "The specified resource is already locked.";
case FMOD.RESULT.ERR_NOT_LOCKED: return "The specified resource is not locked, so it can't be unlocked.";
case FMOD.RESULT.ERR_RECORD_DISCONNECTED: return "The specified recording driver has been disconnected.";
case FMOD.RESULT.ERR_TOOMANYSAMPLES: return "The length provided exceed the allowable limit.";
default: return "Unknown error.";
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
namespace AssetStudio
{
public static class ModelExporter
{
public static void ExportFbx(string path, IImported imported, bool eulerFilter, float filterPrecision, bool allFrames, bool allBones, bool skins, float boneSize, float scaleFactor, bool flatInbetween, int versionIndex, bool isAscii)
{
Fbx.Exporter.Export(path, imported, eulerFilter, filterPrecision, allFrames, allBones, skins, boneSize, scaleFactor, flatInbetween, versionIndex, isAscii);
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("AssetStudioTools")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AssetStudioTools")]
[assembly: AssemblyCopyright("Copyright © Perfare 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("9131c403-7fe8-444d-9af5-5fe5df76ff24")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,317 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using dnlib.DotNet;
namespace AssetStudio
{
//TODO unfinished
public static class ScriptHelper
{
public static string GetScriptString(ObjectReader reader, Dictionary<string, ModuleDef> moduleDic)
{
var m_MonoBehaviour = new MonoBehaviour(reader);
var sb = CreateMonoBehaviourHeader(m_MonoBehaviour);
if (m_MonoBehaviour.m_Script.TryGet(out var m_Script))
{
if (!moduleDic.TryGetValue(m_Script.m_AssemblyName, out var module))
{
return sb.ToString();
}
var typeDef = module.Assembly.Find(m_Script.m_Namespace != "" ? $"{m_Script.m_Namespace}.{m_Script.m_ClassName}" : m_Script.m_ClassName, false);
if (typeDef != null)
{
try
{
DumpType(typeDef.ToTypeSig(), sb, reader, null, -1, true);
}
catch
{
sb = CreateMonoBehaviourHeader(m_MonoBehaviour);
}
}
}
return sb.ToString();
}
private static StringBuilder CreateMonoBehaviourHeader(MonoBehaviour m_MonoBehaviour)
{
var sb = new StringBuilder();
sb.AppendLine("PPtr<GameObject> m_GameObject");
sb.AppendLine($"\tint m_FileID = {m_MonoBehaviour.m_GameObject.m_FileID}");
sb.AppendLine($"\tint64 m_PathID = {m_MonoBehaviour.m_GameObject.m_PathID}");
sb.AppendLine($"UInt8 m_Enabled = {m_MonoBehaviour.m_Enabled}");
sb.AppendLine("PPtr<MonoScript> m_Script");
sb.AppendLine($"\tint m_FileID = {m_MonoBehaviour.m_Script.m_FileID}");
sb.AppendLine($"\tint64 m_PathID = {m_MonoBehaviour.m_Script.m_PathID}");
sb.AppendLine($"string m_Name = \"{m_MonoBehaviour.m_Name}\"");
return sb;
}
private static void DumpType(TypeSig typeSig, StringBuilder sb, ObjectReader reader, string name, int indent, bool isRoot = false)
{
var typeDef = typeSig.ToTypeDefOrRef().ResolveTypeDefThrow();
if (typeSig.IsPrimitive)
{
object value = null;
switch (typeSig.TypeName)
{
case "Boolean":
value = reader.ReadBoolean();
break;
case "Byte":
value = reader.ReadByte();
break;
case "SByte":
value = reader.ReadSByte();
break;
case "Int16":
value = reader.ReadInt16();
break;
case "UInt16":
value = reader.ReadUInt16();
break;
case "Int32":
value = reader.ReadInt32();
break;
case "UInt32":
value = reader.ReadUInt32();
break;
case "Int64":
value = reader.ReadInt64();
break;
case "UInt64":
value = reader.ReadUInt64();
break;
case "Single":
value = reader.ReadSingle();
break;
case "Double":
value = reader.ReadDouble();
break;
case "Char":
value = reader.ReadChar();
break;
}
reader.AlignStream(4);
sb.AppendLine($"{new string('\t', indent)}{typeDef.Name} {name} = {value}");
return;
}
if (typeSig.FullName == "System.String")
{
sb.AppendLine($"{new string('\t', indent)}{typeDef.Name} {name} = \"{reader.ReadAlignedString()}\"");
return;
}
if (typeSig.FullName == "System.Object")
{
return;
}
if (typeDef.IsDelegate)
{
return;
}
if (typeSig is ArraySigBase)
{
if (!typeDef.IsEnum && !IsBaseType(typeDef) && !IsAssignFromUnityObject(typeDef) && !IsEngineType(typeDef) && !typeDef.IsSerializable)
{
return;
}
var size = reader.ReadInt32();
sb.AppendLine($"{new string('\t', indent)}{typeSig.TypeName} {name}");
sb.AppendLine($"{new string('\t', indent + 1)}int size = {size}");
for (int i = 0; i < size; i++)
{
sb.AppendLine($"{new string('\t', indent + 2)}[{i}]");
DumpType(typeDef.ToTypeSig(), sb, reader, "data", indent + 2);
}
return;
}
if (!isRoot && typeSig is GenericInstSig genericInstSig)
{
if (genericInstSig.GenericArguments.Count == 1)
{
var type = genericInstSig.GenericArguments[0].ToTypeDefOrRef().ResolveTypeDefThrow();
if (!type.IsEnum && !IsBaseType(type) && !IsAssignFromUnityObject(type) && !IsEngineType(type) && !type.IsSerializable)
{
return;
}
var size = reader.ReadInt32();
sb.AppendLine($"{new string('\t', indent)}{typeSig.TypeName} {name}");
sb.AppendLine($"{new string('\t', indent + 1)}int size = {size}");
for (int i = 0; i < size; i++)
{
sb.AppendLine($"{new string('\t', indent + 2)}[{i}]");
DumpType(genericInstSig.GenericArguments[0], sb, reader, "data", indent + 2);
}
}
return;
}
if (indent != -1 && IsAssignFromUnityObject(typeDef))
{
var pptr = new PPtr<Object>(reader);
sb.AppendLine($"{new string('\t', indent)}PPtr<{typeDef.Name}> {name} = {{fileID: {pptr.m_FileID}, pathID: {pptr.m_PathID}}}");
return;
}
if (typeDef.IsEnum)
{
sb.AppendLine($"{new string('\t', indent)}{typeDef.Name} {name} = {reader.ReadUInt32()}");
return;
}
if (indent != -1 && !IsEngineType(typeDef) && !typeDef.IsSerializable)
{
return;
}
if (typeDef.FullName == "UnityEngine.Rect")
{
sb.AppendLine($"{new string('\t', indent)}{typeDef.Name} {name}");
var rect = reader.ReadSingleArray(4);
return;
}
if (typeDef.FullName == "UnityEngine.LayerMask")
{
sb.AppendLine($"{new string('\t', indent)}{typeDef.Name} {name}");
var value = reader.ReadInt32();
return;
}
if (typeDef.FullName == "UnityEngine.AnimationCurve")
{
sb.AppendLine($"{new string('\t', indent)}{typeDef.Name} {name}");
var animationCurve = new AnimationCurve<float>(reader, reader.ReadSingle);
return;
}
if (typeDef.FullName == "UnityEngine.Gradient")
{
sb.AppendLine($"{new string('\t', indent)}{typeDef.Name} {name}");
if (reader.version[0] == 5 && reader.version[1] < 5)
reader.Position += 68;
else if (reader.version[0] == 5 && reader.version[1] < 6)
reader.Position += 72;
else
reader.Position += 168;
return;
}
if (typeDef.FullName == "UnityEngine.RectOffset")
{
sb.AppendLine($"{new string('\t', indent)}{typeDef.Name} {name}");
var left = reader.ReadSingle();
var right = reader.ReadSingle();
var top = reader.ReadSingle();
var bottom = reader.ReadSingle();
return;
}
if (typeDef.FullName == "UnityEngine.GUIStyle") //TODO
{
throw new NotSupportedException();
}
if (typeDef.IsClass || typeDef.IsValueType)
{
if (name != null && indent != -1)
{
sb.AppendLine($"{new string('\t', indent)}{typeDef.Name} {name}");
}
if (indent == -1 && typeDef.BaseType.FullName != "UnityEngine.Object")
{
DumpType(typeDef.BaseType.ToTypeSig(), sb, reader, null, indent, true);
}
if (indent != -1 && typeDef.BaseType.FullName != "System.Object")
{
DumpType(typeDef.BaseType.ToTypeSig(), sb, reader, null, indent, true);
}
foreach (var fieldDef in typeDef.Fields)
{
var access = fieldDef.Access & FieldAttributes.FieldAccessMask;
if (access != FieldAttributes.Public)
{
if (fieldDef.CustomAttributes.Any(x => x.TypeFullName.Contains("SerializeField")))
{
DumpType(fieldDef.FieldType, sb, reader, fieldDef.Name, indent + 1);
}
}
else if ((fieldDef.Attributes & FieldAttributes.Static) == 0 && (fieldDef.Attributes & FieldAttributes.InitOnly) == 0 && (fieldDef.Attributes & FieldAttributes.NotSerialized) == 0)
{
DumpType(fieldDef.FieldType, sb, reader, fieldDef.Name, indent + 1);
}
}
}
}
private static bool IsAssignFromUnityObject(TypeDef typeDef)
{
if (typeDef.FullName == "UnityEngine.Object")
{
return true;
}
if (typeDef.BaseType != null)
{
if (typeDef.BaseType.FullName == "UnityEngine.Object")
{
return true;
}
while (true)
{
typeDef = typeDef.BaseType.ResolveTypeDefThrow();
if (typeDef.BaseType == null)
{
break;
}
if (typeDef.BaseType.FullName == "UnityEngine.Object")
{
return true;
}
}
}
return false;
}
private static bool IsBaseType(IFullName typeDef)
{
switch (typeDef.FullName)
{
case "System.Boolean":
case "System.Byte":
case "System.SByte":
case "System.Int16":
case "System.UInt16":
case "System.Int32":
case "System.UInt32":
case "System.Int64":
case "System.UInt64":
case "System.Single":
case "System.Double":
case "System.String":
return true;
default:
return false;
}
}
private static bool IsEngineType(IFullName typeDef)
{
switch (typeDef.FullName)
{
case "UnityEngine.AnimationCurve":
case "UnityEngine.Bounds":
case "UnityEngine.BoundsInt":
case "UnityEngine.Color":
case "UnityEngine.Color32":
case "UnityEngine.Gradient":
case "UnityEngine.GUIStyle":
case "UnityEngine.LayerMask":
case "UnityEngine.Matrix4x4":
case "UnityEngine.Quaternion":
case "UnityEngine.Rect":
case "UnityEngine.RectInt":
case "UnityEngine.RectOffset":
case "UnityEngine.Vector2":
case "UnityEngine.Vector2Int":
case "UnityEngine.Vector3":
case "UnityEngine.Vector3Int":
case "UnityEngine.Vector4":
return true;
default:
return false;
}
}
}
}

View File

@ -0,0 +1,198 @@
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Lz4;
using SharpDX.D3DCompiler;
namespace AssetStudio
{
public static class ShaderConverter
{
public static string Convert(Shader shader)
{
if (shader.m_SubProgramBlob != null) //5.3 - 5.4
{
var decompressedBytes = new byte[shader.decompressedSize];
using (var decoder = new Lz4DecoderStream(new MemoryStream(shader.m_SubProgramBlob)))
{
decoder.Read(decompressedBytes, 0, (int)shader.decompressedSize);
}
using (var blobReader = new BinaryReader(new MemoryStream(decompressedBytes)))
{
var program = new ShaderProgram(blobReader);
return program.Export(Encoding.UTF8.GetString(shader.m_Script));
}
}
if (shader.compressedBlob != null) //5.5 and up
{
//TODO
/*for (var i = 0; i < shader.platforms.Count; i++)
{
var compressedBytes = new byte[shader.compressedLengths[i]];
Array.Copy(shader.compressedBlob, shader.offsets[i], compressedBytes, 0, shader.compressedLengths[i]);
var decompressedBytes = new byte[shader.decompressedLengths[i]];
using (var decoder = new Lz4DecoderStream(new MemoryStream(compressedBytes)))
{
decoder.Read(decompressedBytes, 0, (int)shader.decompressedLengths[i]);
}
using (var blobReader = new BinaryReader(new MemoryStream(decompressedBytes)))
{
new ShaderProgram(blobReader);
}
}*/
return shader.Dump();
}
return Encoding.UTF8.GetString(shader.m_Script);
}
}
public class ShaderProgram
{
private ShaderSubProgram[] m_SubPrograms;
public ShaderProgram(BinaryReader reader)
{
var subProgramsCapacity = reader.ReadInt32();
m_SubPrograms = new ShaderSubProgram[subProgramsCapacity];
for (int i = 0; i < subProgramsCapacity; i++)
{
reader.BaseStream.Position = 4 + i * 8;
var offset = reader.ReadInt32();
reader.BaseStream.Position = offset;
m_SubPrograms[i] = new ShaderSubProgram(reader);
}
}
public string Export(string shader)
{
var evaluator = new MatchEvaluator(match =>
{
var index = int.Parse(match.Groups[1].Value);
return m_SubPrograms[index].Export();
});
shader = Regex.Replace(shader, "GpuProgramIndex (.+)", evaluator);
return shader;
}
}
public class ShaderSubProgram
{
private int magic;
public ShaderGpuProgramType m_ProgramType;
public string[] m_Keywords;
public byte[] m_ProgramCode;
public ShaderSubProgram(BinaryReader reader)
{
//LoadGpuProgramFromData
// 201509030 - Unity 5.3
// 201510240 - Unity 5.4
// 201608170 - Unity 5.5
// 201609010 - Unity 5.6, 2017.1 & 2017.2
// 201708220 - Unity 2017.3, Unity 2017.4 & Unity 2018.1
// 201802150 - Unity 2018.2
magic = reader.ReadInt32();
m_ProgramType = (ShaderGpuProgramType)reader.ReadInt32();
reader.BaseStream.Position += 12;
if (magic >= 201608170) //5.5.0 and up
{
reader.BaseStream.Position += 4;
}
var keywordCount = reader.ReadInt32();
m_Keywords = new string[keywordCount];
for (int i = 0; i < keywordCount; i++)
{
m_Keywords[i] = reader.ReadAlignedString();
}
m_ProgramCode = reader.ReadBytes(reader.ReadInt32());
reader.AlignStream(4);
//TODO
}
public string Export()
{
var sb = new StringBuilder();
if (m_Keywords.Length > 0)
{
sb.Append("Keywords { ");
foreach (string keyword in m_Keywords)
{
sb.Append($"\"{keyword}\" ");
}
sb.Append("}\n");
}
sb.Append("\"\n");
if (m_ProgramCode.Length > 0)
{
switch (m_ProgramType)
{
case ShaderGpuProgramType.kShaderGpuProgramGLLegacy:
case ShaderGpuProgramType.kShaderGpuProgramGLES31AEP:
case ShaderGpuProgramType.kShaderGpuProgramGLES31:
case ShaderGpuProgramType.kShaderGpuProgramGLES3:
case ShaderGpuProgramType.kShaderGpuProgramGLES:
case ShaderGpuProgramType.kShaderGpuProgramGLCore32:
case ShaderGpuProgramType.kShaderGpuProgramGLCore41:
case ShaderGpuProgramType.kShaderGpuProgramGLCore43:
sb.Append(Encoding.UTF8.GetString(m_ProgramCode));
break;
case ShaderGpuProgramType.kShaderGpuProgramDX9VertexSM20:
case ShaderGpuProgramType.kShaderGpuProgramDX9VertexSM30:
case ShaderGpuProgramType.kShaderGpuProgramDX9PixelSM20:
case ShaderGpuProgramType.kShaderGpuProgramDX9PixelSM30:
{
var shaderBytecode = new ShaderBytecode(m_ProgramCode);
sb.Append(shaderBytecode.Disassemble());
break;
}
case ShaderGpuProgramType.kShaderGpuProgramDX10Level9Vertex:
case ShaderGpuProgramType.kShaderGpuProgramDX10Level9Pixel:
case ShaderGpuProgramType.kShaderGpuProgramDX11VertexSM40:
case ShaderGpuProgramType.kShaderGpuProgramDX11VertexSM50:
case ShaderGpuProgramType.kShaderGpuProgramDX11PixelSM40:
case ShaderGpuProgramType.kShaderGpuProgramDX11PixelSM50:
case ShaderGpuProgramType.kShaderGpuProgramDX11GeometrySM40:
case ShaderGpuProgramType.kShaderGpuProgramDX11GeometrySM50:
case ShaderGpuProgramType.kShaderGpuProgramDX11HullSM50:
case ShaderGpuProgramType.kShaderGpuProgramDX11DomainSM50:
{
int start = 6;
if (magic == 201509030) // 5.3
{
start = 5;
}
var buff = new byte[m_ProgramCode.Length - start];
Buffer.BlockCopy(m_ProgramCode, start, buff, 0, buff.Length);
var shaderBytecode = new ShaderBytecode(buff);
sb.Append(shaderBytecode.Disassemble());
break;
}
case ShaderGpuProgramType.kShaderGpuProgramMetalVS:
case ShaderGpuProgramType.kShaderGpuProgramMetalFS:
using (var reader = new BinaryReader(new MemoryStream(m_ProgramCode)))
{
var fourCC = reader.ReadUInt32();
if (fourCC == 0xf00dcafe)
{
int offset = reader.ReadInt32();
reader.BaseStream.Position = offset;
}
var entryName = reader.ReadStringToNull();
var buff = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position));
sb.Append(Encoding.UTF8.GetString(buff));
}
break;
default:
sb.Append($"/*Unsupported program data {m_ProgramType}*/");
break;
}
}
sb.Append('"');
return sb.ToString();
}
}
}

View File

@ -0,0 +1,107 @@
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
namespace AssetStudio
{
public static class SpriteHelper
{
public static Bitmap GetImageFromSprite(Sprite m_Sprite)
{
if (m_Sprite.m_SpriteAtlas != null && m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlas))
{
if (m_SpriteAtlas.m_RenderDataMap.TryGetValue(m_Sprite.m_RenderDataKey, out var spriteAtlasData) && spriteAtlasData.texture.TryGet(out var m_Texture2D))
{
return CutImage(m_Texture2D, spriteAtlasData.textureRect, m_Sprite, spriteAtlasData.settingsRaw);
}
}
else
{
if (m_Sprite.m_RD.texture.TryGet(out var m_Texture2D))
{
return CutImage(m_Texture2D, m_Sprite.m_RD.textureRect, m_Sprite, m_Sprite.m_RD.settingsRaw);
}
}
return null;
}
private static Bitmap CutImage(Texture2D m_Texture2D, RectangleF textureRect, Sprite m_Sprite, SpriteSettings settingsRaw)
{
var texture2D = new Texture2DConverter(m_Texture2D);
var originalImage = texture2D.ConvertToBitmap(false);
if (originalImage != null)
{
using (originalImage)
{
var spriteImage = originalImage.Clone(textureRect, PixelFormat.Format32bppArgb);
//RotateAndFlip
switch (settingsRaw.packingRotation)
{
case SpritePackingRotation.kSPRFlipHorizontal:
spriteImage.RotateFlip(RotateFlipType.RotateNoneFlipX);
break;
case SpritePackingRotation.kSPRFlipVertical:
spriteImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
break;
case SpritePackingRotation.kSPRRotate180:
spriteImage.RotateFlip(RotateFlipType.Rotate180FlipNone);
break;
case SpritePackingRotation.kSPRRotate90:
spriteImage.RotateFlip(RotateFlipType.Rotate270FlipNone);
break;
}
/* TODO Tight
* 2017之前没有PhysicsShape
* 5.6之前使用vertices
* 5.6需要使用VertexData
*/
if (settingsRaw.packingMode == SpritePackingMode.kSPMTight && m_Sprite.m_PhysicsShape?.Length > 0) //Tight
{
try
{
using (var brush = new TextureBrush(spriteImage))
{
using (var path = new GraphicsPath())
{
var points = m_Sprite.m_PhysicsShape.Select(x => x.Select(y => new PointF(y.X, y.Y)).ToArray());
foreach (var p in points)
{
path.AddPolygon(p);
}
using (var matr = new Matrix())
{
matr.Translate(m_Sprite.m_Rect.Width * m_Sprite.m_Pivot.X, m_Sprite.m_Rect.Height * m_Sprite.m_Pivot.Y);
matr.Scale(m_Sprite.m_PixelsToUnits, m_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);
return bitmap;
}
}
}
}
}
catch
{
spriteImage = originalImage.Clone(textureRect, PixelFormat.Format32bppArgb);
spriteImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
return spriteImage;
}
}
//Rectangle
spriteImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
return spriteImage;
}
}
return null;
}
}
}

File diff suppressed because it is too large Load Diff