diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c4efe2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,261 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc \ No newline at end of file diff --git a/AzurLaneLive2DExtract.sln b/AzurLaneLive2DExtract.sln new file mode 100644 index 0000000..cbee701 --- /dev/null +++ b/AzurLaneLive2DExtract.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2016 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzurLaneLive2DExtract", "AzurLaneLive2DExtract\AzurLaneLive2DExtract.csproj", "{FEC68C25-0231-46BC-B7B9-2E2D85EB085E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FEC68C25-0231-46BC-B7B9-2E2D85EB085E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FEC68C25-0231-46BC-B7B9-2E2D85EB085E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FEC68C25-0231-46BC-B7B9-2E2D85EB085E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FEC68C25-0231-46BC-B7B9-2E2D85EB085E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D6204695-C46F-46AA-9E0A-CCA7029EE5C5} + EndGlobalSection +EndGlobal diff --git a/AzurLaneLive2DExtract/7zip/Common/CRC.cs b/AzurLaneLive2DExtract/7zip/Common/CRC.cs new file mode 100644 index 0000000..82cc857 --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Common/CRC.cs @@ -0,0 +1,55 @@ +// Common/CRC.cs + +namespace SevenZip +{ + class CRC + { + public static readonly uint[] Table; + + static CRC() + { + Table = new uint[256]; + const uint kPoly = 0xEDB88320; + for (uint i = 0; i < 256; i++) + { + uint r = i; + for (int j = 0; j < 8; j++) + if ((r & 1) != 0) + r = (r >> 1) ^ kPoly; + else + r >>= 1; + Table[i] = r; + } + } + + uint _value = 0xFFFFFFFF; + + public void Init() { _value = 0xFFFFFFFF; } + + public void UpdateByte(byte b) + { + _value = Table[(((byte)(_value)) ^ b)] ^ (_value >> 8); + } + + public void Update(byte[] data, uint offset, uint size) + { + for (uint i = 0; i < size; i++) + _value = Table[(((byte)(_value)) ^ data[offset + i])] ^ (_value >> 8); + } + + public uint GetDigest() { return _value ^ 0xFFFFFFFF; } + + static uint CalculateDigest(byte[] data, uint offset, uint size) + { + CRC crc = new CRC(); + // crc.Init(); + crc.Update(data, offset, size); + return crc.GetDigest(); + } + + static bool VerifyDigest(uint digest, byte[] data, uint offset, uint size) + { + return (CalculateDigest(data, offset, size) == digest); + } + } +} diff --git a/AzurLaneLive2DExtract/7zip/Common/CommandLineParser.cs b/AzurLaneLive2DExtract/7zip/Common/CommandLineParser.cs new file mode 100644 index 0000000..8eabf59 --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Common/CommandLineParser.cs @@ -0,0 +1,274 @@ +// CommandLineParser.cs + +using System; +using System.Collections; + +namespace SevenZip.CommandLineParser +{ + public enum SwitchType + { + Simple, + PostMinus, + LimitedPostString, + UnLimitedPostString, + PostChar + } + + public class SwitchForm + { + public string IDString; + public SwitchType Type; + public bool Multi; + public int MinLen; + public int MaxLen; + public string PostCharSet; + + public SwitchForm(string idString, SwitchType type, bool multi, + int minLen, int maxLen, string postCharSet) + { + IDString = idString; + Type = type; + Multi = multi; + MinLen = minLen; + MaxLen = maxLen; + PostCharSet = postCharSet; + } + public SwitchForm(string idString, SwitchType type, bool multi, int minLen): + this(idString, type, multi, minLen, 0, "") + { + } + public SwitchForm(string idString, SwitchType type, bool multi): + this(idString, type, multi, 0) + { + } + } + + public class SwitchResult + { + public bool ThereIs; + public bool WithMinus; + public ArrayList PostStrings = new ArrayList(); + public int PostCharIndex; + public SwitchResult() + { + ThereIs = false; + } + } + + public class Parser + { + public ArrayList NonSwitchStrings = new ArrayList(); + SwitchResult[] _switches; + + public Parser(int numSwitches) + { + _switches = new SwitchResult[numSwitches]; + for (int i = 0; i < numSwitches; i++) + _switches[i] = new SwitchResult(); + } + + bool ParseString(string srcString, SwitchForm[] switchForms) + { + int len = srcString.Length; + if (len == 0) + return false; + int pos = 0; + if (!IsItSwitchChar(srcString[pos])) + return false; + while (pos < len) + { + if (IsItSwitchChar(srcString[pos])) + pos++; + const int kNoLen = -1; + int matchedSwitchIndex = 0; + int maxLen = kNoLen; + for (int switchIndex = 0; switchIndex < _switches.Length; switchIndex++) + { + int switchLen = switchForms[switchIndex].IDString.Length; + if (switchLen <= maxLen || pos + switchLen > len) + continue; + if (String.Compare(switchForms[switchIndex].IDString, 0, + srcString, pos, switchLen, true) == 0) + { + matchedSwitchIndex = switchIndex; + maxLen = switchLen; + } + } + if (maxLen == kNoLen) + throw new Exception("maxLen == kNoLen"); + SwitchResult matchedSwitch = _switches[matchedSwitchIndex]; + SwitchForm switchForm = switchForms[matchedSwitchIndex]; + if ((!switchForm.Multi) && matchedSwitch.ThereIs) + throw new Exception("switch must be single"); + matchedSwitch.ThereIs = true; + pos += maxLen; + int tailSize = len - pos; + SwitchType type = switchForm.Type; + switch (type) + { + case SwitchType.PostMinus: + { + if (tailSize == 0) + matchedSwitch.WithMinus = false; + else + { + matchedSwitch.WithMinus = (srcString[pos] == kSwitchMinus); + if (matchedSwitch.WithMinus) + pos++; + } + break; + } + case SwitchType.PostChar: + { + if (tailSize < switchForm.MinLen) + throw new Exception("switch is not full"); + string charSet = switchForm.PostCharSet; + const int kEmptyCharValue = -1; + if (tailSize == 0) + matchedSwitch.PostCharIndex = kEmptyCharValue; + else + { + int index = charSet.IndexOf(srcString[pos]); + if (index < 0) + matchedSwitch.PostCharIndex = kEmptyCharValue; + else + { + matchedSwitch.PostCharIndex = index; + pos++; + } + } + break; + } + case SwitchType.LimitedPostString: + case SwitchType.UnLimitedPostString: + { + int minLen = switchForm.MinLen; + if (tailSize < minLen) + throw new Exception("switch is not full"); + if (type == SwitchType.UnLimitedPostString) + { + matchedSwitch.PostStrings.Add(srcString.Substring(pos)); + return true; + } + String stringSwitch = srcString.Substring(pos, minLen); + pos += minLen; + for (int i = minLen; i < switchForm.MaxLen && pos < len; i++, pos++) + { + char c = srcString[pos]; + if (IsItSwitchChar(c)) + break; + stringSwitch += c; + } + matchedSwitch.PostStrings.Add(stringSwitch); + break; + } + } + } + return true; + + } + + public void ParseStrings(SwitchForm[] switchForms, string[] commandStrings) + { + int numCommandStrings = commandStrings.Length; + bool stopSwitch = false; + for (int i = 0; i < numCommandStrings; i++) + { + string s = commandStrings[i]; + if (stopSwitch) + NonSwitchStrings.Add(s); + else + if (s == kStopSwitchParsing) + stopSwitch = true; + else + if (!ParseString(s, switchForms)) + NonSwitchStrings.Add(s); + } + } + + public SwitchResult this[int index] { get { return _switches[index]; } } + + public static int ParseCommand(CommandForm[] commandForms, string commandString, + out string postString) + { + for (int i = 0; i < commandForms.Length; i++) + { + string id = commandForms[i].IDString; + if (commandForms[i].PostStringMode) + { + if (commandString.IndexOf(id) == 0) + { + postString = commandString.Substring(id.Length); + return i; + } + } + else + if (commandString == id) + { + postString = ""; + return i; + } + } + postString = ""; + return -1; + } + + static bool ParseSubCharsCommand(int numForms, CommandSubCharsSet[] forms, + string commandString, ArrayList indices) + { + indices.Clear(); + int numUsedChars = 0; + for (int i = 0; i < numForms; i++) + { + CommandSubCharsSet charsSet = forms[i]; + int currentIndex = -1; + int len = charsSet.Chars.Length; + for (int j = 0; j < len; j++) + { + char c = charsSet.Chars[j]; + int newIndex = commandString.IndexOf(c); + if (newIndex >= 0) + { + if (currentIndex >= 0) + return false; + if (commandString.IndexOf(c, newIndex + 1) >= 0) + return false; + currentIndex = j; + numUsedChars++; + } + } + if (currentIndex == -1 && !charsSet.EmptyAllowed) + return false; + indices.Add(currentIndex); + } + return (numUsedChars == commandString.Length); + } + const char kSwitchID1 = '-'; + const char kSwitchID2 = '/'; + + const char kSwitchMinus = '-'; + const string kStopSwitchParsing = "--"; + + static bool IsItSwitchChar(char c) + { + return (c == kSwitchID1 || c == kSwitchID2); + } + } + + public class CommandForm + { + public string IDString = ""; + public bool PostStringMode = false; + public CommandForm(string idString, bool postStringMode) + { + IDString = idString; + PostStringMode = postStringMode; + } + } + + class CommandSubCharsSet + { + public string Chars = ""; + public bool EmptyAllowed = false; + } +} diff --git a/AzurLaneLive2DExtract/7zip/Common/InBuffer.cs b/AzurLaneLive2DExtract/7zip/Common/InBuffer.cs new file mode 100644 index 0000000..7c51f0b --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Common/InBuffer.cs @@ -0,0 +1,72 @@ +// InBuffer.cs + +namespace SevenZip.Buffer +{ + public class InBuffer + { + byte[] m_Buffer; + uint m_Pos; + uint m_Limit; + uint m_BufferSize; + System.IO.Stream m_Stream; + bool m_StreamWasExhausted; + ulong m_ProcessedSize; + + public InBuffer(uint bufferSize) + { + m_Buffer = new byte[bufferSize]; + m_BufferSize = bufferSize; + } + + public void Init(System.IO.Stream stream) + { + m_Stream = stream; + m_ProcessedSize = 0; + m_Limit = 0; + m_Pos = 0; + m_StreamWasExhausted = false; + } + + public bool ReadBlock() + { + if (m_StreamWasExhausted) + return false; + m_ProcessedSize += m_Pos; + int aNumProcessedBytes = m_Stream.Read(m_Buffer, 0, (int)m_BufferSize); + m_Pos = 0; + m_Limit = (uint)aNumProcessedBytes; + m_StreamWasExhausted = (aNumProcessedBytes == 0); + return (!m_StreamWasExhausted); + } + + + public void ReleaseStream() + { + // m_Stream.Close(); + m_Stream = null; + } + + public bool ReadByte(byte b) // check it + { + if (m_Pos >= m_Limit) + if (!ReadBlock()) + return false; + b = m_Buffer[m_Pos++]; + return true; + } + + public byte ReadByte() + { + // return (byte)m_Stream.ReadByte(); + if (m_Pos >= m_Limit) + if (!ReadBlock()) + return 0xFF; + return m_Buffer[m_Pos++]; + } + + public ulong GetProcessedSize() + { + return m_ProcessedSize + m_Pos; + } + } +} diff --git a/AzurLaneLive2DExtract/7zip/Common/OutBuffer.cs b/AzurLaneLive2DExtract/7zip/Common/OutBuffer.cs new file mode 100644 index 0000000..2da16e1 --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Common/OutBuffer.cs @@ -0,0 +1,47 @@ +// OutBuffer.cs + +namespace SevenZip.Buffer +{ + public class OutBuffer + { + byte[] m_Buffer; + uint m_Pos; + uint m_BufferSize; + System.IO.Stream m_Stream; + ulong m_ProcessedSize; + + public OutBuffer(uint bufferSize) + { + m_Buffer = new byte[bufferSize]; + m_BufferSize = bufferSize; + } + + public void SetStream(System.IO.Stream stream) { m_Stream = stream; } + public void FlushStream() { m_Stream.Flush(); } + public void CloseStream() { m_Stream.Close(); } + public void ReleaseStream() { m_Stream = null; } + + public void Init() + { + m_ProcessedSize = 0; + m_Pos = 0; + } + + public void WriteByte(byte b) + { + m_Buffer[m_Pos++] = b; + if (m_Pos >= m_BufferSize) + FlushData(); + } + + public void FlushData() + { + if (m_Pos == 0) + return; + m_Stream.Write(m_Buffer, 0, (int)m_Pos); + m_Pos = 0; + } + + public ulong GetProcessedSize() { return m_ProcessedSize + m_Pos; } + } +} diff --git a/AzurLaneLive2DExtract/7zip/Compress/LZ/IMatchFinder.cs b/AzurLaneLive2DExtract/7zip/Compress/LZ/IMatchFinder.cs new file mode 100644 index 0000000..10ca2b3 --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Compress/LZ/IMatchFinder.cs @@ -0,0 +1,24 @@ +// IMatchFinder.cs + +using System; + +namespace SevenZip.Compression.LZ +{ + interface IInWindowStream + { + void SetStream(System.IO.Stream inStream); + void Init(); + void ReleaseStream(); + Byte GetIndexByte(Int32 index); + UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit); + UInt32 GetNumAvailableBytes(); + } + + interface IMatchFinder : IInWindowStream + { + void Create(UInt32 historySize, UInt32 keepAddBufferBefore, + UInt32 matchMaxLen, UInt32 keepAddBufferAfter); + UInt32 GetMatches(UInt32[] distances); + void Skip(UInt32 num); + } +} diff --git a/AzurLaneLive2DExtract/7zip/Compress/LZ/LzBinTree.cs b/AzurLaneLive2DExtract/7zip/Compress/LZ/LzBinTree.cs new file mode 100644 index 0000000..c1c006b --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Compress/LZ/LzBinTree.cs @@ -0,0 +1,367 @@ +// LzBinTree.cs + +using System; + +namespace SevenZip.Compression.LZ +{ + public class BinTree : InWindow, IMatchFinder + { + UInt32 _cyclicBufferPos; + UInt32 _cyclicBufferSize = 0; + UInt32 _matchMaxLen; + + UInt32[] _son; + UInt32[] _hash; + + UInt32 _cutValue = 0xFF; + UInt32 _hashMask; + UInt32 _hashSizeSum = 0; + + bool HASH_ARRAY = true; + + const UInt32 kHash2Size = 1 << 10; + const UInt32 kHash3Size = 1 << 16; + const UInt32 kBT2HashSize = 1 << 16; + const UInt32 kStartMaxLen = 1; + const UInt32 kHash3Offset = kHash2Size; + const UInt32 kEmptyHashValue = 0; + const UInt32 kMaxValForNormalize = ((UInt32)1 << 31) - 1; + + UInt32 kNumHashDirectBytes = 0; + UInt32 kMinMatchCheck = 4; + UInt32 kFixHashSize = kHash2Size + kHash3Size; + + public void SetType(int numHashBytes) + { + HASH_ARRAY = (numHashBytes > 2); + if (HASH_ARRAY) + { + kNumHashDirectBytes = 0; + kMinMatchCheck = 4; + kFixHashSize = kHash2Size + kHash3Size; + } + else + { + kNumHashDirectBytes = 2; + kMinMatchCheck = 2 + 1; + kFixHashSize = 0; + } + } + + public new void SetStream(System.IO.Stream stream) { base.SetStream(stream); } + public new void ReleaseStream() { base.ReleaseStream(); } + + public new void Init() + { + base.Init(); + for (UInt32 i = 0; i < _hashSizeSum; i++) + _hash[i] = kEmptyHashValue; + _cyclicBufferPos = 0; + ReduceOffsets(-1); + } + + public new void MovePos() + { + if (++_cyclicBufferPos >= _cyclicBufferSize) + _cyclicBufferPos = 0; + base.MovePos(); + if (_pos == kMaxValForNormalize) + Normalize(); + } + + public new Byte GetIndexByte(Int32 index) { return base.GetIndexByte(index); } + + public new UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit) + { return base.GetMatchLen(index, distance, limit); } + + public new UInt32 GetNumAvailableBytes() { return base.GetNumAvailableBytes(); } + + public void Create(UInt32 historySize, UInt32 keepAddBufferBefore, + UInt32 matchMaxLen, UInt32 keepAddBufferAfter) + { + if (historySize > kMaxValForNormalize - 256) + throw new Exception(); + _cutValue = 16 + (matchMaxLen >> 1); + + UInt32 windowReservSize = (historySize + keepAddBufferBefore + + matchMaxLen + keepAddBufferAfter) / 2 + 256; + + base.Create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, windowReservSize); + + _matchMaxLen = matchMaxLen; + + UInt32 cyclicBufferSize = historySize + 1; + if (_cyclicBufferSize != cyclicBufferSize) + _son = new UInt32[(_cyclicBufferSize = cyclicBufferSize) * 2]; + + UInt32 hs = kBT2HashSize; + + if (HASH_ARRAY) + { + hs = historySize - 1; + hs |= (hs >> 1); + hs |= (hs >> 2); + hs |= (hs >> 4); + hs |= (hs >> 8); + hs >>= 1; + hs |= 0xFFFF; + if (hs > (1 << 24)) + hs >>= 1; + _hashMask = hs; + hs++; + hs += kFixHashSize; + } + if (hs != _hashSizeSum) + _hash = new UInt32[_hashSizeSum = hs]; + } + + public UInt32 GetMatches(UInt32[] distances) + { + UInt32 lenLimit; + if (_pos + _matchMaxLen <= _streamPos) + lenLimit = _matchMaxLen; + else + { + lenLimit = _streamPos - _pos; + if (lenLimit < kMinMatchCheck) + { + MovePos(); + return 0; + } + } + + UInt32 offset = 0; + UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0; + UInt32 cur = _bufferOffset + _pos; + UInt32 maxLen = kStartMaxLen; // to avoid items for len < hashSize; + UInt32 hashValue, hash2Value = 0, hash3Value = 0; + + if (HASH_ARRAY) + { + UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1]; + hash2Value = temp & (kHash2Size - 1); + temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8); + hash3Value = temp & (kHash3Size - 1); + hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask; + } + else + hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8); + + UInt32 curMatch = _hash[kFixHashSize + hashValue]; + if (HASH_ARRAY) + { + UInt32 curMatch2 = _hash[hash2Value]; + UInt32 curMatch3 = _hash[kHash3Offset + hash3Value]; + _hash[hash2Value] = _pos; + _hash[kHash3Offset + hash3Value] = _pos; + if (curMatch2 > matchMinPos) + if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur]) + { + distances[offset++] = maxLen = 2; + distances[offset++] = _pos - curMatch2 - 1; + } + if (curMatch3 > matchMinPos) + if (_bufferBase[_bufferOffset + curMatch3] == _bufferBase[cur]) + { + if (curMatch3 == curMatch2) + offset -= 2; + distances[offset++] = maxLen = 3; + distances[offset++] = _pos - curMatch3 - 1; + curMatch2 = curMatch3; + } + if (offset != 0 && curMatch2 == curMatch) + { + offset -= 2; + maxLen = kStartMaxLen; + } + } + + _hash[kFixHashSize + hashValue] = _pos; + + UInt32 ptr0 = (_cyclicBufferPos << 1) + 1; + UInt32 ptr1 = (_cyclicBufferPos << 1); + + UInt32 len0, len1; + len0 = len1 = kNumHashDirectBytes; + + if (kNumHashDirectBytes != 0) + { + if (curMatch > matchMinPos) + { + if (_bufferBase[_bufferOffset + curMatch + kNumHashDirectBytes] != + _bufferBase[cur + kNumHashDirectBytes]) + { + distances[offset++] = maxLen = kNumHashDirectBytes; + distances[offset++] = _pos - curMatch - 1; + } + } + } + + UInt32 count = _cutValue; + + while(true) + { + if(curMatch <= matchMinPos || count-- == 0) + { + _son[ptr0] = _son[ptr1] = kEmptyHashValue; + break; + } + UInt32 delta = _pos - curMatch; + UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ? + (_cyclicBufferPos - delta) : + (_cyclicBufferPos - delta + _cyclicBufferSize)) << 1; + + UInt32 pby1 = _bufferOffset + curMatch; + UInt32 len = Math.Min(len0, len1); + if (_bufferBase[pby1 + len] == _bufferBase[cur + len]) + { + while(++len != lenLimit) + if (_bufferBase[pby1 + len] != _bufferBase[cur + len]) + break; + if (maxLen < len) + { + distances[offset++] = maxLen = len; + distances[offset++] = delta - 1; + if (len == lenLimit) + { + _son[ptr1] = _son[cyclicPos]; + _son[ptr0] = _son[cyclicPos + 1]; + break; + } + } + } + if (_bufferBase[pby1 + len] < _bufferBase[cur + len]) + { + _son[ptr1] = curMatch; + ptr1 = cyclicPos + 1; + curMatch = _son[ptr1]; + len1 = len; + } + else + { + _son[ptr0] = curMatch; + ptr0 = cyclicPos; + curMatch = _son[ptr0]; + len0 = len; + } + } + MovePos(); + return offset; + } + + public void Skip(UInt32 num) + { + do + { + UInt32 lenLimit; + if (_pos + _matchMaxLen <= _streamPos) + lenLimit = _matchMaxLen; + else + { + lenLimit = _streamPos - _pos; + if (lenLimit < kMinMatchCheck) + { + MovePos(); + continue; + } + } + + UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0; + UInt32 cur = _bufferOffset + _pos; + + UInt32 hashValue; + + if (HASH_ARRAY) + { + UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1]; + UInt32 hash2Value = temp & (kHash2Size - 1); + _hash[hash2Value] = _pos; + temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8); + UInt32 hash3Value = temp & (kHash3Size - 1); + _hash[kHash3Offset + hash3Value] = _pos; + hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask; + } + else + hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8); + + UInt32 curMatch = _hash[kFixHashSize + hashValue]; + _hash[kFixHashSize + hashValue] = _pos; + + UInt32 ptr0 = (_cyclicBufferPos << 1) + 1; + UInt32 ptr1 = (_cyclicBufferPos << 1); + + UInt32 len0, len1; + len0 = len1 = kNumHashDirectBytes; + + UInt32 count = _cutValue; + while (true) + { + if (curMatch <= matchMinPos || count-- == 0) + { + _son[ptr0] = _son[ptr1] = kEmptyHashValue; + break; + } + + UInt32 delta = _pos - curMatch; + UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ? + (_cyclicBufferPos - delta) : + (_cyclicBufferPos - delta + _cyclicBufferSize)) << 1; + + UInt32 pby1 = _bufferOffset + curMatch; + UInt32 len = Math.Min(len0, len1); + if (_bufferBase[pby1 + len] == _bufferBase[cur + len]) + { + while (++len != lenLimit) + if (_bufferBase[pby1 + len] != _bufferBase[cur + len]) + break; + if (len == lenLimit) + { + _son[ptr1] = _son[cyclicPos]; + _son[ptr0] = _son[cyclicPos + 1]; + break; + } + } + if (_bufferBase[pby1 + len] < _bufferBase[cur + len]) + { + _son[ptr1] = curMatch; + ptr1 = cyclicPos + 1; + curMatch = _son[ptr1]; + len1 = len; + } + else + { + _son[ptr0] = curMatch; + ptr0 = cyclicPos; + curMatch = _son[ptr0]; + len0 = len; + } + } + MovePos(); + } + while (--num != 0); + } + + void NormalizeLinks(UInt32[] items, UInt32 numItems, UInt32 subValue) + { + for (UInt32 i = 0; i < numItems; i++) + { + UInt32 value = items[i]; + if (value <= subValue) + value = kEmptyHashValue; + else + value -= subValue; + items[i] = value; + } + } + + void Normalize() + { + UInt32 subValue = _pos - _cyclicBufferSize; + NormalizeLinks(_son, _cyclicBufferSize * 2, subValue); + NormalizeLinks(_hash, _hashSizeSum, subValue); + ReduceOffsets((Int32)subValue); + } + + public void SetCutValue(UInt32 cutValue) { _cutValue = cutValue; } + } +} diff --git a/AzurLaneLive2DExtract/7zip/Compress/LZ/LzInWindow.cs b/AzurLaneLive2DExtract/7zip/Compress/LZ/LzInWindow.cs new file mode 100644 index 0000000..52d23ce --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Compress/LZ/LzInWindow.cs @@ -0,0 +1,132 @@ +// LzInWindow.cs + +using System; + +namespace SevenZip.Compression.LZ +{ + public class InWindow + { + public Byte[] _bufferBase = null; // pointer to buffer with data + System.IO.Stream _stream; + UInt32 _posLimit; // offset (from _buffer) of first byte when new block reading must be done + bool _streamEndWasReached; // if (true) then _streamPos shows real end of stream + + UInt32 _pointerToLastSafePosition; + + public UInt32 _bufferOffset; + + public UInt32 _blockSize; // Size of Allocated memory block + public UInt32 _pos; // offset (from _buffer) of curent byte + UInt32 _keepSizeBefore; // how many BYTEs must be kept in buffer before _pos + UInt32 _keepSizeAfter; // how many BYTEs must be kept buffer after _pos + public UInt32 _streamPos; // offset (from _buffer) of first not read byte from Stream + + public void MoveBlock() + { + UInt32 offset = (UInt32)(_bufferOffset) + _pos - _keepSizeBefore; + // we need one additional byte, since MovePos moves on 1 byte. + if (offset > 0) + offset--; + + UInt32 numBytes = (UInt32)(_bufferOffset) + _streamPos - offset; + + // check negative offset ???? + for (UInt32 i = 0; i < numBytes; i++) + _bufferBase[i] = _bufferBase[offset + i]; + _bufferOffset -= offset; + } + + public virtual void ReadBlock() + { + if (_streamEndWasReached) + return; + while (true) + { + int size = (int)((0 - _bufferOffset) + _blockSize - _streamPos); + if (size == 0) + return; + int numReadBytes = _stream.Read(_bufferBase, (int)(_bufferOffset + _streamPos), size); + if (numReadBytes == 0) + { + _posLimit = _streamPos; + UInt32 pointerToPostion = _bufferOffset + _posLimit; + if (pointerToPostion > _pointerToLastSafePosition) + _posLimit = (UInt32)(_pointerToLastSafePosition - _bufferOffset); + + _streamEndWasReached = true; + return; + } + _streamPos += (UInt32)numReadBytes; + if (_streamPos >= _pos + _keepSizeAfter) + _posLimit = _streamPos - _keepSizeAfter; + } + } + + void Free() { _bufferBase = null; } + + public void Create(UInt32 keepSizeBefore, UInt32 keepSizeAfter, UInt32 keepSizeReserv) + { + _keepSizeBefore = keepSizeBefore; + _keepSizeAfter = keepSizeAfter; + UInt32 blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv; + if (_bufferBase == null || _blockSize != blockSize) + { + Free(); + _blockSize = blockSize; + _bufferBase = new Byte[_blockSize]; + } + _pointerToLastSafePosition = _blockSize - keepSizeAfter; + } + + public void SetStream(System.IO.Stream stream) { _stream = stream; } + public void ReleaseStream() { _stream = null; } + + public void Init() + { + _bufferOffset = 0; + _pos = 0; + _streamPos = 0; + _streamEndWasReached = false; + ReadBlock(); + } + + public void MovePos() + { + _pos++; + if (_pos > _posLimit) + { + UInt32 pointerToPostion = _bufferOffset + _pos; + if (pointerToPostion > _pointerToLastSafePosition) + MoveBlock(); + ReadBlock(); + } + } + + public Byte GetIndexByte(Int32 index) { return _bufferBase[_bufferOffset + _pos + index]; } + + // index + limit have not to exceed _keepSizeAfter; + public UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit) + { + if (_streamEndWasReached) + if ((_pos + index) + limit > _streamPos) + limit = _streamPos - (UInt32)(_pos + index); + distance++; + // Byte *pby = _buffer + (size_t)_pos + index; + UInt32 pby = _bufferOffset + _pos + (UInt32)index; + + UInt32 i; + for (i = 0; i < limit && _bufferBase[pby + i] == _bufferBase[pby + i - distance]; i++); + return i; + } + + public UInt32 GetNumAvailableBytes() { return _streamPos - _pos; } + + public void ReduceOffsets(Int32 subValue) + { + _bufferOffset += (UInt32)subValue; + _posLimit -= (UInt32)subValue; + _pos -= (UInt32)subValue; + _streamPos -= (UInt32)subValue; + } + } +} diff --git a/AzurLaneLive2DExtract/7zip/Compress/LZ/LzOutWindow.cs b/AzurLaneLive2DExtract/7zip/Compress/LZ/LzOutWindow.cs new file mode 100644 index 0000000..c998584 --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Compress/LZ/LzOutWindow.cs @@ -0,0 +1,110 @@ +// LzOutWindow.cs + +namespace SevenZip.Compression.LZ +{ + public class OutWindow + { + byte[] _buffer = null; + uint _pos; + uint _windowSize = 0; + uint _streamPos; + System.IO.Stream _stream; + + public uint TrainSize = 0; + + public void Create(uint windowSize) + { + if (_windowSize != windowSize) + { + // System.GC.Collect(); + _buffer = new byte[windowSize]; + } + _windowSize = windowSize; + _pos = 0; + _streamPos = 0; + } + + public void Init(System.IO.Stream stream, bool solid) + { + ReleaseStream(); + _stream = stream; + if (!solid) + { + _streamPos = 0; + _pos = 0; + TrainSize = 0; + } + } + + public bool Train(System.IO.Stream stream) + { + long len = stream.Length; + uint size = (len < _windowSize) ? (uint)len : _windowSize; + TrainSize = size; + stream.Position = len - size; + _streamPos = _pos = 0; + while (size > 0) + { + uint curSize = _windowSize - _pos; + if (size < curSize) + curSize = size; + int numReadBytes = stream.Read(_buffer, (int)_pos, (int)curSize); + if (numReadBytes == 0) + return false; + size -= (uint)numReadBytes; + _pos += (uint)numReadBytes; + _streamPos += (uint)numReadBytes; + if (_pos == _windowSize) + _streamPos = _pos = 0; + } + return true; + } + + public void ReleaseStream() + { + Flush(); + _stream = null; + } + + public void Flush() + { + uint size = _pos - _streamPos; + if (size == 0) + return; + _stream.Write(_buffer, (int)_streamPos, (int)size); + if (_pos >= _windowSize) + _pos = 0; + _streamPos = _pos; + } + + public void CopyBlock(uint distance, uint len) + { + uint pos = _pos - distance - 1; + if (pos >= _windowSize) + pos += _windowSize; + for (; len > 0; len--) + { + if (pos >= _windowSize) + pos = 0; + _buffer[_pos++] = _buffer[pos++]; + if (_pos >= _windowSize) + Flush(); + } + } + + public void PutByte(byte b) + { + _buffer[_pos++] = b; + if (_pos >= _windowSize) + Flush(); + } + + public byte GetByte(uint distance) + { + uint pos = _pos - distance - 1; + if (pos >= _windowSize) + pos += _windowSize; + return _buffer[pos]; + } + } +} diff --git a/AzurLaneLive2DExtract/7zip/Compress/LZMA/LzmaBase.cs b/AzurLaneLive2DExtract/7zip/Compress/LZMA/LzmaBase.cs new file mode 100644 index 0000000..c7bca86 --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Compress/LZMA/LzmaBase.cs @@ -0,0 +1,76 @@ +// LzmaBase.cs + +namespace SevenZip.Compression.LZMA +{ + internal abstract class Base + { + public const uint kNumRepDistances = 4; + public const uint kNumStates = 12; + + // static byte []kLiteralNextStates = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; + // static byte []kMatchNextStates = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; + // static byte []kRepNextStates = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; + // static byte []kShortRepNextStates = {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + + public struct State + { + public uint Index; + public void Init() { Index = 0; } + public void UpdateChar() + { + if (Index < 4) Index = 0; + else if (Index < 10) Index -= 3; + else Index -= 6; + } + public void UpdateMatch() { Index = (uint)(Index < 7 ? 7 : 10); } + public void UpdateRep() { Index = (uint)(Index < 7 ? 8 : 11); } + public void UpdateShortRep() { Index = (uint)(Index < 7 ? 9 : 11); } + public bool IsCharState() { return Index < 7; } + } + + public const int kNumPosSlotBits = 6; + public const int kDicLogSizeMin = 0; + // public const int kDicLogSizeMax = 30; + // public const uint kDistTableSizeMax = kDicLogSizeMax * 2; + + public const int kNumLenToPosStatesBits = 2; // it's for speed optimization + public const uint kNumLenToPosStates = 1 << kNumLenToPosStatesBits; + + public const uint kMatchMinLen = 2; + + public static uint GetLenToPosState(uint len) + { + len -= kMatchMinLen; + if (len < kNumLenToPosStates) + return len; + return (uint)(kNumLenToPosStates - 1); + } + + public const int kNumAlignBits = 4; + public const uint kAlignTableSize = 1 << kNumAlignBits; + public const uint kAlignMask = (kAlignTableSize - 1); + + public const uint kStartPosModelIndex = 4; + public const uint kEndPosModelIndex = 14; + public const uint kNumPosModels = kEndPosModelIndex - kStartPosModelIndex; + + public const uint kNumFullDistances = 1 << ((int)kEndPosModelIndex / 2); + + public const uint kNumLitPosStatesBitsEncodingMax = 4; + public const uint kNumLitContextBitsMax = 8; + + public const int kNumPosStatesBitsMax = 4; + public const uint kNumPosStatesMax = (1 << kNumPosStatesBitsMax); + public const int kNumPosStatesBitsEncodingMax = 4; + public const uint kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax); + + public const int kNumLowLenBits = 3; + public const int kNumMidLenBits = 3; + public const int kNumHighLenBits = 8; + public const uint kNumLowLenSymbols = 1 << kNumLowLenBits; + public const uint kNumMidLenSymbols = 1 << kNumMidLenBits; + public const uint kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols + + (1 << kNumHighLenBits); + public const uint kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1; + } +} diff --git a/AzurLaneLive2DExtract/7zip/Compress/LZMA/LzmaDecoder.cs b/AzurLaneLive2DExtract/7zip/Compress/LZMA/LzmaDecoder.cs new file mode 100644 index 0000000..a9be39f --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Compress/LZMA/LzmaDecoder.cs @@ -0,0 +1,398 @@ +// LzmaDecoder.cs + +using System; + +namespace SevenZip.Compression.LZMA +{ + using RangeCoder; + + public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream + { + class LenDecoder + { + BitDecoder m_Choice = new BitDecoder(); + BitDecoder m_Choice2 = new BitDecoder(); + BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; + BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; + BitTreeDecoder m_HighCoder = new BitTreeDecoder(Base.kNumHighLenBits); + uint m_NumPosStates = 0; + + public void Create(uint numPosStates) + { + for (uint posState = m_NumPosStates; posState < numPosStates; posState++) + { + m_LowCoder[posState] = new BitTreeDecoder(Base.kNumLowLenBits); + m_MidCoder[posState] = new BitTreeDecoder(Base.kNumMidLenBits); + } + m_NumPosStates = numPosStates; + } + + public void Init() + { + m_Choice.Init(); + for (uint posState = 0; posState < m_NumPosStates; posState++) + { + m_LowCoder[posState].Init(); + m_MidCoder[posState].Init(); + } + m_Choice2.Init(); + m_HighCoder.Init(); + } + + public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState) + { + if (m_Choice.Decode(rangeDecoder) == 0) + return m_LowCoder[posState].Decode(rangeDecoder); + else + { + uint symbol = Base.kNumLowLenSymbols; + if (m_Choice2.Decode(rangeDecoder) == 0) + symbol += m_MidCoder[posState].Decode(rangeDecoder); + else + { + symbol += Base.kNumMidLenSymbols; + symbol += m_HighCoder.Decode(rangeDecoder); + } + return symbol; + } + } + } + + class LiteralDecoder + { + struct Decoder2 + { + BitDecoder[] m_Decoders; + public void Create() { m_Decoders = new BitDecoder[0x300]; } + public void Init() { for (int i = 0; i < 0x300; i++) m_Decoders[i].Init(); } + + public byte DecodeNormal(RangeCoder.Decoder rangeDecoder) + { + uint symbol = 1; + do + symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder); + while (symbol < 0x100); + return (byte)symbol; + } + + public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, byte matchByte) + { + uint symbol = 1; + do + { + uint matchBit = (uint)(matchByte >> 7) & 1; + matchByte <<= 1; + uint bit = m_Decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder); + symbol = (symbol << 1) | bit; + if (matchBit != bit) + { + while (symbol < 0x100) + symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder); + break; + } + } + while (symbol < 0x100); + return (byte)symbol; + } + } + + Decoder2[] m_Coders; + int m_NumPrevBits; + int m_NumPosBits; + uint m_PosMask; + + public void Create(int numPosBits, int numPrevBits) + { + if (m_Coders != null && m_NumPrevBits == numPrevBits && + m_NumPosBits == numPosBits) + return; + m_NumPosBits = numPosBits; + m_PosMask = ((uint)1 << numPosBits) - 1; + m_NumPrevBits = numPrevBits; + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + m_Coders = new Decoder2[numStates]; + for (uint i = 0; i < numStates; i++) + m_Coders[i].Create(); + } + + public void Init() + { + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + for (uint i = 0; i < numStates; i++) + m_Coders[i].Init(); + } + + uint GetState(uint pos, byte prevByte) + { return ((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits)); } + + public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte) + { return m_Coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); } + + public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte) + { return m_Coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); } + }; + + LZ.OutWindow m_OutWindow = new LZ.OutWindow(); + RangeCoder.Decoder m_RangeDecoder = new RangeCoder.Decoder(); + + BitDecoder[] m_IsMatchDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + BitDecoder[] m_IsRepDecoders = new BitDecoder[Base.kNumStates]; + BitDecoder[] m_IsRepG0Decoders = new BitDecoder[Base.kNumStates]; + BitDecoder[] m_IsRepG1Decoders = new BitDecoder[Base.kNumStates]; + BitDecoder[] m_IsRepG2Decoders = new BitDecoder[Base.kNumStates]; + BitDecoder[] m_IsRep0LongDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + + BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[Base.kNumLenToPosStates]; + BitDecoder[] m_PosDecoders = new BitDecoder[Base.kNumFullDistances - Base.kEndPosModelIndex]; + + BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(Base.kNumAlignBits); + + LenDecoder m_LenDecoder = new LenDecoder(); + LenDecoder m_RepLenDecoder = new LenDecoder(); + + LiteralDecoder m_LiteralDecoder = new LiteralDecoder(); + + uint m_DictionarySize; + uint m_DictionarySizeCheck; + + uint m_PosStateMask; + + public Decoder() + { + m_DictionarySize = 0xFFFFFFFF; + for (int i = 0; i < Base.kNumLenToPosStates; i++) + m_PosSlotDecoder[i] = new BitTreeDecoder(Base.kNumPosSlotBits); + } + + void SetDictionarySize(uint dictionarySize) + { + if (m_DictionarySize != dictionarySize) + { + m_DictionarySize = dictionarySize; + m_DictionarySizeCheck = Math.Max(m_DictionarySize, 1); + uint blockSize = Math.Max(m_DictionarySizeCheck, (1 << 12)); + m_OutWindow.Create(blockSize); + } + } + + void SetLiteralProperties(int lp, int lc) + { + if (lp > 8) + throw new InvalidParamException(); + if (lc > 8) + throw new InvalidParamException(); + m_LiteralDecoder.Create(lp, lc); + } + + void SetPosBitsProperties(int pb) + { + if (pb > Base.kNumPosStatesBitsMax) + throw new InvalidParamException(); + uint numPosStates = (uint)1 << pb; + m_LenDecoder.Create(numPosStates); + m_RepLenDecoder.Create(numPosStates); + m_PosStateMask = numPosStates - 1; + } + + bool _solid = false; + void Init(System.IO.Stream inStream, System.IO.Stream outStream) + { + m_RangeDecoder.Init(inStream); + m_OutWindow.Init(outStream, _solid); + + uint i; + for (i = 0; i < Base.kNumStates; i++) + { + for (uint j = 0; j <= m_PosStateMask; j++) + { + uint index = (i << Base.kNumPosStatesBitsMax) + j; + m_IsMatchDecoders[index].Init(); + m_IsRep0LongDecoders[index].Init(); + } + m_IsRepDecoders[i].Init(); + m_IsRepG0Decoders[i].Init(); + m_IsRepG1Decoders[i].Init(); + m_IsRepG2Decoders[i].Init(); + } + + m_LiteralDecoder.Init(); + for (i = 0; i < Base.kNumLenToPosStates; i++) + m_PosSlotDecoder[i].Init(); + // m_PosSpecDecoder.Init(); + for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++) + m_PosDecoders[i].Init(); + + m_LenDecoder.Init(); + m_RepLenDecoder.Init(); + m_PosAlignDecoder.Init(); + } + + public void Code(System.IO.Stream inStream, System.IO.Stream outStream, + Int64 inSize, Int64 outSize, ICodeProgress progress) + { + Init(inStream, outStream); + + Base.State state = new Base.State(); + state.Init(); + uint rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0; + + UInt64 nowPos64 = 0; + UInt64 outSize64 = (UInt64)outSize; + if (nowPos64 < outSize64) + { + if (m_IsMatchDecoders[state.Index << Base.kNumPosStatesBitsMax].Decode(m_RangeDecoder) != 0) + throw new DataErrorException(); + state.UpdateChar(); + byte b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, 0, 0); + m_OutWindow.PutByte(b); + nowPos64++; + } + while (nowPos64 < outSize64) + { + // UInt64 next = Math.Min(nowPos64 + (1 << 18), outSize64); + // while(nowPos64 < next) + { + uint posState = (uint)nowPos64 & m_PosStateMask; + if (m_IsMatchDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0) + { + byte b; + byte prevByte = m_OutWindow.GetByte(0); + if (!state.IsCharState()) + b = m_LiteralDecoder.DecodeWithMatchByte(m_RangeDecoder, + (uint)nowPos64, prevByte, m_OutWindow.GetByte(rep0)); + else + b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, (uint)nowPos64, prevByte); + m_OutWindow.PutByte(b); + state.UpdateChar(); + nowPos64++; + } + else + { + uint len; + if (m_IsRepDecoders[state.Index].Decode(m_RangeDecoder) == 1) + { + if (m_IsRepG0Decoders[state.Index].Decode(m_RangeDecoder) == 0) + { + if (m_IsRep0LongDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0) + { + state.UpdateShortRep(); + m_OutWindow.PutByte(m_OutWindow.GetByte(rep0)); + nowPos64++; + continue; + } + } + else + { + UInt32 distance; + if (m_IsRepG1Decoders[state.Index].Decode(m_RangeDecoder) == 0) + { + distance = rep1; + } + else + { + if (m_IsRepG2Decoders[state.Index].Decode(m_RangeDecoder) == 0) + distance = rep2; + else + { + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + len = m_RepLenDecoder.Decode(m_RangeDecoder, posState) + Base.kMatchMinLen; + state.UpdateRep(); + } + else + { + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + len = Base.kMatchMinLen + m_LenDecoder.Decode(m_RangeDecoder, posState); + state.UpdateMatch(); + uint posSlot = m_PosSlotDecoder[Base.GetLenToPosState(len)].Decode(m_RangeDecoder); + if (posSlot >= Base.kStartPosModelIndex) + { + int numDirectBits = (int)((posSlot >> 1) - 1); + rep0 = ((2 | (posSlot & 1)) << numDirectBits); + if (posSlot < Base.kEndPosModelIndex) + rep0 += BitTreeDecoder.ReverseDecode(m_PosDecoders, + rep0 - posSlot - 1, m_RangeDecoder, numDirectBits); + else + { + rep0 += (m_RangeDecoder.DecodeDirectBits( + numDirectBits - Base.kNumAlignBits) << Base.kNumAlignBits); + rep0 += m_PosAlignDecoder.ReverseDecode(m_RangeDecoder); + } + } + else + rep0 = posSlot; + } + if (rep0 >= m_OutWindow.TrainSize + nowPos64 || rep0 >= m_DictionarySizeCheck) + { + if (rep0 == 0xFFFFFFFF) + break; + throw new DataErrorException(); + } + m_OutWindow.CopyBlock(rep0, len); + nowPos64 += len; + } + } + } + m_OutWindow.Flush(); + m_OutWindow.ReleaseStream(); + m_RangeDecoder.ReleaseStream(); + } + + public void SetDecoderProperties(byte[] properties) + { + if (properties.Length < 5) + throw new InvalidParamException(); + int lc = properties[0] % 9; + int remainder = properties[0] / 9; + int lp = remainder % 5; + int pb = remainder / 5; + if (pb > Base.kNumPosStatesBitsMax) + throw new InvalidParamException(); + UInt32 dictionarySize = 0; + for (int i = 0; i < 4; i++) + dictionarySize += ((UInt32)(properties[1 + i])) << (i * 8); + SetDictionarySize(dictionarySize); + SetLiteralProperties(lp, lc); + SetPosBitsProperties(pb); + } + + public bool Train(System.IO.Stream stream) + { + _solid = true; + return m_OutWindow.Train(stream); + } + + /* + public override bool CanRead { get { return true; }} + public override bool CanWrite { get { return true; }} + public override bool CanSeek { get { return true; }} + public override long Length { get { return 0; }} + public override long Position + { + get { return 0; } + set { } + } + public override void Flush() { } + public override int Read(byte[] buffer, int offset, int count) + { + return 0; + } + public override void Write(byte[] buffer, int offset, int count) + { + } + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + return 0; + } + public override void SetLength(long value) {} + */ + } +} diff --git a/AzurLaneLive2DExtract/7zip/Compress/LZMA/LzmaEncoder.cs b/AzurLaneLive2DExtract/7zip/Compress/LZMA/LzmaEncoder.cs new file mode 100644 index 0000000..0237c51 --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Compress/LZMA/LzmaEncoder.cs @@ -0,0 +1,1480 @@ +// LzmaEncoder.cs + +using System; + +namespace SevenZip.Compression.LZMA +{ + using RangeCoder; + + public class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties + { + enum EMatchFinderType + { + BT2, + BT4, + }; + + const UInt32 kIfinityPrice = 0xFFFFFFF; + + static Byte[] g_FastPos = new Byte[1 << 11]; + + static Encoder() + { + const Byte kFastSlots = 22; + int c = 2; + g_FastPos[0] = 0; + g_FastPos[1] = 1; + for (Byte slotFast = 2; slotFast < kFastSlots; slotFast++) + { + UInt32 k = ((UInt32)1 << ((slotFast >> 1) - 1)); + for (UInt32 j = 0; j < k; j++, c++) + g_FastPos[c] = slotFast; + } + } + + static UInt32 GetPosSlot(UInt32 pos) + { + if (pos < (1 << 11)) + return g_FastPos[pos]; + if (pos < (1 << 21)) + return (UInt32)(g_FastPos[pos >> 10] + 20); + return (UInt32)(g_FastPos[pos >> 20] + 40); + } + + static UInt32 GetPosSlot2(UInt32 pos) + { + if (pos < (1 << 17)) + return (UInt32)(g_FastPos[pos >> 6] + 12); + if (pos < (1 << 27)) + return (UInt32)(g_FastPos[pos >> 16] + 32); + return (UInt32)(g_FastPos[pos >> 26] + 52); + } + + Base.State _state = new Base.State(); + Byte _previousByte; + UInt32[] _repDistances = new UInt32[Base.kNumRepDistances]; + + void BaseInit() + { + _state.Init(); + _previousByte = 0; + for (UInt32 i = 0; i < Base.kNumRepDistances; i++) + _repDistances[i] = 0; + } + + const int kDefaultDictionaryLogSize = 22; + const UInt32 kNumFastBytesDefault = 0x20; + + class LiteralEncoder + { + public struct Encoder2 + { + BitEncoder[] m_Encoders; + + public void Create() { m_Encoders = new BitEncoder[0x300]; } + + public void Init() { for (int i = 0; i < 0x300; i++) m_Encoders[i].Init(); } + + public void Encode(RangeCoder.Encoder rangeEncoder, byte symbol) + { + uint context = 1; + for (int i = 7; i >= 0; i--) + { + uint bit = (uint)((symbol >> i) & 1); + m_Encoders[context].Encode(rangeEncoder, bit); + context = (context << 1) | bit; + } + } + + public void EncodeMatched(RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol) + { + uint context = 1; + bool same = true; + for (int i = 7; i >= 0; i--) + { + uint bit = (uint)((symbol >> i) & 1); + uint state = context; + if (same) + { + uint matchBit = (uint)((matchByte >> i) & 1); + state += ((1 + matchBit) << 8); + same = (matchBit == bit); + } + m_Encoders[state].Encode(rangeEncoder, bit); + context = (context << 1) | bit; + } + } + + public uint GetPrice(bool matchMode, byte matchByte, byte symbol) + { + uint price = 0; + uint context = 1; + int i = 7; + if (matchMode) + { + for (; i >= 0; i--) + { + uint matchBit = (uint)(matchByte >> i) & 1; + uint bit = (uint)(symbol >> i) & 1; + price += m_Encoders[((1 + matchBit) << 8) + context].GetPrice(bit); + context = (context << 1) | bit; + if (matchBit != bit) + { + i--; + break; + } + } + } + for (; i >= 0; i--) + { + uint bit = (uint)(symbol >> i) & 1; + price += m_Encoders[context].GetPrice(bit); + context = (context << 1) | bit; + } + return price; + } + } + + Encoder2[] m_Coders; + int m_NumPrevBits; + int m_NumPosBits; + uint m_PosMask; + + public void Create(int numPosBits, int numPrevBits) + { + if (m_Coders != null && m_NumPrevBits == numPrevBits && m_NumPosBits == numPosBits) + return; + m_NumPosBits = numPosBits; + m_PosMask = ((uint)1 << numPosBits) - 1; + m_NumPrevBits = numPrevBits; + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + m_Coders = new Encoder2[numStates]; + for (uint i = 0; i < numStates; i++) + m_Coders[i].Create(); + } + + public void Init() + { + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + for (uint i = 0; i < numStates; i++) + m_Coders[i].Init(); + } + + public Encoder2 GetSubCoder(UInt32 pos, Byte prevByte) + { return m_Coders[((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits))]; } + } + + class LenEncoder + { + RangeCoder.BitEncoder _choice = new RangeCoder.BitEncoder(); + RangeCoder.BitEncoder _choice2 = new RangeCoder.BitEncoder(); + RangeCoder.BitTreeEncoder[] _lowCoder = new RangeCoder.BitTreeEncoder[Base.kNumPosStatesEncodingMax]; + RangeCoder.BitTreeEncoder[] _midCoder = new RangeCoder.BitTreeEncoder[Base.kNumPosStatesEncodingMax]; + RangeCoder.BitTreeEncoder _highCoder = new RangeCoder.BitTreeEncoder(Base.kNumHighLenBits); + + public LenEncoder() + { + for (UInt32 posState = 0; posState < Base.kNumPosStatesEncodingMax; posState++) + { + _lowCoder[posState] = new RangeCoder.BitTreeEncoder(Base.kNumLowLenBits); + _midCoder[posState] = new RangeCoder.BitTreeEncoder(Base.kNumMidLenBits); + } + } + + public void Init(UInt32 numPosStates) + { + _choice.Init(); + _choice2.Init(); + for (UInt32 posState = 0; posState < numPosStates; posState++) + { + _lowCoder[posState].Init(); + _midCoder[posState].Init(); + } + _highCoder.Init(); + } + + public void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState) + { + if (symbol < Base.kNumLowLenSymbols) + { + _choice.Encode(rangeEncoder, 0); + _lowCoder[posState].Encode(rangeEncoder, symbol); + } + else + { + symbol -= Base.kNumLowLenSymbols; + _choice.Encode(rangeEncoder, 1); + if (symbol < Base.kNumMidLenSymbols) + { + _choice2.Encode(rangeEncoder, 0); + _midCoder[posState].Encode(rangeEncoder, symbol); + } + else + { + _choice2.Encode(rangeEncoder, 1); + _highCoder.Encode(rangeEncoder, symbol - Base.kNumMidLenSymbols); + } + } + } + + public void SetPrices(UInt32 posState, UInt32 numSymbols, UInt32[] prices, UInt32 st) + { + UInt32 a0 = _choice.GetPrice0(); + UInt32 a1 = _choice.GetPrice1(); + UInt32 b0 = a1 + _choice2.GetPrice0(); + UInt32 b1 = a1 + _choice2.GetPrice1(); + UInt32 i = 0; + for (i = 0; i < Base.kNumLowLenSymbols; i++) + { + if (i >= numSymbols) + return; + prices[st + i] = a0 + _lowCoder[posState].GetPrice(i); + } + for (; i < Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; i++) + { + if (i >= numSymbols) + return; + prices[st + i] = b0 + _midCoder[posState].GetPrice(i - Base.kNumLowLenSymbols); + } + for (; i < numSymbols; i++) + prices[st + i] = b1 + _highCoder.GetPrice(i - Base.kNumLowLenSymbols - Base.kNumMidLenSymbols); + } + }; + + const UInt32 kNumLenSpecSymbols = Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; + + class LenPriceTableEncoder : LenEncoder + { + UInt32[] _prices = new UInt32[Base.kNumLenSymbols << Base.kNumPosStatesBitsEncodingMax]; + UInt32 _tableSize; + UInt32[] _counters = new UInt32[Base.kNumPosStatesEncodingMax]; + + public void SetTableSize(UInt32 tableSize) { _tableSize = tableSize; } + + public UInt32 GetPrice(UInt32 symbol, UInt32 posState) + { + return _prices[posState * Base.kNumLenSymbols + symbol]; + } + + void UpdateTable(UInt32 posState) + { + SetPrices(posState, _tableSize, _prices, posState * Base.kNumLenSymbols); + _counters[posState] = _tableSize; + } + + public void UpdateTables(UInt32 numPosStates) + { + for (UInt32 posState = 0; posState < numPosStates; posState++) + UpdateTable(posState); + } + + public new void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState) + { + base.Encode(rangeEncoder, symbol, posState); + if (--_counters[posState] == 0) + UpdateTable(posState); + } + } + + const UInt32 kNumOpts = 1 << 12; + class Optimal + { + public Base.State State; + + public bool Prev1IsChar; + public bool Prev2; + + public UInt32 PosPrev2; + public UInt32 BackPrev2; + + public UInt32 Price; + public UInt32 PosPrev; + public UInt32 BackPrev; + + public UInt32 Backs0; + public UInt32 Backs1; + public UInt32 Backs2; + public UInt32 Backs3; + + public void MakeAsChar() { BackPrev = 0xFFFFFFFF; Prev1IsChar = false; } + public void MakeAsShortRep() { BackPrev = 0; ; Prev1IsChar = false; } + public bool IsShortRep() { return (BackPrev == 0); } + }; + Optimal[] _optimum = new Optimal[kNumOpts]; + LZ.IMatchFinder _matchFinder = null; + RangeCoder.Encoder _rangeEncoder = new RangeCoder.Encoder(); + + RangeCoder.BitEncoder[] _isMatch = new RangeCoder.BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + RangeCoder.BitEncoder[] _isRep = new RangeCoder.BitEncoder[Base.kNumStates]; + RangeCoder.BitEncoder[] _isRepG0 = new RangeCoder.BitEncoder[Base.kNumStates]; + RangeCoder.BitEncoder[] _isRepG1 = new RangeCoder.BitEncoder[Base.kNumStates]; + RangeCoder.BitEncoder[] _isRepG2 = new RangeCoder.BitEncoder[Base.kNumStates]; + RangeCoder.BitEncoder[] _isRep0Long = new RangeCoder.BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + + RangeCoder.BitTreeEncoder[] _posSlotEncoder = new RangeCoder.BitTreeEncoder[Base.kNumLenToPosStates]; + + RangeCoder.BitEncoder[] _posEncoders = new RangeCoder.BitEncoder[Base.kNumFullDistances - Base.kEndPosModelIndex]; + RangeCoder.BitTreeEncoder _posAlignEncoder = new RangeCoder.BitTreeEncoder(Base.kNumAlignBits); + + LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder(); + LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder(); + + LiteralEncoder _literalEncoder = new LiteralEncoder(); + + UInt32[] _matchDistances = new UInt32[Base.kMatchMaxLen * 2 + 2]; + + UInt32 _numFastBytes = kNumFastBytesDefault; + UInt32 _longestMatchLength; + UInt32 _numDistancePairs; + + UInt32 _additionalOffset; + + UInt32 _optimumEndIndex; + UInt32 _optimumCurrentIndex; + + bool _longestMatchWasFound; + + UInt32[] _posSlotPrices = new UInt32[1 << (Base.kNumPosSlotBits + Base.kNumLenToPosStatesBits)]; + UInt32[] _distancesPrices = new UInt32[Base.kNumFullDistances << Base.kNumLenToPosStatesBits]; + UInt32[] _alignPrices = new UInt32[Base.kAlignTableSize]; + UInt32 _alignPriceCount; + + UInt32 _distTableSize = (kDefaultDictionaryLogSize * 2); + + int _posStateBits = 2; + UInt32 _posStateMask = (4 - 1); + int _numLiteralPosStateBits = 0; + int _numLiteralContextBits = 3; + + UInt32 _dictionarySize = (1 << kDefaultDictionaryLogSize); + UInt32 _dictionarySizePrev = 0xFFFFFFFF; + UInt32 _numFastBytesPrev = 0xFFFFFFFF; + + Int64 nowPos64; + bool _finished; + System.IO.Stream _inStream; + + EMatchFinderType _matchFinderType = EMatchFinderType.BT4; + bool _writeEndMark = false; + + bool _needReleaseMFStream; + + void Create() + { + if (_matchFinder == null) + { + LZ.BinTree bt = new LZ.BinTree(); + int numHashBytes = 4; + if (_matchFinderType == EMatchFinderType.BT2) + numHashBytes = 2; + bt.SetType(numHashBytes); + _matchFinder = bt; + } + _literalEncoder.Create(_numLiteralPosStateBits, _numLiteralContextBits); + + if (_dictionarySize == _dictionarySizePrev && _numFastBytesPrev == _numFastBytes) + return; + _matchFinder.Create(_dictionarySize, kNumOpts, _numFastBytes, Base.kMatchMaxLen + 1); + _dictionarySizePrev = _dictionarySize; + _numFastBytesPrev = _numFastBytes; + } + + public Encoder() + { + for (int i = 0; i < kNumOpts; i++) + _optimum[i] = new Optimal(); + for (int i = 0; i < Base.kNumLenToPosStates; i++) + _posSlotEncoder[i] = new RangeCoder.BitTreeEncoder(Base.kNumPosSlotBits); + } + + void SetWriteEndMarkerMode(bool writeEndMarker) + { + _writeEndMark = writeEndMarker; + } + + void Init() + { + BaseInit(); + _rangeEncoder.Init(); + + uint i; + for (i = 0; i < Base.kNumStates; i++) + { + for (uint j = 0; j <= _posStateMask; j++) + { + uint complexState = (i << Base.kNumPosStatesBitsMax) + j; + _isMatch[complexState].Init(); + _isRep0Long[complexState].Init(); + } + _isRep[i].Init(); + _isRepG0[i].Init(); + _isRepG1[i].Init(); + _isRepG2[i].Init(); + } + _literalEncoder.Init(); + for (i = 0; i < Base.kNumLenToPosStates; i++) + _posSlotEncoder[i].Init(); + for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++) + _posEncoders[i].Init(); + + _lenEncoder.Init((UInt32)1 << _posStateBits); + _repMatchLenEncoder.Init((UInt32)1 << _posStateBits); + + _posAlignEncoder.Init(); + + _longestMatchWasFound = false; + _optimumEndIndex = 0; + _optimumCurrentIndex = 0; + _additionalOffset = 0; + } + + void ReadMatchDistances(out UInt32 lenRes, out UInt32 numDistancePairs) + { + lenRes = 0; + numDistancePairs = _matchFinder.GetMatches(_matchDistances); + if (numDistancePairs > 0) + { + lenRes = _matchDistances[numDistancePairs - 2]; + if (lenRes == _numFastBytes) + lenRes += _matchFinder.GetMatchLen((int)lenRes - 1, _matchDistances[numDistancePairs - 1], + Base.kMatchMaxLen - lenRes); + } + _additionalOffset++; + } + + + void MovePos(UInt32 num) + { + if (num > 0) + { + _matchFinder.Skip(num); + _additionalOffset += num; + } + } + + UInt32 GetRepLen1Price(Base.State state, UInt32 posState) + { + return _isRepG0[state.Index].GetPrice0() + + _isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0(); + } + + UInt32 GetPureRepPrice(UInt32 repIndex, Base.State state, UInt32 posState) + { + UInt32 price; + if (repIndex == 0) + { + price = _isRepG0[state.Index].GetPrice0(); + price += _isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1(); + } + else + { + price = _isRepG0[state.Index].GetPrice1(); + if (repIndex == 1) + price += _isRepG1[state.Index].GetPrice0(); + else + { + price += _isRepG1[state.Index].GetPrice1(); + price += _isRepG2[state.Index].GetPrice(repIndex - 2); + } + } + return price; + } + + UInt32 GetRepPrice(UInt32 repIndex, UInt32 len, Base.State state, UInt32 posState) + { + UInt32 price = _repMatchLenEncoder.GetPrice(len - Base.kMatchMinLen, posState); + return price + GetPureRepPrice(repIndex, state, posState); + } + + UInt32 GetPosLenPrice(UInt32 pos, UInt32 len, UInt32 posState) + { + UInt32 price; + UInt32 lenToPosState = Base.GetLenToPosState(len); + if (pos < Base.kNumFullDistances) + price = _distancesPrices[(lenToPosState * Base.kNumFullDistances) + pos]; + else + price = _posSlotPrices[(lenToPosState << Base.kNumPosSlotBits) + GetPosSlot2(pos)] + + _alignPrices[pos & Base.kAlignMask]; + return price + _lenEncoder.GetPrice(len - Base.kMatchMinLen, posState); + } + + UInt32 Backward(out UInt32 backRes, UInt32 cur) + { + _optimumEndIndex = cur; + UInt32 posMem = _optimum[cur].PosPrev; + UInt32 backMem = _optimum[cur].BackPrev; + do + { + if (_optimum[cur].Prev1IsChar) + { + _optimum[posMem].MakeAsChar(); + _optimum[posMem].PosPrev = posMem - 1; + if (_optimum[cur].Prev2) + { + _optimum[posMem - 1].Prev1IsChar = false; + _optimum[posMem - 1].PosPrev = _optimum[cur].PosPrev2; + _optimum[posMem - 1].BackPrev = _optimum[cur].BackPrev2; + } + } + UInt32 posPrev = posMem; + UInt32 backCur = backMem; + + backMem = _optimum[posPrev].BackPrev; + posMem = _optimum[posPrev].PosPrev; + + _optimum[posPrev].BackPrev = backCur; + _optimum[posPrev].PosPrev = cur; + cur = posPrev; + } + while (cur > 0); + backRes = _optimum[0].BackPrev; + _optimumCurrentIndex = _optimum[0].PosPrev; + return _optimumCurrentIndex; + } + + UInt32[] reps = new UInt32[Base.kNumRepDistances]; + UInt32[] repLens = new UInt32[Base.kNumRepDistances]; + + + UInt32 GetOptimum(UInt32 position, out UInt32 backRes) + { + if (_optimumEndIndex != _optimumCurrentIndex) + { + UInt32 lenRes = _optimum[_optimumCurrentIndex].PosPrev - _optimumCurrentIndex; + backRes = _optimum[_optimumCurrentIndex].BackPrev; + _optimumCurrentIndex = _optimum[_optimumCurrentIndex].PosPrev; + return lenRes; + } + _optimumCurrentIndex = _optimumEndIndex = 0; + + UInt32 lenMain, numDistancePairs; + if (!_longestMatchWasFound) + { + ReadMatchDistances(out lenMain, out numDistancePairs); + } + else + { + lenMain = _longestMatchLength; + numDistancePairs = _numDistancePairs; + _longestMatchWasFound = false; + } + + UInt32 numAvailableBytes = _matchFinder.GetNumAvailableBytes() + 1; + if (numAvailableBytes < 2) + { + backRes = 0xFFFFFFFF; + return 1; + } + if (numAvailableBytes > Base.kMatchMaxLen) + numAvailableBytes = Base.kMatchMaxLen; + + UInt32 repMaxIndex = 0; + UInt32 i; + for (i = 0; i < Base.kNumRepDistances; i++) + { + reps[i] = _repDistances[i]; + repLens[i] = _matchFinder.GetMatchLen(0 - 1, reps[i], Base.kMatchMaxLen); + if (repLens[i] > repLens[repMaxIndex]) + repMaxIndex = i; + } + if (repLens[repMaxIndex] >= _numFastBytes) + { + backRes = repMaxIndex; + UInt32 lenRes = repLens[repMaxIndex]; + MovePos(lenRes - 1); + return lenRes; + } + + if (lenMain >= _numFastBytes) + { + backRes = _matchDistances[numDistancePairs - 1] + Base.kNumRepDistances; + MovePos(lenMain - 1); + return lenMain; + } + + Byte currentByte = _matchFinder.GetIndexByte(0 - 1); + Byte matchByte = _matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - 1)); + + if (lenMain < 2 && currentByte != matchByte && repLens[repMaxIndex] < 2) + { + backRes = (UInt32)0xFFFFFFFF; + return 1; + } + + _optimum[0].State = _state; + + UInt32 posState = (position & _posStateMask); + + _optimum[1].Price = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() + + _literalEncoder.GetSubCoder(position, _previousByte).GetPrice(!_state.IsCharState(), matchByte, currentByte); + _optimum[1].MakeAsChar(); + + UInt32 matchPrice = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1(); + UInt32 repMatchPrice = matchPrice + _isRep[_state.Index].GetPrice1(); + + if (matchByte == currentByte) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(_state, posState); + if (shortRepPrice < _optimum[1].Price) + { + _optimum[1].Price = shortRepPrice; + _optimum[1].MakeAsShortRep(); + } + } + + UInt32 lenEnd = ((lenMain >= repLens[repMaxIndex]) ? lenMain : repLens[repMaxIndex]); + + if(lenEnd < 2) + { + backRes = _optimum[1].BackPrev; + return 1; + } + + _optimum[1].PosPrev = 0; + + _optimum[0].Backs0 = reps[0]; + _optimum[0].Backs1 = reps[1]; + _optimum[0].Backs2 = reps[2]; + _optimum[0].Backs3 = reps[3]; + + UInt32 len = lenEnd; + do + _optimum[len--].Price = kIfinityPrice; + while (len >= 2); + + for (i = 0; i < Base.kNumRepDistances; i++) + { + UInt32 repLen = repLens[i]; + if (repLen < 2) + continue; + UInt32 price = repMatchPrice + GetPureRepPrice(i, _state, posState); + do + { + UInt32 curAndLenPrice = price + _repMatchLenEncoder.GetPrice(repLen - 2, posState); + Optimal optimum = _optimum[repLen]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = 0; + optimum.BackPrev = i; + optimum.Prev1IsChar = false; + } + } + while (--repLen >= 2); + } + + UInt32 normalMatchPrice = matchPrice + _isRep[_state.Index].GetPrice0(); + + len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); + if (len <= lenMain) + { + UInt32 offs = 0; + while (len > _matchDistances[offs]) + offs += 2; + for (; ; len++) + { + UInt32 distance = _matchDistances[offs + 1]; + UInt32 curAndLenPrice = normalMatchPrice + GetPosLenPrice(distance, len, posState); + Optimal optimum = _optimum[len]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = 0; + optimum.BackPrev = distance + Base.kNumRepDistances; + optimum.Prev1IsChar = false; + } + if (len == _matchDistances[offs]) + { + offs += 2; + if (offs == numDistancePairs) + break; + } + } + } + + UInt32 cur = 0; + + while (true) + { + cur++; + if (cur == lenEnd) + return Backward(out backRes, cur); + UInt32 newLen; + ReadMatchDistances(out newLen, out numDistancePairs); + if (newLen >= _numFastBytes) + { + _numDistancePairs = numDistancePairs; + _longestMatchLength = newLen; + _longestMatchWasFound = true; + return Backward(out backRes, cur); + } + position++; + UInt32 posPrev = _optimum[cur].PosPrev; + Base.State state; + if (_optimum[cur].Prev1IsChar) + { + posPrev--; + if (_optimum[cur].Prev2) + { + state = _optimum[_optimum[cur].PosPrev2].State; + if (_optimum[cur].BackPrev2 < Base.kNumRepDistances) + state.UpdateRep(); + else + state.UpdateMatch(); + } + else + state = _optimum[posPrev].State; + state.UpdateChar(); + } + else + state = _optimum[posPrev].State; + if (posPrev == cur - 1) + { + if (_optimum[cur].IsShortRep()) + state.UpdateShortRep(); + else + state.UpdateChar(); + } + else + { + UInt32 pos; + if (_optimum[cur].Prev1IsChar && _optimum[cur].Prev2) + { + posPrev = _optimum[cur].PosPrev2; + pos = _optimum[cur].BackPrev2; + state.UpdateRep(); + } + else + { + pos = _optimum[cur].BackPrev; + if (pos < Base.kNumRepDistances) + state.UpdateRep(); + else + state.UpdateMatch(); + } + Optimal opt = _optimum[posPrev]; + if (pos < Base.kNumRepDistances) + { + if (pos == 0) + { + reps[0] = opt.Backs0; + reps[1] = opt.Backs1; + reps[2] = opt.Backs2; + reps[3] = opt.Backs3; + } + else if (pos == 1) + { + reps[0] = opt.Backs1; + reps[1] = opt.Backs0; + reps[2] = opt.Backs2; + reps[3] = opt.Backs3; + } + else if (pos == 2) + { + reps[0] = opt.Backs2; + reps[1] = opt.Backs0; + reps[2] = opt.Backs1; + reps[3] = opt.Backs3; + } + else + { + reps[0] = opt.Backs3; + reps[1] = opt.Backs0; + reps[2] = opt.Backs1; + reps[3] = opt.Backs2; + } + } + else + { + reps[0] = (pos - Base.kNumRepDistances); + reps[1] = opt.Backs0; + reps[2] = opt.Backs1; + reps[3] = opt.Backs2; + } + } + _optimum[cur].State = state; + _optimum[cur].Backs0 = reps[0]; + _optimum[cur].Backs1 = reps[1]; + _optimum[cur].Backs2 = reps[2]; + _optimum[cur].Backs3 = reps[3]; + UInt32 curPrice = _optimum[cur].Price; + + currentByte = _matchFinder.GetIndexByte(0 - 1); + matchByte = _matchFinder.GetIndexByte((Int32)(0 - reps[0] - 1 - 1)); + + posState = (position & _posStateMask); + + UInt32 curAnd1Price = curPrice + + _isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() + + _literalEncoder.GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)). + GetPrice(!state.IsCharState(), matchByte, currentByte); + + Optimal nextOptimum = _optimum[cur + 1]; + + bool nextIsChar = false; + if (curAnd1Price < nextOptimum.Price) + { + nextOptimum.Price = curAnd1Price; + nextOptimum.PosPrev = cur; + nextOptimum.MakeAsChar(); + nextIsChar = true; + } + + matchPrice = curPrice + _isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1(); + repMatchPrice = matchPrice + _isRep[state.Index].GetPrice1(); + + if (matchByte == currentByte && + !(nextOptimum.PosPrev < cur && nextOptimum.BackPrev == 0)) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(state, posState); + if (shortRepPrice <= nextOptimum.Price) + { + nextOptimum.Price = shortRepPrice; + nextOptimum.PosPrev = cur; + nextOptimum.MakeAsShortRep(); + nextIsChar = true; + } + } + + UInt32 numAvailableBytesFull = _matchFinder.GetNumAvailableBytes() + 1; + numAvailableBytesFull = Math.Min(kNumOpts - 1 - cur, numAvailableBytesFull); + numAvailableBytes = numAvailableBytesFull; + + if (numAvailableBytes < 2) + continue; + if (numAvailableBytes > _numFastBytes) + numAvailableBytes = _numFastBytes; + if (!nextIsChar && matchByte != currentByte) + { + // try Literal + rep0 + UInt32 t = Math.Min(numAvailableBytesFull - 1, _numFastBytes); + UInt32 lenTest2 = _matchFinder.GetMatchLen(0, reps[0], t); + if (lenTest2 >= 2) + { + Base.State state2 = state; + state2.UpdateChar(); + UInt32 posStateNext = (position + 1) & _posStateMask; + UInt32 nextRepMatchPrice = curAnd1Price + + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1() + + _isRep[state2.Index].GetPrice1(); + { + UInt32 offset = cur + 1 + lenTest2; + while (lenEnd < offset) + _optimum[++lenEnd].Price = kIfinityPrice; + UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice( + 0, lenTest2, state2, posStateNext); + Optimal optimum = _optimum[offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = false; + } + } + } + } + + UInt32 startLen = 2; // speed optimization + + for (UInt32 repIndex = 0; repIndex < Base.kNumRepDistances; repIndex++) + { + UInt32 lenTest = _matchFinder.GetMatchLen(0 - 1, reps[repIndex], numAvailableBytes); + if (lenTest < 2) + continue; + UInt32 lenTestTemp = lenTest; + do + { + while (lenEnd < cur + lenTest) + _optimum[++lenEnd].Price = kIfinityPrice; + UInt32 curAndLenPrice = repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState); + Optimal optimum = _optimum[cur + lenTest]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur; + optimum.BackPrev = repIndex; + optimum.Prev1IsChar = false; + } + } + while(--lenTest >= 2); + lenTest = lenTestTemp; + + if (repIndex == 0) + startLen = lenTest + 1; + + // if (_maxMode) + if (lenTest < numAvailableBytesFull) + { + UInt32 t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes); + UInt32 lenTest2 = _matchFinder.GetMatchLen((Int32)lenTest, reps[repIndex], t); + if (lenTest2 >= 2) + { + Base.State state2 = state; + state2.UpdateRep(); + UInt32 posStateNext = (position + lenTest) & _posStateMask; + UInt32 curAndLenCharPrice = + repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState) + + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice0() + + _literalEncoder.GetSubCoder(position + lenTest, + _matchFinder.GetIndexByte((Int32)lenTest - 1 - 1)).GetPrice(true, + _matchFinder.GetIndexByte((Int32)((Int32)lenTest - 1 - (Int32)(reps[repIndex] + 1))), + _matchFinder.GetIndexByte((Int32)lenTest - 1)); + state2.UpdateChar(); + posStateNext = (position + lenTest + 1) & _posStateMask; + UInt32 nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1(); + UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1(); + + // for(; lenTest2 >= 2; lenTest2--) + { + UInt32 offset = lenTest + 1 + lenTest2; + while(lenEnd < cur + offset) + _optimum[++lenEnd].Price = kIfinityPrice; + UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); + Optimal optimum = _optimum[cur + offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + lenTest + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = true; + optimum.PosPrev2 = cur; + optimum.BackPrev2 = repIndex; + } + } + } + } + } + + if (newLen > numAvailableBytes) + { + newLen = numAvailableBytes; + for (numDistancePairs = 0; newLen > _matchDistances[numDistancePairs]; numDistancePairs += 2) ; + _matchDistances[numDistancePairs] = newLen; + numDistancePairs += 2; + } + if (newLen >= startLen) + { + normalMatchPrice = matchPrice + _isRep[state.Index].GetPrice0(); + while (lenEnd < cur + newLen) + _optimum[++lenEnd].Price = kIfinityPrice; + + UInt32 offs = 0; + while (startLen > _matchDistances[offs]) + offs += 2; + + for (UInt32 lenTest = startLen; ; lenTest++) + { + UInt32 curBack = _matchDistances[offs + 1]; + UInt32 curAndLenPrice = normalMatchPrice + GetPosLenPrice(curBack, lenTest, posState); + Optimal optimum = _optimum[cur + lenTest]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur; + optimum.BackPrev = curBack + Base.kNumRepDistances; + optimum.Prev1IsChar = false; + } + + if (lenTest == _matchDistances[offs]) + { + if (lenTest < numAvailableBytesFull) + { + UInt32 t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes); + UInt32 lenTest2 = _matchFinder.GetMatchLen((Int32)lenTest, curBack, t); + if (lenTest2 >= 2) + { + Base.State state2 = state; + state2.UpdateMatch(); + UInt32 posStateNext = (position + lenTest) & _posStateMask; + UInt32 curAndLenCharPrice = curAndLenPrice + + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice0() + + _literalEncoder.GetSubCoder(position + lenTest, + _matchFinder.GetIndexByte((Int32)lenTest - 1 - 1)). + GetPrice(true, + _matchFinder.GetIndexByte((Int32)lenTest - (Int32)(curBack + 1) - 1), + _matchFinder.GetIndexByte((Int32)lenTest - 1)); + state2.UpdateChar(); + posStateNext = (position + lenTest + 1) & _posStateMask; + UInt32 nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1(); + UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1(); + + UInt32 offset = lenTest + 1 + lenTest2; + while (lenEnd < cur + offset) + _optimum[++lenEnd].Price = kIfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); + optimum = _optimum[cur + offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + lenTest + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = true; + optimum.PosPrev2 = cur; + optimum.BackPrev2 = curBack + Base.kNumRepDistances; + } + } + } + offs += 2; + if (offs == numDistancePairs) + break; + } + } + } + } + } + + bool ChangePair(UInt32 smallDist, UInt32 bigDist) + { + const int kDif = 7; + return (smallDist < ((UInt32)(1) << (32 - kDif)) && bigDist >= (smallDist << kDif)); + } + + void WriteEndMarker(UInt32 posState) + { + if (!_writeEndMark) + return; + + _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 1); + _isRep[_state.Index].Encode(_rangeEncoder, 0); + _state.UpdateMatch(); + UInt32 len = Base.kMatchMinLen; + _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); + UInt32 posSlot = (1 << Base.kNumPosSlotBits) - 1; + UInt32 lenToPosState = Base.GetLenToPosState(len); + _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); + int footerBits = 30; + UInt32 posReduced = (((UInt32)1) << footerBits) - 1; + _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits); + _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask); + } + + void Flush(UInt32 nowPos) + { + ReleaseMFStream(); + WriteEndMarker(nowPos & _posStateMask); + _rangeEncoder.FlushData(); + _rangeEncoder.FlushStream(); + } + + public void CodeOneBlock(out Int64 inSize, out Int64 outSize, out bool finished) + { + inSize = 0; + outSize = 0; + finished = true; + + if (_inStream != null) + { + _matchFinder.SetStream(_inStream); + _matchFinder.Init(); + _needReleaseMFStream = true; + _inStream = null; + if (_trainSize > 0) + _matchFinder.Skip(_trainSize); + } + + if (_finished) + return; + _finished = true; + + + Int64 progressPosValuePrev = nowPos64; + if (nowPos64 == 0) + { + if (_matchFinder.GetNumAvailableBytes() == 0) + { + Flush((UInt32)nowPos64); + return; + } + UInt32 len, numDistancePairs; // it's not used + ReadMatchDistances(out len, out numDistancePairs); + UInt32 posState = (UInt32)(nowPos64) & _posStateMask; + _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 0); + _state.UpdateChar(); + Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset)); + _literalEncoder.GetSubCoder((UInt32)(nowPos64), _previousByte).Encode(_rangeEncoder, curByte); + _previousByte = curByte; + _additionalOffset--; + nowPos64++; + } + if (_matchFinder.GetNumAvailableBytes() == 0) + { + Flush((UInt32)nowPos64); + return; + } + while (true) + { + UInt32 pos; + UInt32 len = GetOptimum((UInt32)nowPos64, out pos); + + UInt32 posState = ((UInt32)nowPos64) & _posStateMask; + UInt32 complexState = (_state.Index << Base.kNumPosStatesBitsMax) + posState; + if (len == 1 && pos == 0xFFFFFFFF) + { + _isMatch[complexState].Encode(_rangeEncoder, 0); + Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset)); + LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((UInt32)nowPos64, _previousByte); + if (!_state.IsCharState()) + { + Byte matchByte = _matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - _additionalOffset)); + subCoder.EncodeMatched(_rangeEncoder, matchByte, curByte); + } + else + subCoder.Encode(_rangeEncoder, curByte); + _previousByte = curByte; + _state.UpdateChar(); + } + else + { + _isMatch[complexState].Encode(_rangeEncoder, 1); + if (pos < Base.kNumRepDistances) + { + _isRep[_state.Index].Encode(_rangeEncoder, 1); + if (pos == 0) + { + _isRepG0[_state.Index].Encode(_rangeEncoder, 0); + if (len == 1) + _isRep0Long[complexState].Encode(_rangeEncoder, 0); + else + _isRep0Long[complexState].Encode(_rangeEncoder, 1); + } + else + { + _isRepG0[_state.Index].Encode(_rangeEncoder, 1); + if (pos == 1) + _isRepG1[_state.Index].Encode(_rangeEncoder, 0); + else + { + _isRepG1[_state.Index].Encode(_rangeEncoder, 1); + _isRepG2[_state.Index].Encode(_rangeEncoder, pos - 2); + } + } + if (len == 1) + _state.UpdateShortRep(); + else + { + _repMatchLenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); + _state.UpdateRep(); + } + UInt32 distance = _repDistances[pos]; + if (pos != 0) + { + for (UInt32 i = pos; i >= 1; i--) + _repDistances[i] = _repDistances[i - 1]; + _repDistances[0] = distance; + } + } + else + { + _isRep[_state.Index].Encode(_rangeEncoder, 0); + _state.UpdateMatch(); + _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); + pos -= Base.kNumRepDistances; + UInt32 posSlot = GetPosSlot(pos); + UInt32 lenToPosState = Base.GetLenToPosState(len); + _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); + + if (posSlot >= Base.kStartPosModelIndex) + { + int footerBits = (int)((posSlot >> 1) - 1); + UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits); + UInt32 posReduced = pos - baseVal; + + if (posSlot < Base.kEndPosModelIndex) + RangeCoder.BitTreeEncoder.ReverseEncode(_posEncoders, + baseVal - posSlot - 1, _rangeEncoder, footerBits, posReduced); + else + { + _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits); + _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask); + _alignPriceCount++; + } + } + UInt32 distance = pos; + for (UInt32 i = Base.kNumRepDistances - 1; i >= 1; i--) + _repDistances[i] = _repDistances[i - 1]; + _repDistances[0] = distance; + _matchPriceCount++; + } + _previousByte = _matchFinder.GetIndexByte((Int32)(len - 1 - _additionalOffset)); + } + _additionalOffset -= len; + nowPos64 += len; + if (_additionalOffset == 0) + { + // if (!_fastMode) + if (_matchPriceCount >= (1 << 7)) + FillDistancesPrices(); + if (_alignPriceCount >= Base.kAlignTableSize) + FillAlignPrices(); + inSize = nowPos64; + outSize = _rangeEncoder.GetProcessedSizeAdd(); + if (_matchFinder.GetNumAvailableBytes() == 0) + { + Flush((UInt32)nowPos64); + return; + } + + if (nowPos64 - progressPosValuePrev >= (1 << 12)) + { + _finished = false; + finished = false; + return; + } + } + } + } + + void ReleaseMFStream() + { + if (_matchFinder != null && _needReleaseMFStream) + { + _matchFinder.ReleaseStream(); + _needReleaseMFStream = false; + } + } + + void SetOutStream(System.IO.Stream outStream) { _rangeEncoder.SetStream(outStream); } + void ReleaseOutStream() { _rangeEncoder.ReleaseStream(); } + + void ReleaseStreams() + { + ReleaseMFStream(); + ReleaseOutStream(); + } + + void SetStreams(System.IO.Stream inStream, System.IO.Stream outStream, + Int64 inSize, Int64 outSize) + { + _inStream = inStream; + _finished = false; + Create(); + SetOutStream(outStream); + Init(); + + // if (!_fastMode) + { + FillDistancesPrices(); + FillAlignPrices(); + } + + _lenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen); + _lenEncoder.UpdateTables((UInt32)1 << _posStateBits); + _repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen); + _repMatchLenEncoder.UpdateTables((UInt32)1 << _posStateBits); + + nowPos64 = 0; + } + + + public void Code(System.IO.Stream inStream, System.IO.Stream outStream, + Int64 inSize, Int64 outSize, ICodeProgress progress) + { + _needReleaseMFStream = false; + try + { + SetStreams(inStream, outStream, inSize, outSize); + while (true) + { + Int64 processedInSize; + Int64 processedOutSize; + bool finished; + CodeOneBlock(out processedInSize, out processedOutSize, out finished); + if (finished) + return; + if (progress != null) + { + progress.SetProgress(processedInSize, processedOutSize); + } + } + } + finally + { + ReleaseStreams(); + } + } + + const int kPropSize = 5; + Byte[] properties = new Byte[kPropSize]; + + public void WriteCoderProperties(System.IO.Stream outStream) + { + properties[0] = (Byte)((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits); + for (int i = 0; i < 4; i++) + properties[1 + i] = (Byte)((_dictionarySize >> (8 * i)) & 0xFF); + outStream.Write(properties, 0, kPropSize); + } + + UInt32[] tempPrices = new UInt32[Base.kNumFullDistances]; + UInt32 _matchPriceCount; + + void FillDistancesPrices() + { + for (UInt32 i = Base.kStartPosModelIndex; i < Base.kNumFullDistances; i++) + { + UInt32 posSlot = GetPosSlot(i); + int footerBits = (int)((posSlot >> 1) - 1); + UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits); + tempPrices[i] = BitTreeEncoder.ReverseGetPrice(_posEncoders, + baseVal - posSlot - 1, footerBits, i - baseVal); + } + + for (UInt32 lenToPosState = 0; lenToPosState < Base.kNumLenToPosStates; lenToPosState++) + { + UInt32 posSlot; + RangeCoder.BitTreeEncoder encoder = _posSlotEncoder[lenToPosState]; + + UInt32 st = (lenToPosState << Base.kNumPosSlotBits); + for (posSlot = 0; posSlot < _distTableSize; posSlot++) + _posSlotPrices[st + posSlot] = encoder.GetPrice(posSlot); + for (posSlot = Base.kEndPosModelIndex; posSlot < _distTableSize; posSlot++) + _posSlotPrices[st + posSlot] += ((((posSlot >> 1) - 1) - Base.kNumAlignBits) << RangeCoder.BitEncoder.kNumBitPriceShiftBits); + + UInt32 st2 = lenToPosState * Base.kNumFullDistances; + UInt32 i; + for (i = 0; i < Base.kStartPosModelIndex; i++) + _distancesPrices[st2 + i] = _posSlotPrices[st + i]; + for (; i < Base.kNumFullDistances; i++) + _distancesPrices[st2 + i] = _posSlotPrices[st + GetPosSlot(i)] + tempPrices[i]; + } + _matchPriceCount = 0; + } + + void FillAlignPrices() + { + for (UInt32 i = 0; i < Base.kAlignTableSize; i++) + _alignPrices[i] = _posAlignEncoder.ReverseGetPrice(i); + _alignPriceCount = 0; + } + + + static string[] kMatchFinderIDs = + { + "BT2", + "BT4", + }; + + static int FindMatchFinder(string s) + { + for (int m = 0; m < kMatchFinderIDs.Length; m++) + if (s == kMatchFinderIDs[m]) + return m; + return -1; + } + + public void SetCoderProperties(CoderPropID[] propIDs, object[] properties) + { + for (UInt32 i = 0; i < properties.Length; i++) + { + object prop = properties[i]; + switch (propIDs[i]) + { + case CoderPropID.NumFastBytes: + { + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 numFastBytes = (Int32)prop; + if (numFastBytes < 5 || numFastBytes > Base.kMatchMaxLen) + throw new InvalidParamException(); + _numFastBytes = (UInt32)numFastBytes; + break; + } + case CoderPropID.Algorithm: + { + /* + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 maximize = (Int32)prop; + _fastMode = (maximize == 0); + _maxMode = (maximize >= 2); + */ + break; + } + case CoderPropID.MatchFinder: + { + if (!(prop is String)) + throw new InvalidParamException(); + EMatchFinderType matchFinderIndexPrev = _matchFinderType; + int m = FindMatchFinder(((string)prop).ToUpper()); + if (m < 0) + throw new InvalidParamException(); + _matchFinderType = (EMatchFinderType)m; + if (_matchFinder != null && matchFinderIndexPrev != _matchFinderType) + { + _dictionarySizePrev = 0xFFFFFFFF; + _matchFinder = null; + } + break; + } + case CoderPropID.DictionarySize: + { + const int kDicLogSizeMaxCompress = 30; + if (!(prop is Int32)) + throw new InvalidParamException(); ; + Int32 dictionarySize = (Int32)prop; + if (dictionarySize < (UInt32)(1 << Base.kDicLogSizeMin) || + dictionarySize > (UInt32)(1 << kDicLogSizeMaxCompress)) + throw new InvalidParamException(); + _dictionarySize = (UInt32)dictionarySize; + int dicLogSize; + for (dicLogSize = 0; dicLogSize < (UInt32)kDicLogSizeMaxCompress; dicLogSize++) + if (dictionarySize <= ((UInt32)(1) << dicLogSize)) + break; + _distTableSize = (UInt32)dicLogSize * 2; + break; + } + case CoderPropID.PosStateBits: + { + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 v = (Int32)prop; + if (v < 0 || v > (UInt32)Base.kNumPosStatesBitsEncodingMax) + throw new InvalidParamException(); + _posStateBits = (int)v; + _posStateMask = (((UInt32)1) << (int)_posStateBits) - 1; + break; + } + case CoderPropID.LitPosBits: + { + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 v = (Int32)prop; + if (v < 0 || v > (UInt32)Base.kNumLitPosStatesBitsEncodingMax) + throw new InvalidParamException(); + _numLiteralPosStateBits = (int)v; + break; + } + case CoderPropID.LitContextBits: + { + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 v = (Int32)prop; + if (v < 0 || v > (UInt32)Base.kNumLitContextBitsMax) + throw new InvalidParamException(); ; + _numLiteralContextBits = (int)v; + break; + } + case CoderPropID.EndMarker: + { + if (!(prop is Boolean)) + throw new InvalidParamException(); + SetWriteEndMarkerMode((Boolean)prop); + break; + } + default: + throw new InvalidParamException(); + } + } + } + + uint _trainSize = 0; + public void SetTrainSize(uint trainSize) + { + _trainSize = trainSize; + } + + } +} diff --git a/AzurLaneLive2DExtract/7zip/Compress/RangeCoder/RangeCoder.cs b/AzurLaneLive2DExtract/7zip/Compress/RangeCoder/RangeCoder.cs new file mode 100644 index 0000000..949c6bb --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Compress/RangeCoder/RangeCoder.cs @@ -0,0 +1,234 @@ +using System; + +namespace SevenZip.Compression.RangeCoder +{ + class Encoder + { + public const uint kTopValue = (1 << 24); + + System.IO.Stream Stream; + + public UInt64 Low; + public uint Range; + uint _cacheSize; + byte _cache; + + long StartPosition; + + public void SetStream(System.IO.Stream stream) + { + Stream = stream; + } + + public void ReleaseStream() + { + Stream = null; + } + + public void Init() + { + StartPosition = Stream.Position; + + Low = 0; + Range = 0xFFFFFFFF; + _cacheSize = 1; + _cache = 0; + } + + public void FlushData() + { + for (int i = 0; i < 5; i++) + ShiftLow(); + } + + public void FlushStream() + { + Stream.Flush(); + } + + public void CloseStream() + { + Stream.Close(); + } + + public void Encode(uint start, uint size, uint total) + { + Low += start * (Range /= total); + Range *= size; + while (Range < kTopValue) + { + Range <<= 8; + ShiftLow(); + } + } + + public void ShiftLow() + { + if ((uint)Low < (uint)0xFF000000 || (uint)(Low >> 32) == 1) + { + byte temp = _cache; + do + { + Stream.WriteByte((byte)(temp + (Low >> 32))); + temp = 0xFF; + } + while (--_cacheSize != 0); + _cache = (byte)(((uint)Low) >> 24); + } + _cacheSize++; + Low = ((uint)Low) << 8; + } + + public void EncodeDirectBits(uint v, int numTotalBits) + { + for (int i = numTotalBits - 1; i >= 0; i--) + { + Range >>= 1; + if (((v >> i) & 1) == 1) + Low += Range; + if (Range < kTopValue) + { + Range <<= 8; + ShiftLow(); + } + } + } + + public void EncodeBit(uint size0, int numTotalBits, uint symbol) + { + uint newBound = (Range >> numTotalBits) * size0; + if (symbol == 0) + Range = newBound; + else + { + Low += newBound; + Range -= newBound; + } + while (Range < kTopValue) + { + Range <<= 8; + ShiftLow(); + } + } + + public long GetProcessedSizeAdd() + { + return _cacheSize + + Stream.Position - StartPosition + 4; + // (long)Stream.GetProcessedSize(); + } + } + + class Decoder + { + public const uint kTopValue = (1 << 24); + public uint Range; + public uint Code; + // public Buffer.InBuffer Stream = new Buffer.InBuffer(1 << 16); + public System.IO.Stream Stream; + + public void Init(System.IO.Stream stream) + { + // Stream.Init(stream); + Stream = stream; + + Code = 0; + Range = 0xFFFFFFFF; + for (int i = 0; i < 5; i++) + Code = (Code << 8) | (byte)Stream.ReadByte(); + } + + public void ReleaseStream() + { + // Stream.ReleaseStream(); + Stream = null; + } + + public void CloseStream() + { + Stream.Close(); + } + + public void Normalize() + { + while (Range < kTopValue) + { + Code = (Code << 8) | (byte)Stream.ReadByte(); + Range <<= 8; + } + } + + public void Normalize2() + { + if (Range < kTopValue) + { + Code = (Code << 8) | (byte)Stream.ReadByte(); + Range <<= 8; + } + } + + public uint GetThreshold(uint total) + { + return Code / (Range /= total); + } + + public void Decode(uint start, uint size, uint total) + { + Code -= start * Range; + Range *= size; + Normalize(); + } + + public uint DecodeDirectBits(int numTotalBits) + { + uint range = Range; + uint code = Code; + uint result = 0; + for (int i = numTotalBits; i > 0; i--) + { + range >>= 1; + /* + result <<= 1; + if (code >= range) + { + code -= range; + result |= 1; + } + */ + uint t = (code - range) >> 31; + code -= range & (t - 1); + result = (result << 1) | (1 - t); + + if (range < kTopValue) + { + code = (code << 8) | (byte)Stream.ReadByte(); + range <<= 8; + } + } + Range = range; + Code = code; + return result; + } + + public uint DecodeBit(uint size0, int numTotalBits) + { + uint newBound = (Range >> numTotalBits) * size0; + uint symbol; + if (Code < newBound) + { + symbol = 0; + Range = newBound; + } + else + { + symbol = 1; + Code -= newBound; + Range -= newBound; + } + Normalize(); + return symbol; + } + + // ulong GetProcessedSize() {return Stream.GetProcessedSize(); } + } +} diff --git a/AzurLaneLive2DExtract/7zip/Compress/RangeCoder/RangeCoderBit.cs b/AzurLaneLive2DExtract/7zip/Compress/RangeCoder/RangeCoderBit.cs new file mode 100644 index 0000000..4f0346d --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Compress/RangeCoder/RangeCoderBit.cs @@ -0,0 +1,117 @@ +using System; + +namespace SevenZip.Compression.RangeCoder +{ + struct BitEncoder + { + public const int kNumBitModelTotalBits = 11; + public const uint kBitModelTotal = (1 << kNumBitModelTotalBits); + const int kNumMoveBits = 5; + const int kNumMoveReducingBits = 2; + public const int kNumBitPriceShiftBits = 6; + + uint Prob; + + public void Init() { Prob = kBitModelTotal >> 1; } + + public void UpdateModel(uint symbol) + { + if (symbol == 0) + Prob += (kBitModelTotal - Prob) >> kNumMoveBits; + else + Prob -= (Prob) >> kNumMoveBits; + } + + public void Encode(Encoder encoder, uint symbol) + { + // encoder.EncodeBit(Prob, kNumBitModelTotalBits, symbol); + // UpdateModel(symbol); + uint newBound = (encoder.Range >> kNumBitModelTotalBits) * Prob; + if (symbol == 0) + { + encoder.Range = newBound; + Prob += (kBitModelTotal - Prob) >> kNumMoveBits; + } + else + { + encoder.Low += newBound; + encoder.Range -= newBound; + Prob -= (Prob) >> kNumMoveBits; + } + if (encoder.Range < Encoder.kTopValue) + { + encoder.Range <<= 8; + encoder.ShiftLow(); + } + } + + private static UInt32[] ProbPrices = new UInt32[kBitModelTotal >> kNumMoveReducingBits]; + + static BitEncoder() + { + const int kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits); + for (int i = kNumBits - 1; i >= 0; i--) + { + UInt32 start = (UInt32)1 << (kNumBits - i - 1); + UInt32 end = (UInt32)1 << (kNumBits - i); + for (UInt32 j = start; j < end; j++) + ProbPrices[j] = ((UInt32)i << kNumBitPriceShiftBits) + + (((end - j) << kNumBitPriceShiftBits) >> (kNumBits - i - 1)); + } + } + + public uint GetPrice(uint symbol) + { + return ProbPrices[(((Prob - symbol) ^ ((-(int)symbol))) & (kBitModelTotal - 1)) >> kNumMoveReducingBits]; + } + public uint GetPrice0() { return ProbPrices[Prob >> kNumMoveReducingBits]; } + public uint GetPrice1() { return ProbPrices[(kBitModelTotal - Prob) >> kNumMoveReducingBits]; } + } + + struct BitDecoder + { + public const int kNumBitModelTotalBits = 11; + public const uint kBitModelTotal = (1 << kNumBitModelTotalBits); + const int kNumMoveBits = 5; + + uint Prob; + + public void UpdateModel(int numMoveBits, uint symbol) + { + if (symbol == 0) + Prob += (kBitModelTotal - Prob) >> numMoveBits; + else + Prob -= (Prob) >> numMoveBits; + } + + public void Init() { Prob = kBitModelTotal >> 1; } + + public uint Decode(RangeCoder.Decoder rangeDecoder) + { + uint newBound = (uint)(rangeDecoder.Range >> kNumBitModelTotalBits) * (uint)Prob; + if (rangeDecoder.Code < newBound) + { + rangeDecoder.Range = newBound; + Prob += (kBitModelTotal - Prob) >> kNumMoveBits; + if (rangeDecoder.Range < Decoder.kTopValue) + { + rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte(); + rangeDecoder.Range <<= 8; + } + return 0; + } + else + { + rangeDecoder.Range -= newBound; + rangeDecoder.Code -= newBound; + Prob -= (Prob) >> kNumMoveBits; + if (rangeDecoder.Range < Decoder.kTopValue) + { + rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte(); + rangeDecoder.Range <<= 8; + } + return 1; + } + } + } +} diff --git a/AzurLaneLive2DExtract/7zip/Compress/RangeCoder/RangeCoderBitTree.cs b/AzurLaneLive2DExtract/7zip/Compress/RangeCoder/RangeCoderBitTree.cs new file mode 100644 index 0000000..4b4506f --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/Compress/RangeCoder/RangeCoderBitTree.cs @@ -0,0 +1,157 @@ +using System; + +namespace SevenZip.Compression.RangeCoder +{ + struct BitTreeEncoder + { + BitEncoder[] Models; + int NumBitLevels; + + public BitTreeEncoder(int numBitLevels) + { + NumBitLevels = numBitLevels; + Models = new BitEncoder[1 << numBitLevels]; + } + + public void Init() + { + for (uint i = 1; i < (1 << NumBitLevels); i++) + Models[i].Init(); + } + + public void Encode(Encoder rangeEncoder, UInt32 symbol) + { + UInt32 m = 1; + for (int bitIndex = NumBitLevels; bitIndex > 0; ) + { + bitIndex--; + UInt32 bit = (symbol >> bitIndex) & 1; + Models[m].Encode(rangeEncoder, bit); + m = (m << 1) | bit; + } + } + + public void ReverseEncode(Encoder rangeEncoder, UInt32 symbol) + { + UInt32 m = 1; + for (UInt32 i = 0; i < NumBitLevels; i++) + { + UInt32 bit = symbol & 1; + Models[m].Encode(rangeEncoder, bit); + m = (m << 1) | bit; + symbol >>= 1; + } + } + + public UInt32 GetPrice(UInt32 symbol) + { + UInt32 price = 0; + UInt32 m = 1; + for (int bitIndex = NumBitLevels; bitIndex > 0; ) + { + bitIndex--; + UInt32 bit = (symbol >> bitIndex) & 1; + price += Models[m].GetPrice(bit); + m = (m << 1) + bit; + } + return price; + } + + public UInt32 ReverseGetPrice(UInt32 symbol) + { + UInt32 price = 0; + UInt32 m = 1; + for (int i = NumBitLevels; i > 0; i--) + { + UInt32 bit = symbol & 1; + symbol >>= 1; + price += Models[m].GetPrice(bit); + m = (m << 1) | bit; + } + return price; + } + + public static UInt32 ReverseGetPrice(BitEncoder[] Models, UInt32 startIndex, + int NumBitLevels, UInt32 symbol) + { + UInt32 price = 0; + UInt32 m = 1; + for (int i = NumBitLevels; i > 0; i--) + { + UInt32 bit = symbol & 1; + symbol >>= 1; + price += Models[startIndex + m].GetPrice(bit); + m = (m << 1) | bit; + } + return price; + } + + public static void ReverseEncode(BitEncoder[] Models, UInt32 startIndex, + Encoder rangeEncoder, int NumBitLevels, UInt32 symbol) + { + UInt32 m = 1; + for (int i = 0; i < NumBitLevels; i++) + { + UInt32 bit = symbol & 1; + Models[startIndex + m].Encode(rangeEncoder, bit); + m = (m << 1) | bit; + symbol >>= 1; + } + } + } + + struct BitTreeDecoder + { + BitDecoder[] Models; + int NumBitLevels; + + public BitTreeDecoder(int numBitLevels) + { + NumBitLevels = numBitLevels; + Models = new BitDecoder[1 << numBitLevels]; + } + + public void Init() + { + for (uint i = 1; i < (1 << NumBitLevels); i++) + Models[i].Init(); + } + + public uint Decode(RangeCoder.Decoder rangeDecoder) + { + uint m = 1; + for (int bitIndex = NumBitLevels; bitIndex > 0; bitIndex--) + m = (m << 1) + Models[m].Decode(rangeDecoder); + return m - ((uint)1 << NumBitLevels); + } + + public uint ReverseDecode(RangeCoder.Decoder rangeDecoder) + { + uint m = 1; + uint symbol = 0; + for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + { + uint bit = Models[m].Decode(rangeDecoder); + m <<= 1; + m += bit; + symbol |= (bit << bitIndex); + } + return symbol; + } + + public static uint ReverseDecode(BitDecoder[] Models, UInt32 startIndex, + RangeCoder.Decoder rangeDecoder, int NumBitLevels) + { + uint m = 1; + uint symbol = 0; + for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + { + uint bit = Models[startIndex + m].Decode(rangeDecoder); + m <<= 1; + m += bit; + symbol |= (bit << bitIndex); + } + return symbol; + } + } +} diff --git a/AzurLaneLive2DExtract/7zip/ICoder.cs b/AzurLaneLive2DExtract/7zip/ICoder.cs new file mode 100644 index 0000000..c8b95c8 --- /dev/null +++ b/AzurLaneLive2DExtract/7zip/ICoder.cs @@ -0,0 +1,157 @@ +// ICoder.h + +using System; + +namespace SevenZip +{ + /// + /// The exception that is thrown when an error in input stream occurs during decoding. + /// + class DataErrorException : ApplicationException + { + public DataErrorException(): base("Data Error") { } + } + + /// + /// The exception that is thrown when the value of an argument is outside the allowable range. + /// + class InvalidParamException : ApplicationException + { + public InvalidParamException(): base("Invalid Parameter") { } + } + + public interface ICodeProgress + { + /// + /// Callback progress. + /// + /// + /// input size. -1 if unknown. + /// + /// + /// output size. -1 if unknown. + /// + void SetProgress(Int64 inSize, Int64 outSize); + }; + + public interface ICoder + { + /// + /// Codes streams. + /// + /// + /// input Stream. + /// + /// + /// output Stream. + /// + /// + /// input Size. -1 if unknown. + /// + /// + /// output Size. -1 if unknown. + /// + /// + /// callback progress reference. + /// + /// + /// if input stream is not valid + /// + void Code(System.IO.Stream inStream, System.IO.Stream outStream, + Int64 inSize, Int64 outSize, ICodeProgress progress); + }; + + /* + public interface ICoder2 + { + void Code(ISequentialInStream []inStreams, + const UInt64 []inSizes, + ISequentialOutStream []outStreams, + UInt64 []outSizes, + ICodeProgress progress); + }; + */ + + /// + /// Provides the fields that represent properties idenitifiers for compressing. + /// + public enum CoderPropID + { + /// + /// Specifies default property. + /// + DefaultProp = 0, + /// + /// Specifies size of dictionary. + /// + DictionarySize, + /// + /// Specifies size of memory for PPM*. + /// + UsedMemorySize, + /// + /// Specifies order for PPM methods. + /// + Order, + /// + /// Specifies Block Size. + /// + BlockSize, + /// + /// Specifies number of postion state bits for LZMA (0 <= x <= 4). + /// + PosStateBits, + /// + /// Specifies number of literal context bits for LZMA (0 <= x <= 8). + /// + LitContextBits, + /// + /// Specifies number of literal position bits for LZMA (0 <= x <= 4). + /// + LitPosBits, + /// + /// Specifies number of fast bytes for LZ*. + /// + NumFastBytes, + /// + /// Specifies match finder. LZMA: "BT2", "BT4" or "BT4B". + /// + MatchFinder, + /// + /// Specifies the number of match finder cyckes. + /// + MatchFinderCycles, + /// + /// Specifies number of passes. + /// + NumPasses, + /// + /// Specifies number of algorithm. + /// + Algorithm, + /// + /// Specifies the number of threads. + /// + NumThreads, + /// + /// Specifies mode with end marker. + /// + EndMarker + }; + + + public interface ISetCoderProperties + { + void SetCoderProperties(CoderPropID[] propIDs, object[] properties); + }; + + public interface IWriteCoderProperties + { + void WriteCoderProperties(System.IO.Stream outStream); + } + + public interface ISetDecoderProperties + { + void SetDecoderProperties(byte[] properties); + } +} diff --git a/AzurLaneLive2DExtract/App.config b/AzurLaneLive2DExtract/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/AzurLaneLive2DExtract/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/AzurLaneLive2DExtract/AssetStudioCore/AssetPreloadData.cs b/AzurLaneLive2DExtract/AssetStudioCore/AssetPreloadData.cs new file mode 100644 index 0000000..3eec387 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/AssetPreloadData.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudioCore +{ + public class AssetPreloadData + { + public long m_PathID; + public uint Offset; + public int Size; + public ClassIDReference Type; + public int Type1; + public int Type2; + + public string TypeString; + + public AssetsFile sourceFile; + public string uniqueID; + + public EndianBinaryReader InitReader() + { + var reader = sourceFile.reader; + reader.Position = Offset; + return reader; + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/AssetsFile.cs b/AzurLaneLive2DExtract/AssetStudioCore/AssetsFile.cs new file mode 100644 index 0000000..29375e3 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/AssetsFile.cs @@ -0,0 +1,441 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace AssetStudioCore +{ + public class AssetsFile + { + public EndianBinaryReader reader; + 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 preloadTable = new Dictionary(); + public List sharedAssetsList = new List { new SharedAssets() }; + + + private bool baseDefinitions; + private List classIDs = new List();//use for 5.5.0 + + + #region cmmon string + private static Dictionary baseStrings = new Dictionary + { + {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"}, + {564, "PPtr"}, + {581, "PPtr"}, + {596, "PPtr"}, + {616, "PPtr"}, + {633, "PPtr"}, + {646, "PPtr"}, + {659, "PPtr"}, + {672, "PPtr"}, + {688, "PPtr"}, + {702, "PPtr"}, + {718, "PPtr"}, + {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) + { + this.reader = reader; + filePath = fullName; + fileName = Path.GetFileName(fullName); + upperFileName = fileName.ToUpper(); + try + { + int tableSize = this.reader.ReadInt32(); + int dataEnd = this.reader.ReadInt32(); + fileGen = this.reader.ReadInt32(); + uint dataOffset = this.reader.ReadUInt32(); + sharedAssetsList[0].fileName = fileName; //reference itself because sharedFileIDs start from 1 + + switch (fileGen) + { + case 6: //2.5.0 - 2.6.1 + { + this.reader.Position = (dataEnd - tableSize); + this.reader.Position += 1; + break; + } + case 7: //3.0.0 beta + { + 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 == "") + { + return; + } + + if (platform > 255 || platform < 0) + { + byte[] b32 = BitConverter.GetBytes(platform); + Array.Reverse(b32); + platform = BitConverter.ToInt32(b32, 0); + this.reader.endian = EndianType.LittleEndian; + } + + 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) + { + int classID = this.reader.ReadInt32(); + string baseType = this.reader.ReadStringToNull(); + string baseName = this.reader.ReadStringToNull(); + this.reader.Position += 20; + int memberCount = this.reader.ReadInt32(); + + for (int m = 0; m < memberCount; m++) + { + readBase(); + } + } + else + { + readBase5(); + } + } + + if (fileGen >= 7 && fileGen < 14) + { + this.reader.Position += 4; //azero + } + + int assetCount = this.reader.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) + { + this.reader.AlignStream(4); + } + + AssetPreloadData asset = new AssetPreloadData(); + asset.m_PathID = fileGen < 14 ? this.reader.ReadInt32() : this.reader.ReadInt64(); + asset.Offset = this.reader.ReadUInt32(); + asset.Offset += dataOffset; + asset.Size = this.reader.ReadInt32(); + if (fileGen > 15) + { + int index = this.reader.ReadInt32(); + asset.Type1 = classIDs[index][0]; + asset.Type2 = classIDs[index][1]; + } + else + { + asset.Type1 = this.reader.ReadInt32(); + asset.Type2 = this.reader.ReadUInt16(); + this.reader.Position += 2; + } + if (fileGen == 15) + { + byte unknownByte = this.reader.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.sourceFile = this; + + preloadTable.Add(asset.m_PathID, asset); + } + #endregion + + buildType = Regex.Replace(m_Version, @"\d", "").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries); + var firstVersion = int.Parse(m_Version.Split('.')[0]); + version = Regex.Matches(m_Version, @"\d").Cast().Select(m => int.Parse(m.Value)).ToArray(); + if (firstVersion > 5)//2017 and up + { + var nversion = new int[version.Length - 3]; + nversion[0] = firstVersion; + 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 = 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; + } + catch + { + } + } + + private void readBase() + { + string varType = reader.ReadStringToNull(); + string varName = reader.ReadStringToNull(); + int size = reader.ReadInt32(); + int index = reader.ReadInt32(); + int isArray = reader.ReadInt32(); + int num0 = reader.ReadInt32(); + int flag = reader.ReadInt32(); + int childrenCount = reader.ReadInt32(); + for (int i = 0; i < childrenCount; i++) + { + readBase(); + } + } + + private void readBase5() + { + int classID = reader.ReadInt32(); + if (fileGen > 15)//5.5.0 and up + { + reader.ReadByte(); + int type1; + if ((type1 = reader.ReadInt16()) >= 0) + { + type1 = -1 - type1; + } + else + { + type1 = classID; + } + classIDs.Add(new[] { type1, classID }); + if (classID == 114) + { + reader.Position += 16; + } + classID = type1; + } + else if (classID < 0) + { + reader.Position += 16; + } + reader.Position += 16; + + if (baseDefinitions) + { + int varCount = reader.ReadInt32(); + int stringSize = reader.ReadInt32(); + + reader.Position += varCount * 24; + using (var stringReader = new BinaryReader(new MemoryStream(reader.ReadBytes(stringSize)))) + { + //build Class Structures + reader.Position -= varCount * 24 + stringSize; + for (int i = 0; i < varCount; i++) + { + ushort num0 = reader.ReadUInt16(); + byte level = reader.ReadByte(); + bool isArray = reader.ReadBoolean(); + + ushort varTypeIndex = reader.ReadUInt16(); + ushort test = reader.ReadUInt16(); + string varTypeStr; + if (test == 0) //varType is an offset in the string block + { + stringReader.BaseStream.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 = reader.ReadUInt16(); + test = reader.ReadUInt16(); + string varNameStr; + if (test == 0) + { + stringReader.BaseStream.Position = varNameIndex; + varNameStr = stringReader.ReadStringToNull(); + } + else + { + varNameStr = baseStrings.ContainsKey(varNameIndex) ? baseStrings[varNameIndex] : varNameIndex.ToString(); + } + + int size = reader.ReadInt32(); + int index = reader.ReadInt32(); + int flag = reader.ReadInt32(); + } + reader.Position += stringSize; + } + } + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/BinaryReaderExtensions.cs b/AzurLaneLive2DExtract/AssetStudioCore/BinaryReaderExtensions.cs new file mode 100644 index 0000000..8b4bc0f --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/BinaryReaderExtensions.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using SharpDX; + +namespace AssetStudioCore +{ + public static class BinaryReaderExtensions + { + public static void AlignStream(this BinaryReader reader, int alignment) + { + var pos = reader.BaseStream.Position; + var mod = pos % alignment; + if (mod != 0) + { + reader.BaseStream.Position += alignment - mod; + } + } + + public static string ReadAlignedString(this BinaryReader reader) + { + return ReadAlignedString(reader, reader.ReadInt32()); + } + + public static string ReadAlignedString(this BinaryReader reader, int length) + { + if (length > 0 && length < (reader.BaseStream.Length - reader.BaseStream.Position)) + { + var stringData = reader.ReadBytes(length); + var result = Encoding.UTF8.GetString(stringData); + reader.AlignStream(4); + return result; + } + return ""; + } + + public static string ReadStringToNull(this BinaryReader reader) + { + var bytes = new List(); + byte b; + while (reader.BaseStream.Position != reader.BaseStream.Length && (b = reader.ReadByte()) != 0) + bytes.Add(b); + return Encoding.UTF8.GetString(bytes.ToArray()); + } + + public static Quaternion ReadQuaternion(this BinaryReader reader) + { + var q = new Quaternion + { + X = reader.ReadSingle(), + Y = reader.ReadSingle(), + Z = reader.ReadSingle(), + W = reader.ReadSingle() + }; + return q; + } + + public static Vector2 ReadVector2(this BinaryReader reader) + { + Vector2 v = new Vector2 + { + X = reader.ReadSingle(), + Y = reader.ReadSingle() + }; + return v; + } + + public static Vector3 ReadVector3(this BinaryReader reader) + { + var v = new Vector3 + { + X = reader.ReadSingle(), + Y = reader.ReadSingle(), + Z = reader.ReadSingle() + }; + return v; + } + + public static Vector4 ReadVector4(this BinaryReader reader) + { + var v = new Vector4 + { + X = reader.ReadSingle(), + Y = reader.ReadSingle(), + Z = reader.ReadSingle(), + W = reader.ReadSingle() + }; + return v; + } + + private static T[] ReadArray(Func del, int length) + { + var array = new T[length]; + for (int i = 0; i < array.Length; i++) + { + array[i] = del(); + } + return array; + } + + public static int[] ReadInt32Array(this BinaryReader reader, int length) + { + return ReadArray(reader.ReadInt32, length); + } + + public static uint[] ReadUInt32Array(this BinaryReader reader, int length) + { + return ReadArray(reader.ReadUInt32, length); + } + + public static float[] ReadSingleArray(this BinaryReader reader, int length) + { + return ReadArray(reader.ReadSingle, length); + } + + public static Vector2[] ReadVector2Array(this BinaryReader reader, int length) + { + return ReadArray(reader.ReadVector2, length); + } + + public static Vector4[] ReadVector4Array(this BinaryReader reader, int length) + { + return ReadArray(reader.ReadVector4, length); + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/BinaryWriterExtensions.cs b/AzurLaneLive2DExtract/AssetStudioCore/BinaryWriterExtensions.cs new file mode 100644 index 0000000..6fb1fed --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/BinaryWriterExtensions.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace AssetStudioCore +{ + public static class BinaryWriterExtensions + { + private static void WriteArray(Action del, T[] array) + { + foreach (var item in array) + { + del(item); + } + } + + public static void Write(this BinaryWriter writer, uint[] array) + { + WriteArray(writer.Write, array); + } + + public static void AlignStream(this BinaryWriter writer, int alignment) + { + var pos = writer.BaseStream.Position; + var mod = pos % alignment; + if (mod != 0) + { + writer.Write(new byte[alignment - mod]); + } + } + + public static void WriteAlignedString(this BinaryWriter writer, string str) + { + var bytes = Encoding.UTF8.GetBytes(str); + writer.Write(bytes.Length); + writer.Write(bytes); + writer.AlignStream(4); + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/BuildTarget.cs b/AzurLaneLive2DExtract/AssetStudioCore/BuildTarget.cs new file mode 100644 index 0000000..fddef63 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/BuildTarget.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudioCore +{ + 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 + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/BundleFile.cs b/AzurLaneLive2DExtract/AssetStudioCore/BundleFile.cs new file mode 100644 index 0000000..9ea866d --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/BundleFile.cs @@ -0,0 +1,176 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Lz4; + +namespace AssetStudioCore +{ + public class StreamFile + { + public string fileName; + public Stream stream; + } + + public class BlockInfo + { + public uint compressedSize; + public uint uncompressedSize; + public short flag; + } + + public class BundleFile + { + private string path; + public string versionPlayer; + public string versionEngine; + public List fileList = new List(); + + public BundleFile(string path, EndianBinaryReader bundleReader) + { + this.path = path; + var signature = bundleReader.ReadStringToNull(); + switch (signature) + { + case "UnityFS": + { + var format = bundleReader.ReadInt32(); + versionPlayer = bundleReader.ReadStringToNull(); + versionEngine = bundleReader.ReadStringToNull(); + if (format == 6) + { + ReadFormat6(bundleReader); + } + break; + } + } + } + + 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 blocksInfoReader = new EndianBinaryReader(blocksInfoStream)) + { + blocksInfoReader.Position = 0x10; + int blockcount = blocksInfoReader.ReadInt32(); + var blockInfos = new BlockInfo[blockcount]; + for (int i = 0; i < blockcount; i++) + { + blockInfos[i] = new BlockInfo + { + uncompressedSize = blocksInfoReader.ReadUInt32(), + compressedSize = blocksInfoReader.ReadUInt32(), + flag = blocksInfoReader.ReadInt16() + }; + } + Stream dataStream; + var uncompressedSizeSum = blockInfos.Sum(x => x.uncompressedSize); + if (uncompressedSizeSum > int.MaxValue) + { + /*var memoryMappedFile = MemoryMappedFile.CreateNew(Path.GetFileName(path), uncompressedSizeSum); + assetsDataStream = memoryMappedFile.CreateViewStream();*/ + dataStream = new FileStream(path + ".temp", FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose); + } + else + { + dataStream = new MemoryStream(); + } + foreach (var blockInfo in blockInfos) + { + switch (blockInfo.flag & 0x3F) + { + default://None + { + bundleReader.BaseStream.CopyTo(dataStream, blockInfo.compressedSize); + break; + } + case 1://LZMA + { + SevenZipHelper.StreamDecompress(bundleReader.BaseStream, dataStream, blockInfo.compressedSize, blockInfo.uncompressedSize); + break; + } + case 2://LZ4 + case 3://LZ4HC + { + var lz4Stream = new Lz4DecoderStream(bundleReader.BaseStream, blockInfo.compressedSize); + lz4Stream.CopyTo(dataStream, blockInfo.uncompressedSize); + break; + } + //case 4:LZHAM? + } + } + dataStream.Position = 0; + using (dataStream) + { + var entryinfo_count = blocksInfoReader.ReadInt32(); + for (int i = 0; i < entryinfo_count; i++) + { + var file = new StreamFile(); + var entryinfo_offset = blocksInfoReader.ReadInt64(); + var entryinfo_size = blocksInfoReader.ReadInt64(); + flag = blocksInfoReader.ReadInt32(); + file.fileName = Path.GetFileName(blocksInfoReader.ReadStringToNull()); + if (entryinfo_size > int.MaxValue) + { + /*var memoryMappedFile = MemoryMappedFile.CreateNew(file.fileName, entryinfo_size); + file.stream = memoryMappedFile.CreateViewStream();*/ + var extractPath = path + "_unpacked\\"; + Directory.CreateDirectory(extractPath); + file.stream = File.Create(extractPath + file.fileName); + } + else + { + file.stream = new MemoryStream(); + } + dataStream.Position = entryinfo_offset; + dataStream.CopyTo(file.stream, entryinfo_size); + file.stream.Position = 0; + fileList.Add(file); + } + } + } + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/ClassIDReference.cs b/AzurLaneLive2DExtract/AssetStudioCore/ClassIDReference.cs new file mode 100644 index 0000000..679ae97 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/ClassIDReference.cs @@ -0,0 +1,272 @@ +namespace AssetStudioCore +{ + 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 + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Classes/AnimationClip.cs b/AzurLaneLive2DExtract/AssetStudioCore/Classes/AnimationClip.cs new file mode 100644 index 0000000..c34a2d9 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Classes/AnimationClip.cs @@ -0,0 +1,769 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Text; +using SharpDX; + +namespace AssetStudioCore.Classes +{ + public class Keyframe + { + public float time { get; set; } + public T value { get; set; } + public T inSlope { get; set; } + public T outSlope { get; set; } + public int weightedMode { get; set; } + public T inWeight { get; set; } + public T outWeight { get; set; } + + + public Keyframe(EndianBinaryReader reader, Func readerFunc, int[] version) + { + time = reader.ReadSingle(); + value = readerFunc(); + inSlope = readerFunc(); + outSlope = readerFunc(); + if (version[0] >= 2018) + { + weightedMode = reader.ReadInt32(); + inWeight = readerFunc(); + outWeight = readerFunc(); + } + } + } + + public class AnimationCurve + { + public List> m_Curve { get; set; } + public int m_PreInfinity { get; set; } + public int m_PostInfinity { get; set; } + public int m_RotationOrder { get; set; } + + public AnimationCurve(EndianBinaryReader reader, Func readerFunc, int[] version) + { + int numCurves = reader.ReadInt32(); + m_Curve = new List>(numCurves); + for (int i = 0; i < numCurves; i++) + { + m_Curve.Add(new Keyframe(reader, readerFunc, version)); + } + + m_PreInfinity = reader.ReadInt32(); + m_PostInfinity = reader.ReadInt32(); + if (version[0] > 5 || (version[0] == 5 && version[1] >= 3))//5.3 and up + { + m_RotationOrder = reader.ReadInt32(); + } + } + } + + public class QuaternionCurve + { + public AnimationCurve curve { get; set; } + public string path { get; set; } + + public QuaternionCurve(EndianBinaryReader reader, int[] version) + { + curve = new AnimationCurve(reader, reader.ReadQuaternion, version); + path = reader.ReadAlignedString(); + } + } + + public class PackedBitVector + { + public uint m_NumItems { get; set; } + public float m_Range { get; set; } + public float m_Start { get; set; } + public byte[] m_Data { get; set; } + public byte m_BitSize { get; set; } + + public PackedBitVector(EndianBinaryReader reader) + { + m_NumItems = reader.ReadUInt32(); + m_Range = reader.ReadSingle(); + m_Start = reader.ReadSingle(); + + int numData = reader.ReadInt32(); + m_Data = reader.ReadBytes(numData); + reader.AlignStream(4); + + m_BitSize = reader.ReadByte(); + reader.AlignStream(4); + } + } + + public class PackedBitVector2 + { + public uint m_NumItems { get; set; } + public byte[] m_Data { get; set; } + public byte m_BitSize { get; set; } + + public PackedBitVector2(EndianBinaryReader reader) + { + m_NumItems = reader.ReadUInt32(); + + int numData = reader.ReadInt32(); + m_Data = reader.ReadBytes(numData); + reader.AlignStream(4); + + m_BitSize = reader.ReadByte(); + reader.AlignStream(4); + } + } + + public class PackedBitVector3 + { + public uint m_NumItems { get; set; } + public byte[] m_Data { get; set; } + + public PackedBitVector3(EndianBinaryReader reader) + { + m_NumItems = reader.ReadUInt32(); + + int numData = reader.ReadInt32(); + m_Data = reader.ReadBytes(numData); + + reader.AlignStream(4); + } + } + + public class CompressedAnimationCurve + { + public string m_Path { get; set; } + public PackedBitVector2 m_Times { get; set; } + public PackedBitVector3 m_Values { get; set; } + public PackedBitVector m_Slopes { get; set; } + public int m_PreInfinity { get; set; } + public int m_PostInfinity { get; set; } + + public CompressedAnimationCurve(EndianBinaryReader reader) + { + m_Path = reader.ReadAlignedString(); + m_Times = new PackedBitVector2(reader); + m_Values = new PackedBitVector3(reader); + m_Slopes = new PackedBitVector(reader); + m_PreInfinity = reader.ReadInt32(); + m_PostInfinity = reader.ReadInt32(); + } + } + + public class Vector3Curve + { + public AnimationCurve curve { get; set; } + public string path { get; set; } + + public Vector3Curve(EndianBinaryReader reader, int[] version) + { + curve = new AnimationCurve(reader, reader.ReadVector3, version); + path = reader.ReadAlignedString(); + } + } + + public class FloatCurve + { + public AnimationCurve curve { get; set; } + public string attribute { get; set; } + public string path { get; set; } + public int classID { get; set; } + public PPtr script { get; set; } + + + public FloatCurve(AssetPreloadData preloadData) + { + var reader = preloadData.sourceFile.reader; + curve = new AnimationCurve(reader, reader.ReadSingle, preloadData.sourceFile.version); + attribute = reader.ReadAlignedString(); + path = reader.ReadAlignedString(); + classID = reader.ReadInt32(); + script = preloadData.sourceFile.ReadPPtr(); + } + } + + public class PPtrKeyframe + { + public float time { get; set; } + public PPtr value { get; set; } + + + public PPtrKeyframe(AssetPreloadData preloadData) + { + var reader = preloadData.sourceFile.reader; + time = reader.ReadSingle(); + value = preloadData.sourceFile.ReadPPtr(); + } + } + + public class PPtrCurve + { + public List curve { get; set; } + public string attribute { get; set; } + public string path { get; set; } + public int classID { get; set; } + public PPtr script { get; set; } + + + public PPtrCurve(AssetPreloadData preloadData) + { + var reader = preloadData.sourceFile.reader; + + int numCurves = reader.ReadInt32(); + curve = new List(numCurves); + for (int i = 0; i < numCurves; i++) + { + curve.Add(new PPtrKeyframe(preloadData)); + } + + attribute = reader.ReadAlignedString(); + path = reader.ReadAlignedString(); + classID = reader.ReadInt32(); + script = preloadData.sourceFile.ReadPPtr(); + } + } + + public class AABB + { + public Vector3 m_Center { get; set; } + public Vector3 m_Extend { get; set; } + + public AABB(EndianBinaryReader reader) + { + m_Center = reader.ReadVector3(); + m_Extend = reader.ReadVector3(); + } + } + + public class xform + { + public object t { get; set; } + public Quaternion q { get; set; } + public object s { get; set; } + + public xform(EndianBinaryReader reader, int[] version) + { + t = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? (object)reader.ReadVector3() : (object)reader.ReadVector4();//5.4 and up + q = reader.ReadQuaternion(); + s = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? (object)reader.ReadVector3() : (object)reader.ReadVector4();//5.4 and up + } + } + + public class HandPose + { + public xform m_GrabX { get; set; } + public float[] m_DoFArray { get; set; } + public float m_Override { get; set; } + public float m_CloseOpen { get; set; } + public float m_InOut { get; set; } + public float m_Grab { get; set; } + + public HandPose(EndianBinaryReader reader, int[] version) + { + m_GrabX = new xform(reader, version); + + int numDoFs = reader.ReadInt32(); + m_DoFArray = reader.ReadSingleArray(numDoFs); + + m_Override = reader.ReadSingle(); + m_CloseOpen = reader.ReadSingle(); + m_InOut = reader.ReadSingle(); + m_Grab = reader.ReadSingle(); + } + } + + public class HumanGoal + { + public xform m_X { get; set; } + public float m_WeightT { get; set; } + public float m_WeightR { get; set; } + public object m_HintT { get; set; } + public float m_HintWeightT { get; set; } + + public HumanGoal(EndianBinaryReader reader, int[] version) + { + m_X = new xform(reader, version); + m_WeightT = reader.ReadSingle(); + m_WeightR = reader.ReadSingle(); + if (version[0] >= 5)//5.0 and up + { + m_HintT = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? (object)reader.ReadVector3() : (object)reader.ReadVector4();//5.4 and up + m_HintWeightT = reader.ReadSingle(); + } + } + } + + public class HumanPose + { + public xform m_RootX { get; set; } + public object m_LookAtPosition { get; set; } + public Vector4 m_LookAtWeight { get; set; } + public List m_GoalArray { get; set; } + public HandPose m_LeftHandPose { get; set; } + public HandPose m_RightHandPose { get; set; } + public float[] m_DoFArray { get; set; } + public object[] m_TDoFArray { get; set; } + + public HumanPose(EndianBinaryReader reader, int[] version) + { + m_RootX = new xform(reader, version); + m_LookAtPosition = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? (object)reader.ReadVector3() : (object)reader.ReadVector4();//5.4 and up + m_LookAtWeight = reader.ReadVector4(); + + int numGoals = reader.ReadInt32(); + m_GoalArray = new List(numGoals); + for (int i = 0; i < numGoals; i++) + { + m_GoalArray.Add(new HumanGoal(reader, version)); + } + + m_LeftHandPose = new HandPose(reader, version); + m_RightHandPose = new HandPose(reader, version); + + int numDoFs = reader.ReadInt32(); + m_DoFArray = reader.ReadSingleArray(numDoFs); + + if (version[0] > 5 || (version[0] == 5 && version[1] >= 2))//5.2 and up + { + int numTDof = reader.ReadInt32(); + m_TDoFArray = new object[numTDof]; + for (int i = 0; i < numTDof; i++) + { + m_TDoFArray[i] = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? (object)reader.ReadVector3() : (object)reader.ReadVector4();//5.4 and up + } + } + } + } + + public class StreamedClip + { + public uint[] data { get; set; } + public uint curveCount { get; set; } + + public StreamedClip(EndianBinaryReader reader) + { + int numData = reader.ReadInt32(); + data = reader.ReadUInt32Array(numData); + curveCount = reader.ReadUInt32(); + } + + public class StreamedCurveKey + { + public int index { get; set; } + public Vector3 tcb { get; set; } + public float value { get; set; } + public StreamedCurveKey(BinaryReader reader) + { + index = reader.ReadInt32(); + tcb = reader.ReadVector3(); + value = reader.ReadSingle(); + } + } + + public class StreamedFrame + { + public float time { get; set; } + public List keyList { get; set; } + + public StreamedFrame(BinaryReader reader) + { + time = reader.ReadSingle(); + + int numKeys = reader.ReadInt32(); + keyList = new List(numKeys); + for (int i = 0; i < numKeys; i++) + { + keyList.Add(new StreamedCurveKey(reader)); + } + } + } + + public List ReadData() + { + List frameList = new List(); + using (Stream stream = new MemoryStream()) + { + BinaryWriter writer = new BinaryWriter(stream); + writer.Write(data); + stream.Position = 0; + while (stream.Position < stream.Length) + { + frameList.Add(new StreamedFrame(new BinaryReader(stream))); + } + } + return frameList; + } + } + + public class DenseClip + { + public int m_FrameCount { get; set; } + public uint m_CurveCount { get; set; } + public float m_SampleRate { get; set; } + public float m_BeginTime { get; set; } + public float[] m_SampleArray { get; set; } + + public DenseClip(EndianBinaryReader reader) + { + m_FrameCount = reader.ReadInt32(); + m_CurveCount = reader.ReadUInt32(); + m_SampleRate = reader.ReadSingle(); + m_BeginTime = reader.ReadSingle(); + + int numSamples = reader.ReadInt32(); + m_SampleArray = reader.ReadSingleArray(numSamples); + } + } + + public class ConstantClip + { + public float[] data { get; set; } + + public ConstantClip(EndianBinaryReader reader) + { + int numData = reader.ReadInt32(); + data = reader.ReadSingleArray(numData); + } + } + + public class ValueConstant + { + public uint m_ID { get; set; } + public uint m_TypeID { get; set; } + public uint m_Type { get; set; } + public uint m_Index { get; set; } + + public ValueConstant(EndianBinaryReader reader, int[] version) + { + m_ID = reader.ReadUInt32(); + if (version[0] < 5 || (version[0] == 5 && version[1] < 5))//5.5 down + { + m_TypeID = reader.ReadUInt32(); + } + m_Type = reader.ReadUInt32(); + m_Index = reader.ReadUInt32(); + } + } + + public class ValueArrayConstant + { + public List m_ValueArray { get; set; } + + public ValueArrayConstant(EndianBinaryReader reader, int[] version) + { + int numVals = reader.ReadInt32(); + m_ValueArray = new List(numVals); + for (int i = 0; i < numVals; i++) + { + m_ValueArray.Add(new ValueConstant(reader, version)); + } + } + } + + public class Clip + { + public StreamedClip m_StreamedClip { get; set; } + public DenseClip m_DenseClip { get; set; } + public ConstantClip m_ConstantClip { get; set; } + public ValueArrayConstant m_Binding { get; set; } + + public Clip(EndianBinaryReader reader, int[] version) + { + m_StreamedClip = new StreamedClip(reader); + m_DenseClip = new DenseClip(reader); + m_ConstantClip = new ConstantClip(reader); + m_Binding = new ValueArrayConstant(reader, version); + } + } + + public class ValueDelta + { + public float m_Start { get; set; } + public float m_Stop { get; set; } + + public ValueDelta(EndianBinaryReader reader) + { + m_Start = reader.ReadSingle(); + m_Stop = reader.ReadSingle(); + } + } + + public class ClipMuscleConstant + { + public HumanPose m_DeltaPose { get; set; } + public xform m_StartX { get; set; } + public xform m_StopX { get; set; } + public xform m_LeftFootStartX { get; set; } + public xform m_RightFootStartX { get; set; } + public xform m_MotionStartX { get; set; } + public xform m_MotionStopX { get; set; } + public object m_AverageSpeed { get; set; } + public Clip m_Clip { get; set; } + public float m_StartTime { get; set; } + public float m_StopTime { get; set; } + public float m_OrientationOffsetY { get; set; } + public float m_Level { get; set; } + public float m_CycleOffset { get; set; } + public float m_AverageAngularSpeed { get; set; } + public int[] m_IndexArray { get; set; } + public List m_ValueArrayDelta { get; set; } + public float[] m_ValueArrayReferencePose { get; set; } + public bool m_Mirror { get; set; } + public bool m_LoopTime { get; set; } + public bool m_LoopBlend { get; set; } + public bool m_LoopBlendOrientation { get; set; } + public bool m_LoopBlendPositionY { get; set; } + public bool m_LoopBlendPositionXZ { get; set; } + public bool m_StartAtOrigin { get; set; } + public bool m_KeepOriginalOrientation { get; set; } + public bool m_KeepOriginalPositionY { get; set; } + public bool m_KeepOriginalPositionXZ { get; set; } + public bool m_HeightFromFeet { get; set; } + + public ClipMuscleConstant(EndianBinaryReader reader, int[] version) + { + m_DeltaPose = new HumanPose(reader, version); + m_StartX = new xform(reader, version); + if (version[0] > 5 || (version[0] == 5 && version[1] >= 5))//5.5 and up + { + m_StopX = new xform(reader, version); + } + m_LeftFootStartX = new xform(reader, version); + m_RightFootStartX = new xform(reader, version); + if (version[0] < 5)//5.0 down + { + m_MotionStartX = new xform(reader, version); + m_MotionStopX = new xform(reader, version); + } + m_AverageSpeed = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? (object)reader.ReadVector3() : (object)reader.ReadVector4();//5.4 and up + m_Clip = new Clip(reader, version); + m_StartTime = reader.ReadSingle(); + m_StopTime = reader.ReadSingle(); + m_OrientationOffsetY = reader.ReadSingle(); + m_Level = reader.ReadSingle(); + m_CycleOffset = reader.ReadSingle(); + m_AverageAngularSpeed = reader.ReadSingle(); + + int numIndices = reader.ReadInt32(); + m_IndexArray = reader.ReadInt32Array(numIndices); + + int numDeltas = reader.ReadInt32(); + m_ValueArrayDelta = new List(numDeltas); + for (int i = 0; i < numDeltas; i++) + { + m_ValueArrayDelta.Add(new ValueDelta(reader)); + } + if (version[0] > 5 || (version[0] == 5 && version[1] >= 3))//5.3 and up + { + m_ValueArrayReferencePose = reader.ReadSingleArray(reader.ReadInt32()); + } + + m_Mirror = reader.ReadBoolean(); + m_LoopTime = reader.ReadBoolean(); + m_LoopBlend = reader.ReadBoolean(); + m_LoopBlendOrientation = reader.ReadBoolean(); + m_LoopBlendPositionY = reader.ReadBoolean(); + m_LoopBlendPositionXZ = reader.ReadBoolean(); + if (version[0] > 5 || (version[0] == 5 && version[1] >= 5))//5.5 and up + { + m_StartAtOrigin = reader.ReadBoolean(); + } + m_KeepOriginalOrientation = reader.ReadBoolean(); + m_KeepOriginalPositionY = reader.ReadBoolean(); + m_KeepOriginalPositionXZ = reader.ReadBoolean(); + m_HeightFromFeet = reader.ReadBoolean(); + reader.AlignStream(4); + } + } + + public class GenericBinding + { + public uint path { get; set; } + public uint attribute { get; set; } + public PPtr script { get; set; } + public int typeID { get; set; } + public byte customType { get; set; } + public byte isPPtrCurve { get; set; } + + public GenericBinding(AssetPreloadData preloadData) + { + var reader = preloadData.sourceFile.reader; + var version = preloadData.sourceFile.version; + path = reader.ReadUInt32(); + attribute = reader.ReadUInt32(); + script = preloadData.sourceFile.ReadPPtr(); + if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up + { + typeID = reader.ReadInt32(); + } + else + { + typeID = reader.ReadUInt16(); + } + customType = reader.ReadByte(); + isPPtrCurve = reader.ReadByte(); + reader.AlignStream(4); + } + } + + public class AnimationClipBindingConstant + { + public List genericBindings { get; set; } + public List pptrCurveMapping { get; set; } + + public AnimationClipBindingConstant(AssetPreloadData preloadData) + { + var reader = preloadData.sourceFile.reader; + int numBindings = reader.ReadInt32(); + genericBindings = new List(numBindings); + for (int i = 0; i < numBindings; i++) + { + genericBindings.Add(new GenericBinding(preloadData)); + } + + int numMappings = reader.ReadInt32(); + pptrCurveMapping = new List(numMappings); + for (int i = 0; i < numMappings; i++) + { + pptrCurveMapping.Add(preloadData.sourceFile.ReadPPtr()); + } + } + + public GenericBinding FindBinding(int index) + { + int curves = 0; + foreach (var b in genericBindings) + { + curves += b.attribute == 2 ? 4 : b.attribute <= 4 ? 3 : 1; + if (curves > index) + { + return b; + } + } + + return null; + } + } + + public class AnimationClip : NamedObject + { + public int m_AnimationType { get; set; } + public bool m_Legacy { get; set; } + public bool m_Compressed { get; set; } + public bool m_UseHighQualityCurve { get; set; } + public List m_RotationCurves { get; set; } + public List m_CompressedRotationCurves { get; set; } + public List m_EulerCurves { get; set; } + public List m_PositionCurves { get; set; } + public List m_ScaleCurves { get; set; } + public List m_FloatCurves { get; set; } + public List m_PPtrCurves { get; set; } + public float m_SampleRate { get; set; } + public int m_WrapMode { get; set; } + public AABB m_Bounds { get; set; } + public uint m_MuscleClipSize { get; set; } + public ClipMuscleConstant m_MuscleClip { get; set; } + public AnimationClipBindingConstant m_ClipBindingConstant { get; set; } + //public List m_Events { get; set; } + + + public AnimationClip(AssetPreloadData preloadData) : base(preloadData) + { + if (version[0] >= 5)//5.0 and up + { + m_Legacy = reader.ReadBoolean(); + } + else if (version[0] >= 4)//4.0 and up + { + m_AnimationType = reader.ReadInt32(); + if (m_AnimationType == 1) + m_Legacy = true; + } + else + { + m_Legacy = true; + } + m_Compressed = reader.ReadBoolean(); + if (version[0] > 4 || (version[0] == 4 && version[1] >= 3))//4.3 and up + { + m_UseHighQualityCurve = reader.ReadBoolean(); + } + reader.AlignStream(4); + int numRCurves = reader.ReadInt32(); + m_RotationCurves = new List(numRCurves); + for (int i = 0; i < numRCurves; i++) + { + m_RotationCurves.Add(new QuaternionCurve(reader, version)); + } + + int numCRCurves = reader.ReadInt32(); + m_CompressedRotationCurves = new List(numCRCurves); + for (int i = 0; i < numCRCurves; i++) + { + m_CompressedRotationCurves.Add(new CompressedAnimationCurve(reader)); + } + + if (version[0] > 5 || (version[0] == 5 && version[1] >= 3))//5.3 and up + { + int numEulerCurves = reader.ReadInt32(); + m_EulerCurves = new List(numEulerCurves); + for (int i = 0; i < numEulerCurves; i++) + { + m_EulerCurves.Add(new Vector3Curve(reader, version)); + } + } + + int numPCurves = reader.ReadInt32(); + m_PositionCurves = new List(numPCurves); + for (int i = 0; i < numPCurves; i++) + { + m_PositionCurves.Add(new Vector3Curve(reader, version)); + } + + int numSCurves = reader.ReadInt32(); + m_ScaleCurves = new List(numSCurves); + for (int i = 0; i < numSCurves; i++) + { + m_ScaleCurves.Add(new Vector3Curve(reader, version)); + } + + int numFCurves = reader.ReadInt32(); + m_FloatCurves = new List(numFCurves); + for (int i = 0; i < numFCurves; i++) + { + m_FloatCurves.Add(new FloatCurve(preloadData)); + } + + if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up + { + int numPtrCurves = reader.ReadInt32(); + m_PPtrCurves = new List(numPtrCurves); + for (int i = 0; i < numPtrCurves; i++) + { + m_PPtrCurves.Add(new PPtrCurve(preloadData)); + } + } + + m_SampleRate = reader.ReadSingle(); + m_WrapMode = reader.ReadInt32(); + if (version[0] > 3 || (version[0] == 3 && version[1] >= 4)) //3.4 and up + { + m_Bounds = new AABB(reader); + } + if (version[0] >= 4)//4.0 and up + { + m_MuscleClipSize = reader.ReadUInt32(); + m_MuscleClip = new ClipMuscleConstant(reader, version); + } + if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up + { + m_ClipBindingConstant = new AnimationClipBindingConstant(preloadData); + } + /*int numEvents = reader.ReadInt32(); + m_Events = new List(numEvents); + for (int i = 0; i < numEvents; i++) + { + m_Events.Add(new AnimationEvent(stream, file.Version[0] - '0')); + }*/ + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Classes/Animator.cs b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Animator.cs new file mode 100644 index 0000000..0ccd253 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Animator.cs @@ -0,0 +1,14 @@ +namespace AssetStudioCore.Classes +{ + public sealed class Animator : Behaviour + { + public PPtr m_Avatar; + public PPtr m_Controller; + + public Animator(AssetPreloadData preloadData) : base(preloadData) + { + m_Avatar = sourceFile.ReadPPtr(); + m_Controller = sourceFile.ReadPPtr(); + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Classes/Behaviour.cs b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Behaviour.cs new file mode 100644 index 0000000..7f3fec0 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Behaviour.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudioCore.Classes +{ + public abstract class Behaviour : Component + { + protected Behaviour(AssetPreloadData preloadData) : base(preloadData) + { + var m_Enabled = reader.ReadByte(); + reader.AlignStream(4); + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Classes/Component.cs b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Component.cs new file mode 100644 index 0000000..ac645e9 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Component.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudioCore.Classes +{ + public abstract class Component : EditorExtension + { + public PPtr m_GameObject; + + protected Component(AssetPreloadData preloadData) : base(preloadData) + { + m_GameObject = sourceFile.ReadPPtr(); + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Classes/EditorExtension.cs b/AzurLaneLive2DExtract/AssetStudioCore/Classes/EditorExtension.cs new file mode 100644 index 0000000..da20b70 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Classes/EditorExtension.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudioCore.Classes +{ + public abstract class EditorExtension : Object + { + protected EditorExtension(AssetPreloadData preloadData) : base(preloadData) + { + if (platform == BuildTarget.NoTarget) + { + var m_PrefabParentObject = sourceFile.ReadPPtr(); + var m_PrefabInternal = sourceFile.ReadPPtr(); + } + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Classes/GameObject.cs b/AzurLaneLive2DExtract/AssetStudioCore/Classes/GameObject.cs new file mode 100644 index 0000000..007eace --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Classes/GameObject.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; + +namespace AssetStudioCore.Classes +{ + public sealed class GameObject : EditorExtension + { + public List m_Component; + public string m_Name; + + public GameObject(AssetPreloadData preloadData) : base(preloadData) + { + int m_Component_size = reader.ReadInt32(); + m_Component = new List(m_Component_size); + for (int j = 0; j < m_Component_size; j++) + { + if ((version[0] == 5 && version[1] >= 5) || version[0] > 5)//5.5.0 and up + { + m_Component.Add(sourceFile.ReadPPtr()); + } + else + { + int first = reader.ReadInt32(); + m_Component.Add(sourceFile.ReadPPtr()); + } + } + var m_Layer = reader.ReadInt32(); + m_Name = reader.ReadAlignedString(); + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Classes/NamedObject.cs b/AzurLaneLive2DExtract/AssetStudioCore/Classes/NamedObject.cs new file mode 100644 index 0000000..81bb90a --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Classes/NamedObject.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudioCore.Classes +{ + public abstract class NamedObject : EditorExtension + { + public string m_Name; + + protected NamedObject(AssetPreloadData preloadData) : base(preloadData) + { + m_Name = reader.ReadAlignedString(); + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Classes/Object.cs b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Object.cs new file mode 100644 index 0000000..a211568 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Object.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudioCore.Classes +{ + public abstract class Object + { + public AssetsFile sourceFile; + protected EndianBinaryReader reader; + protected int[] version; + protected string[] buildType; + protected BuildTarget platform; + + + protected Object(AssetPreloadData preloadData) + { + sourceFile = preloadData.sourceFile; + reader = preloadData.InitReader(); + version = sourceFile.version; + buildType = sourceFile.buildType; + platform = (BuildTarget)sourceFile.platform; + + if (platform == BuildTarget.NoTarget) + { + var m_ObjectHideFlags = reader.ReadUInt32(); + } + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Classes/TextAsset.cs b/AzurLaneLive2DExtract/AssetStudioCore/Classes/TextAsset.cs new file mode 100644 index 0000000..2dbc376 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Classes/TextAsset.cs @@ -0,0 +1,12 @@ +namespace AssetStudioCore.Classes +{ + public sealed class TextAsset : NamedObject + { + public byte[] m_Script; + + public TextAsset(AssetPreloadData preloadData) : base(preloadData) + { + m_Script = reader.ReadBytes(reader.ReadInt32()); + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Classes/Texture.cs b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Texture.cs new file mode 100644 index 0000000..bab2949 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Texture.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudioCore.Classes +{ + public abstract class Texture : NamedObject + { + protected Texture(AssetPreloadData preloadData) : base(preloadData) + { + if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)) //2017.3 and up + { + var m_ForcedFallbackFormat = reader.ReadInt32(); + var m_DownscaleFallback = reader.ReadBoolean(); + reader.AlignStream(4); + } + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Classes/Texture2D.cs b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Texture2D.cs new file mode 100644 index 0000000..f2b509f --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Texture2D.cs @@ -0,0 +1,155 @@ + + +namespace AssetStudioCore.Classes +{ + class Texture2D : Texture + { + public int m_Width; + public int m_Height; + public int m_CompleteImageSize; + public TextureFormat m_TextureFormat; + public bool m_MipMap; + public int m_MipCount; + public bool m_IsReadable; + public bool m_ReadAllowed; + public int m_ImageCount; + public int m_TextureDimension; + //m_TextureSettings + public int m_FilterMode; + public int m_Aniso; + public float m_MipBias; + public int m_WrapMode; + public int m_LightmapFormat; + public int m_ColorSpace; + //image dataa + public int image_data_size; + public byte[] image_data; + //m_StreamData + public uint offset; + public uint size; + public string path; + + public Texture2D(AssetPreloadData preloadData) : base(preloadData) + { + m_Width = reader.ReadInt32(); + m_Height = reader.ReadInt32(); + m_CompleteImageSize = reader.ReadInt32(); + m_TextureFormat = (TextureFormat)reader.ReadInt32(); + + if (version[0] < 5 || (version[0] == 5 && version[1] < 2)) + { + m_MipMap = reader.ReadBoolean(); + } + else + { + m_MipCount = reader.ReadInt32(); + } + + m_IsReadable = reader.ReadBoolean(); //2.6.0 and up + m_ReadAllowed = reader.ReadBoolean(); //3.0.0 - 5.4 + //m_StreamingMipmaps 2018.2 and up + reader.AlignStream(4); + if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up + { + var m_StreamingMipmapsPriority = reader.ReadInt32(); + } + m_ImageCount = reader.ReadInt32(); + m_TextureDimension = reader.ReadInt32(); + //m_TextureSettings + m_FilterMode = reader.ReadInt32(); + m_Aniso = reader.ReadInt32(); + m_MipBias = reader.ReadSingle(); + if (version[0] >= 2017)//2017.x and up + { + int m_WrapU = reader.ReadInt32(); + int m_WrapV = reader.ReadInt32(); + int m_WrapW = reader.ReadInt32(); + } + else + { + m_WrapMode = reader.ReadInt32(); + } + if (version[0] >= 3) + { + m_LightmapFormat = reader.ReadInt32(); + if (version[0] >= 4 || version[1] >= 5)//3.5.0 and up + { + m_ColorSpace = reader.ReadInt32(); + } + } + + image_data_size = reader.ReadInt32(); + + if (image_data_size == 0 && ((version[0] == 5 && version[1] >= 3) || version[0] > 5))//5.3.0 and up + { + offset = reader.ReadUInt32(); + size = reader.ReadUInt32(); + image_data_size = (int)size; + path = reader.ReadAlignedString(); + } + + image_data = reader.ReadBytes(image_data_size); + } + } + + public enum TextureFormat + { + Alpha8 = 1, + ARGB4444, + RGB24, + RGBA32, + ARGB32, + RGB565 = 7, + R16 = 9, + DXT1, + DXT5 = 12, + RGBA4444, + BGRA32, + RHalf, + RGHalf, + RGBAHalf, + RFloat, + RGFloat, + RGBAFloat, + YUY2, + RGB9e5Float, + BC4 = 26, + BC5, + BC6H = 24, + BC7, + DXT1Crunched = 28, + DXT5Crunched, + PVRTC_RGB2, + PVRTC_RGBA2, + PVRTC_RGB4, + PVRTC_RGBA4, + ETC_RGB4, + ATC_RGB4, + ATC_RGBA8, + EAC_R = 41, + EAC_R_SIGNED, + EAC_RG, + EAC_RG_SIGNED, + ETC2_RGB, + ETC2_RGBA1, + ETC2_RGBA8, + ASTC_RGB_4x4, + ASTC_RGB_5x5, + ASTC_RGB_6x6, + ASTC_RGB_8x8, + ASTC_RGB_10x10, + ASTC_RGB_12x12, + ASTC_RGBA_4x4, + ASTC_RGBA_5x5, + ASTC_RGBA_6x6, + ASTC_RGBA_8x8, + ASTC_RGBA_10x10, + ASTC_RGBA_12x12, + ETC_RGB4_3DS, + ETC_RGBA8_3DS, + RG16, + R8, + ETC_RGB4Crunched, + ETC2_RGBA8Crunched, + } +} \ No newline at end of file diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Classes/Transform.cs b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Transform.cs new file mode 100644 index 0000000..5674802 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Classes/Transform.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +namespace AssetStudioCore.Classes +{ + public class Transform : Component + { + public float[] m_LocalRotation; + public float[] m_LocalPosition; + public float[] m_LocalScale; + public List m_Children; + public PPtr m_Father; + + public Transform(AssetPreloadData preloadData) : base(preloadData) + { + m_LocalRotation = new[] { reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() }; + m_LocalPosition = new[] { reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() }; + m_LocalScale = new[] { reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() }; + int m_ChildrenCount = reader.ReadInt32(); + m_Children = new List(m_ChildrenCount); + for (int j = 0; j < m_ChildrenCount; j++) + { + m_Children.Add(sourceFile.ReadPPtr()); + } + m_Father = sourceFile.ReadPPtr(); + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/EndianBinaryReader.cs b/AzurLaneLive2DExtract/AssetStudioCore/EndianBinaryReader.cs new file mode 100644 index 0000000..d978122 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/EndianBinaryReader.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace AssetStudioCore +{ + 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(); + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Lz4DecoderStream.cs b/AzurLaneLive2DExtract/AssetStudioCore/Lz4DecoderStream.cs new file mode 100644 index 0000000..1ff219e --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Lz4DecoderStream.cs @@ -0,0 +1,539 @@ +#define CHECK_ARGS +#define CHECK_EOF +//#define LOCAL_SHADOW + +using System; +using System.IO; + +namespace Lz4 +{ + public class Lz4DecoderStream : Stream + { + public Lz4DecoderStream(Stream input, long inputLength = long.MaxValue) + { + Reset(input, inputLength); + } + + public void Reset(Stream input, long inputLength = long.MaxValue) + { + this.inputLength = inputLength; + this.input = input; + + phase = DecodePhase.ReadToken; + + decodeBufferPos = 0; + + litLen = 0; + matLen = 0; + matDst = 0; + + inBufPos = DecBufLen; + inBufEnd = DecBufLen; + } + + protected override void Dispose(bool disposing) + { + try + { + if (disposing && input != null) + { + input.Close(); + } + input = null; + } + finally + { + base.Dispose(disposing); + } + } + + private long inputLength; + private Stream input; + + //because we might not be able to match back across invocations, + //we have to keep the last window's worth of bytes around for reuse + //we use a circular buffer for this - every time we write into this + //buffer, we also write the same into our output buffer + + private const int DecBufLen = 0x10000; + private const int DecBufMask = 0xFFFF; + + private const int InBufLen = 128; + + private byte[] decodeBuffer = new byte[DecBufLen + InBufLen]; + private int decodeBufferPos, inBufPos, inBufEnd; + + //we keep track of which phase we're in so that we can jump right back + //into the correct part of decoding + + private DecodePhase phase; + + private enum DecodePhase + { + ReadToken, + ReadExLiteralLength, + CopyLiteral, + ReadOffset, + ReadExMatchLength, + CopyMatch, + } + + //state within interruptable phases and across phase boundaries is + //kept here - again, so that we can punt out and restart freely + + private int litLen, matLen, matDst; + + public override int Read(byte[] buffer, int offset, int count) + { +#if CHECK_ARGS + if (buffer == null) + throw new ArgumentNullException("buffer"); + if (offset < 0 || count < 0 || buffer.Length - count < offset) + throw new ArgumentOutOfRangeException(); + + if (input == null) + throw new InvalidOperationException(); +#endif + int nRead, nToRead = count; + + var decBuf = decodeBuffer; + + //the stringy gotos are obnoxious, but their purpose is to + //make it *blindingly* obvious how the state machine transitions + //back and forth as it reads - remember, we can yield out of + //this routine in several places, and we must be able to re-enter + //and pick up where we left off! + +#if LOCAL_SHADOW + var phase = this.phase; + var inBufPos = this.inBufPos; + var inBufEnd = this.inBufEnd; +#endif + switch (phase) + { + case DecodePhase.ReadToken: + goto readToken; + + case DecodePhase.ReadExLiteralLength: + goto readExLiteralLength; + + case DecodePhase.CopyLiteral: + goto copyLiteral; + + case DecodePhase.ReadOffset: + goto readOffset; + + case DecodePhase.ReadExMatchLength: + goto readExMatchLength; + + case DecodePhase.CopyMatch: + goto copyMatch; + } + + readToken: + int tok; + if (inBufPos < inBufEnd) + { + tok = decBuf[inBufPos++]; + } + else + { +#if LOCAL_SHADOW + this.inBufPos = inBufPos; +#endif + + tok = ReadByteCore(); +#if LOCAL_SHADOW + inBufPos = this.inBufPos; + inBufEnd = this.inBufEnd; +#endif +#if CHECK_EOF + if (tok == -1) + goto finish; +#endif + } + + litLen = tok >> 4; + matLen = (tok & 0xF) + 4; + + switch (litLen) + { + case 0: + phase = DecodePhase.ReadOffset; + goto readOffset; + + case 0xF: + phase = DecodePhase.ReadExLiteralLength; + goto readExLiteralLength; + + default: + phase = DecodePhase.CopyLiteral; + goto copyLiteral; + } + + readExLiteralLength: + int exLitLen; + if (inBufPos < inBufEnd) + { + exLitLen = decBuf[inBufPos++]; + } + else + { +#if LOCAL_SHADOW + this.inBufPos = inBufPos; +#endif + exLitLen = ReadByteCore(); +#if LOCAL_SHADOW + inBufPos = this.inBufPos; + inBufEnd = this.inBufEnd; +#endif + +#if CHECK_EOF + if (exLitLen == -1) + goto finish; +#endif + } + + litLen += exLitLen; + if (exLitLen == 255) + goto readExLiteralLength; + + phase = DecodePhase.CopyLiteral; + goto copyLiteral; + + copyLiteral: + int nReadLit = litLen < nToRead ? litLen : nToRead; + if (nReadLit != 0) + { + if (inBufPos + nReadLit <= inBufEnd) + { + int ofs = offset; + + for (int c = nReadLit; c-- != 0;) + buffer[ofs++] = decBuf[inBufPos++]; + + nRead = nReadLit; + } + else + { +#if LOCAL_SHADOW + this.inBufPos = inBufPos; +#endif + nRead = ReadCore(buffer, offset, nReadLit); +#if LOCAL_SHADOW + inBufPos = this.inBufPos; + inBufEnd = this.inBufEnd; +#endif +#if CHECK_EOF + if (nRead == 0) + goto finish; +#endif + } + + offset += nRead; + nToRead -= nRead; + + litLen -= nRead; + + if (litLen != 0) + goto copyLiteral; + } + + if (nToRead == 0) + goto finish; + + phase = DecodePhase.ReadOffset; + goto readOffset; + + readOffset: + if (inBufPos + 1 < inBufEnd) + { + matDst = (decBuf[inBufPos + 1] << 8) | decBuf[inBufPos]; + inBufPos += 2; + } + else + { +#if LOCAL_SHADOW + this.inBufPos = inBufPos; +#endif + matDst = ReadOffsetCore(); +#if LOCAL_SHADOW + inBufPos = this.inBufPos; + inBufEnd = this.inBufEnd; +#endif +#if CHECK_EOF + if (matDst == -1) + goto finish; +#endif + } + + if (matLen == 15 + 4) + { + phase = DecodePhase.ReadExMatchLength; + goto readExMatchLength; + } + else + { + phase = DecodePhase.CopyMatch; + goto copyMatch; + } + + readExMatchLength: + int exMatLen; + if (inBufPos < inBufEnd) + { + exMatLen = decBuf[inBufPos++]; + } + else + { +#if LOCAL_SHADOW + this.inBufPos = inBufPos; +#endif + exMatLen = ReadByteCore(); +#if LOCAL_SHADOW + inBufPos = this.inBufPos; + inBufEnd = this.inBufEnd; +#endif +#if CHECK_EOF + if (exMatLen == -1) + goto finish; +#endif + } + + matLen += exMatLen; + if (exMatLen == 255) + goto readExMatchLength; + + phase = DecodePhase.CopyMatch; + goto copyMatch; + + copyMatch: + int nCpyMat = matLen < nToRead ? matLen : nToRead; + if (nCpyMat != 0) + { + nRead = count - nToRead; + + int bufDst = matDst - nRead; + if (bufDst > 0) + { + //offset is fairly far back, we need to pull from the buffer + + int bufSrc = decodeBufferPos - bufDst; + if (bufSrc < 0) + bufSrc += DecBufLen; + int bufCnt = bufDst < nCpyMat ? bufDst : nCpyMat; + + for (int c = bufCnt; c-- != 0;) + buffer[offset++] = decBuf[bufSrc++ & DecBufMask]; + } + else + { + bufDst = 0; + } + + int sOfs = offset - matDst; + for (int i = bufDst; i < nCpyMat; i++) + buffer[offset++] = buffer[sOfs++]; + + nToRead -= nCpyMat; + matLen -= nCpyMat; + } + + if (nToRead == 0) + goto finish; + + phase = DecodePhase.ReadToken; + goto readToken; + + finish: + nRead = count - nToRead; + + int nToBuf = nRead < DecBufLen ? nRead : DecBufLen; + int repPos = offset - nToBuf; + + if (nToBuf == DecBufLen) + { + Buffer.BlockCopy(buffer, repPos, decBuf, 0, DecBufLen); + decodeBufferPos = 0; + } + else + { + int decPos = decodeBufferPos; + + while (nToBuf-- != 0) + decBuf[decPos++ & DecBufMask] = buffer[repPos++]; + + decodeBufferPos = decPos & DecBufMask; + } + +#if LOCAL_SHADOW + this.phase = phase; + this.inBufPos = inBufPos; +#endif + return nRead; + } + + private int ReadByteCore() + { + var buf = decodeBuffer; + + if (inBufPos == inBufEnd) + { + int nRead = input.Read(buf, DecBufLen, + InBufLen < inputLength ? InBufLen : (int)inputLength); + +#if CHECK_EOF + if (nRead == 0) + return -1; +#endif + + inputLength -= nRead; + + inBufPos = DecBufLen; + inBufEnd = DecBufLen + nRead; + } + + return buf[inBufPos++]; + } + + private int ReadOffsetCore() + { + var buf = decodeBuffer; + + if (inBufPos == inBufEnd) + { + int nRead = input.Read(buf, DecBufLen, + InBufLen < inputLength ? InBufLen : (int)inputLength); + +#if CHECK_EOF + if (nRead == 0) + return -1; +#endif + + inputLength -= nRead; + + inBufPos = DecBufLen; + inBufEnd = DecBufLen + nRead; + } + + if (inBufEnd - inBufPos == 1) + { + buf[DecBufLen] = buf[inBufPos]; + + int nRead = input.Read(buf, DecBufLen + 1, + InBufLen - 1 < inputLength ? InBufLen - 1 : (int)inputLength); + +#if CHECK_EOF + if (nRead == 0) + { + inBufPos = DecBufLen; + inBufEnd = DecBufLen + 1; + + return -1; + } +#endif + + inputLength -= nRead; + + inBufPos = DecBufLen; + inBufEnd = DecBufLen + nRead + 1; + } + + int ret = (buf[inBufPos + 1] << 8) | buf[inBufPos]; + inBufPos += 2; + + return ret; + } + + private int ReadCore(byte[] buffer, int offset, int count) + { + int nToRead = count; + + var buf = decodeBuffer; + int inBufLen = inBufEnd - inBufPos; + + int fromBuf = nToRead < inBufLen ? nToRead : inBufLen; + if (fromBuf != 0) + { + var bufPos = inBufPos; + + for (int c = fromBuf; c-- != 0;) + buffer[offset++] = buf[bufPos++]; + + inBufPos = bufPos; + nToRead -= fromBuf; + } + + if (nToRead != 0) + { + int nRead; + + if (nToRead >= InBufLen) + { + nRead = input.Read(buffer, offset, + nToRead < inputLength ? nToRead : (int)inputLength); + nToRead -= nRead; + } + else + { + nRead = input.Read(buf, DecBufLen, + InBufLen < inputLength ? InBufLen : (int)inputLength); + + inBufPos = DecBufLen; + inBufEnd = DecBufLen + nRead; + + fromBuf = nToRead < nRead ? nToRead : nRead; + + var bufPos = inBufPos; + + for (int c = fromBuf; c-- != 0;) + buffer[offset++] = buf[bufPos++]; + + inBufPos = bufPos; + nToRead -= fromBuf; + } + + inputLength -= nRead; + } + + return count - nToRead; + } + + #region Stream internals + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override void Flush() + { + } + + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + #endregion + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/PPtrHelpers.cs b/AzurLaneLive2DExtract/AssetStudioCore/PPtrHelpers.cs new file mode 100644 index 0000000..ef2785c --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/PPtrHelpers.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudioCore +{ + public class PPtr + { + public AssetsFile sourceFile; + public int m_FileID; + public long m_PathID; + + public AssetPreloadData Get() + { + return sourceFile.preloadTable[m_PathID]; + } + + public bool TryGet(out AssetPreloadData assetPreloadData ) + { + return sourceFile.preloadTable.TryGetValue(m_PathID, out assetPreloadData); + } + } + + public static class PPtrHelpers + { + public static PPtr ReadPPtr(this AssetsFile sourceFile) + { + var result = new PPtr(); + result.sourceFile = sourceFile; + var reader = sourceFile.reader; + result.m_FileID = reader.ReadInt32(); + result.m_PathID = sourceFile.fileGen < 14 ? reader.ReadInt32() : reader.ReadInt64(); + return result; + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/SevenZipHelper.cs b/AzurLaneLive2DExtract/AssetStudioCore/SevenZipHelper.cs new file mode 100644 index 0000000..8c083b4 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/SevenZipHelper.cs @@ -0,0 +1,48 @@ +using System; +using System.IO; +using SevenZip.Compression.LZMA; + + +namespace AssetStudioCore +{ + public static class SevenZipHelper + { + public static MemoryStream StreamDecompress(MemoryStream inStream) + { + var decoder = new Decoder(); + + inStream.Seek(0, 0); + var newOutStream = new MemoryStream(); + + var properties = new byte[5]; + if (inStream.Read(properties, 0, 5) != 5) + throw (new Exception("input .lzma is too short")); + long outSize = 0; + for (var i = 0; i < 8; i++) + { + var v = inStream.ReadByte(); + if (v < 0) + throw (new Exception("Can't Read 1")); + outSize |= ((long)(byte)v) << (8 * i); + } + decoder.SetDecoderProperties(properties); + + var compressedSize = inStream.Length - inStream.Position; + decoder.Code(inStream, newOutStream, compressedSize, outSize, null); + + newOutStream.Position = 0; + return newOutStream; + } + + public static void StreamDecompress(Stream inStream, Stream outStream, long inSize, long outSize) + { + var decoder = new Decoder(); + var properties = new byte[5]; + if (inStream.Read(properties, 0, 5) != 5) + throw new Exception("input .lzma is too short"); + decoder.SetDecoderProperties(properties); + inSize -= 5L; + decoder.Code(inStream, outStream, inSize, outSize, null); + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/StreamExtensions.cs b/AzurLaneLive2DExtract/AssetStudioCore/StreamExtensions.cs new file mode 100644 index 0000000..b3a60af --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/StreamExtensions.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace AssetStudioCore +{ + public static class StreamExtensions + { + private const int BufferSize = 81920; + + public static void CopyTo(this Stream source, Stream destination, long size) + { + var buffer = new byte[BufferSize]; + for (var left = size; left > 0; left -= BufferSize) + { + int toRead = BufferSize < left ? BufferSize : (int)left; + int read = source.Read(buffer, 0, toRead); + destination.Write(buffer, 0, read); + if (read != toRead) + { + return; + } + } + } + } +} diff --git a/AzurLaneLive2DExtract/AssetStudioCore/Texture2DConverter.cs b/AzurLaneLive2DExtract/AssetStudioCore/Texture2DConverter.cs new file mode 100644 index 0000000..8163a02 --- /dev/null +++ b/AzurLaneLive2DExtract/AssetStudioCore/Texture2DConverter.cs @@ -0,0 +1,1175 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using AssetStudioCore.Classes; + +namespace AssetStudioCore +{ + class Texture2DConverter + { + //Texture2D + private int m_Width; + private int m_Height; + private TextureFormat m_TextureFormat; + private int image_data_size; + public byte[] image_data; + private int[] version; + + //DDS Start + private byte[] dwMagic = { 0x44, 0x44, 0x53, 0x20, 0x7c }; + private int dwFlags = 0x1 + 0x2 + 0x4 + 0x1000; + //public int dwHeight; m_Height + //public int dwWidth; m_Width + private int dwPitchOrLinearSize; + private int dwMipMapCount = 0x1; + private int dwSize = 0x20; + private int dwFlags2; + private int dwFourCC; + private int dwRGBBitCount; + private int dwRBitMask; + private int dwGBitMask; + private int dwBBitMask; + private int dwABitMask; + private int dwCaps = 0x1000; + private int dwCaps2 = 0x0; + //DDS End + //PVR Start + private int pvrVersion = 0x03525650; + private int pvrFlags = 0x0; + private long pvrPixelFormat; + private int pvrColourSpace = 0x0; + private int pvrChannelType = 0x0; + //public int pvrHeight; m_Height + //public int pvrWidth; m_Width + private int pvrDepth = 0x1; + private int pvrNumSurfaces = 0x1; //For texture arrays + private int pvrNumFaces = 0x1; //For cube maps + //public int pvrMIPMapCount; dwMipMapCount + private int pvrMetaDataSize = 0x0; + //PVR End + //KTX Start + private int glType = 0; + private int glTypeSize = 1; + private int glFormat = 0; + private int glInternalFormat; + private int glBaseInternalFormat; + //public int pixelWidth; m_Width + //public int pixelHeight; m_Height + private int pixelDepth = 0; + private int numberOfArrayElements = 0; + private int numberOfFaces = 1; + private int numberOfMipmapLevels = 1; + private int bytesOfKeyValueData = 0; + //KTX End + //TextureConverter + private QFORMAT q_format; + //texgenpack + private texgenpack_texturetype texturetype; + + [DllImport("PVRTexLibWrapper.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern bool DecompressPVR(byte[] buffer, IntPtr bmp, int len); + + [DllImport("TextureConverterWrapper.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern bool Ponvert(byte[] buffer, IntPtr bmp, int nWidth, int nHeight, int len, int type, int bmpsize, bool fixAlpha); + + [DllImport("crunch.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern bool DecompressCRN(byte[] pSrc_file_data, int src_file_size, out IntPtr uncompressedData, out int uncompressedSize); + + [DllImport("crunchunity.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern bool DecompressUnityCRN(byte[] pSrc_file_data, int src_file_size, out IntPtr uncompressedData, out int uncompressedSize); + + [DllImport("texgenpack.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern void Decode(int texturetype, byte[] texturedata, int width, int height, IntPtr bmp); + + + public Texture2DConverter(Texture2D m_Texture2D) + { + image_data_size = m_Texture2D.image_data_size; + image_data = m_Texture2D.image_data; + m_Width = m_Texture2D.m_Width; + m_Height = m_Texture2D.m_Height; + m_TextureFormat = m_Texture2D.m_TextureFormat; + var mMipMap = m_Texture2D.m_MipMap; + version = m_Texture2D.sourceFile.version; + var platform = m_Texture2D.sourceFile.platform; + + if (version[0] < 5 || (version[0] == 5 && version[1] < 2))//5.2 down + { + if (mMipMap) + { + dwFlags += 0x20000; + dwMipMapCount = Convert.ToInt32(Math.Log(Math.Max(m_Width, m_Height)) / Math.Log(2)); + dwCaps += 0x400008; + } + } + else + { + dwFlags += 0x20000; + dwMipMapCount = m_Texture2D.m_MipCount; + dwCaps += 0x400008; + } + + + switch (m_TextureFormat) + { + //TODO 导出到DDS容器时应该用原像素还是转换以后的像素? + case TextureFormat.Alpha8: //test pass + { + /*dwFlags2 = 0x2; + dwRGBBitCount = 0x8; + dwRBitMask = 0x0; + dwGBitMask = 0x0; + dwBBitMask = 0x0; + dwABitMask = 0xFF; */ + + //转BGRA32 + var BGRA32 = Enumerable.Repeat(0xFF, image_data_size * 4).ToArray(); + for (var i = 0; i < image_data_size; i++) + { + BGRA32[i * 4 + 3] = image_data[i]; + } + SetBGRA32Info(BGRA32); + break; + } + case TextureFormat.ARGB4444: //test pass + { + SwapBytesForXbox(platform); + + /*dwFlags2 = 0x41; + dwRGBBitCount = 0x10; + dwRBitMask = 0xF00; + dwGBitMask = 0xF0; + dwBBitMask = 0xF; + dwABitMask = 0xF000;*/ + + //转BGRA32 + var BGRA32 = new byte[image_data_size * 2]; + for (var i = 0; i < image_data_size / 2; i++) + { + var pixelNew = new byte[4]; + var pixelOldShort = BitConverter.ToUInt16(image_data, i * 2); + pixelNew[0] = (byte)(pixelOldShort & 0x000f); + pixelNew[1] = (byte)((pixelOldShort & 0x00f0) >> 4); + pixelNew[2] = (byte)((pixelOldShort & 0x0f00) >> 8); + pixelNew[3] = (byte)((pixelOldShort & 0xf000) >> 12); + // convert range + for (var j = 0; j < 4; j++) + pixelNew[j] = (byte)((pixelNew[j] << 4) | pixelNew[j]); + pixelNew.CopyTo(BGRA32, i * 4); + } + SetBGRA32Info(BGRA32); + break; + } + case TextureFormat.RGB24: //test pass + { + /*dwFlags2 = 0x40; + dwRGBBitCount = 0x18; + dwRBitMask = 0xFF; + dwGBitMask = 0xFF00; + dwBBitMask = 0xFF0000; + dwABitMask = 0x0;*/ + + //转BGRA32 + var BGRA32 = new byte[image_data_size / 3 * 4]; + for (var i = 0; i < image_data_size / 3; i++) + { + BGRA32[i * 4] = image_data[i * 3 + 2]; + BGRA32[i * 4 + 1] = image_data[i * 3 + 1]; + BGRA32[i * 4 + 2] = image_data[i * 3 + 0]; + BGRA32[i * 4 + 3] = 255; + } + SetBGRA32Info(BGRA32); + break; + } + case TextureFormat.RGBA32: //test pass + { + /*dwFlags2 = 0x41; + dwRGBBitCount = 0x20; + dwRBitMask = 0xFF; + dwGBitMask = 0xFF00; + dwBBitMask = 0xFF0000; + dwABitMask = -16777216;*/ + + //转BGRA32 + var BGRA32 = new byte[image_data_size]; + for (var i = 0; i < image_data_size; i += 4) + { + BGRA32[i] = image_data[i + 2]; + BGRA32[i + 1] = image_data[i + 1]; + BGRA32[i + 2] = image_data[i + 0]; + BGRA32[i + 3] = image_data[i + 3]; + } + SetBGRA32Info(BGRA32); + break; + } + case TextureFormat.ARGB32://test pass + { + /*dwFlags2 = 0x41; + dwRGBBitCount = 0x20; + dwRBitMask = 0xFF00; + dwGBitMask = 0xFF0000; + dwBBitMask = -16777216; + dwABitMask = 0xFF;*/ + + //转BGRA32 + var BGRA32 = new byte[image_data_size]; + for (var i = 0; i < image_data_size; i += 4) + { + BGRA32[i] = image_data[i + 3]; + BGRA32[i + 1] = image_data[i + 2]; + BGRA32[i + 2] = image_data[i + 1]; + BGRA32[i + 3] = image_data[i + 0]; + } + SetBGRA32Info(BGRA32); + break; + } + case TextureFormat.RGB565: //test pass + { + SwapBytesForXbox(platform); + + dwFlags2 = 0x40; + dwRGBBitCount = 0x10; + dwRBitMask = 0xF800; + dwGBitMask = 0x7E0; + dwBBitMask = 0x1F; + dwABitMask = 0x0; + break; + } + /*case TextureFormat.R16: //test pass + { + //转BGRA32 + var BGRA32 = new byte[image_data_size * 2]; + for (var i = 0; i < image_data_size; i += 2) + { + float f = Half.ToHalf(image_data, i); + BGRA32[i * 2 + 2] = (byte)Math.Ceiling(f * 255);//R + BGRA32[i * 2 + 3] = 255;//A + } + SetBGRA32Info(BGRA32); + break; + }*/ + case TextureFormat.DXT1: //test pass + case TextureFormat.DXT1Crunched: //test pass + { + SwapBytesForXbox(platform); + + if (mMipMap) + { + dwPitchOrLinearSize = m_Height * m_Width / 2; + } + dwFlags2 = 0x4; + dwFourCC = 0x31545844; + dwRGBBitCount = 0x0; + dwRBitMask = 0x0; + dwGBitMask = 0x0; + dwBBitMask = 0x0; + dwABitMask = 0x0; + + q_format = QFORMAT.Q_FORMAT_S3TC_DXT1_RGB; + break; + } + case TextureFormat.DXT5: //test pass + case TextureFormat.DXT5Crunched: //test pass + { + SwapBytesForXbox(platform); + + if (mMipMap) + { + dwPitchOrLinearSize = m_Height * m_Width / 2; + } + dwFlags2 = 0x4; + dwFourCC = 0x35545844; + dwRGBBitCount = 0x0; + dwRBitMask = 0x0; + dwGBitMask = 0x0; + dwBBitMask = 0x0; + dwABitMask = 0x0; + + q_format = QFORMAT.Q_FORMAT_S3TC_DXT5_RGBA; + break; + } + case TextureFormat.RGBA4444: //test pass + { + /*dwFlags2 = 0x41; + dwRGBBitCount = 0x10; + dwRBitMask = 0xF000; + dwGBitMask = 0xF00; + dwBBitMask = 0xF0; + dwABitMask = 0xF;*/ + + //转BGRA32 + var BGRA32 = new byte[image_data_size * 2]; + for (var i = 0; i < image_data_size / 2; i++) + { + var pixelNew = new byte[4]; + var pixelOldShort = BitConverter.ToUInt16(image_data, i * 2); + pixelNew[0] = (byte)((pixelOldShort & 0x00f0) >> 4); + pixelNew[1] = (byte)((pixelOldShort & 0x0f00) >> 8); + pixelNew[2] = (byte)((pixelOldShort & 0xf000) >> 12); + pixelNew[3] = (byte)(pixelOldShort & 0x000f); + // convert range + for (var j = 0; j < 4; j++) + pixelNew[j] = (byte)((pixelNew[j] << 4) | pixelNew[j]); + pixelNew.CopyTo(BGRA32, i * 4); + } + SetBGRA32Info(BGRA32); + break; + } + case TextureFormat.BGRA32: //test pass + { + dwFlags2 = 0x41; + dwRGBBitCount = 0x20; + dwRBitMask = 0xFF0000; + dwGBitMask = 0xFF00; + dwBBitMask = 0xFF; + dwABitMask = -16777216; + break; + } + case TextureFormat.RHalf: //test pass + { + q_format = QFORMAT.Q_FORMAT_R_16F; + glInternalFormat = KTXHeader.GL_R16F; + glBaseInternalFormat = KTXHeader.GL_RED; + break; + } + case TextureFormat.RGHalf: //test pass + { + q_format = QFORMAT.Q_FORMAT_RG_HF; + glInternalFormat = KTXHeader.GL_RG16F; + glBaseInternalFormat = KTXHeader.GL_RG; + break; + } + case TextureFormat.RGBAHalf: //test pass + { + q_format = QFORMAT.Q_FORMAT_RGBA_HF; + glInternalFormat = KTXHeader.GL_RGBA16F; + glBaseInternalFormat = KTXHeader.GL_RGBA; + break; + } + case TextureFormat.RFloat: //test pass + { + q_format = QFORMAT.Q_FORMAT_R_F; + glInternalFormat = KTXHeader.GL_R32F; + glBaseInternalFormat = KTXHeader.GL_RED; + break; + } + case TextureFormat.RGFloat: //test pass + { + q_format = QFORMAT.Q_FORMAT_RG_F; + glInternalFormat = KTXHeader.GL_RG32F; + glBaseInternalFormat = KTXHeader.GL_RG; + break; + } + case TextureFormat.RGBAFloat: //test pass + { + q_format = QFORMAT.Q_FORMAT_RGBA_F; + glInternalFormat = KTXHeader.GL_RGBA32F; + glBaseInternalFormat = KTXHeader.GL_RGBA; + break; + } + case TextureFormat.YUY2: //test pass + { + pvrPixelFormat = 17; + break; + } + case TextureFormat.RGB9e5Float: //TODO Test failure + { + q_format = QFORMAT.Q_FORMAT_RGB9_E5; + break; + } + case TextureFormat.BC4: //test pass + { + texturetype = texgenpack_texturetype.RGTC1; + glInternalFormat = KTXHeader.GL_COMPRESSED_RED_RGTC1; + glBaseInternalFormat = KTXHeader.GL_RED; + break; + } + case TextureFormat.BC5: //test pass + { + texturetype = texgenpack_texturetype.RGTC2; + glInternalFormat = KTXHeader.GL_COMPRESSED_RG_RGTC2; + glBaseInternalFormat = KTXHeader.GL_RG; + break; + } + case TextureFormat.BC6H: //test pass + { + texturetype = texgenpack_texturetype.BPTC_FLOAT; + glInternalFormat = KTXHeader.GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT; + glBaseInternalFormat = KTXHeader.GL_RGB; + break; + } + case TextureFormat.BC7: //test pass + { + texturetype = texgenpack_texturetype.BPTC; + glInternalFormat = KTXHeader.GL_COMPRESSED_RGBA_BPTC_UNORM; + glBaseInternalFormat = KTXHeader.GL_RGBA; + break; + } + case TextureFormat.PVRTC_RGB2: //test pass + { + pvrPixelFormat = 0; + glInternalFormat = KTXHeader.GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + glBaseInternalFormat = KTXHeader.GL_RGB; + break; + } + case TextureFormat.PVRTC_RGBA2: //test pass + { + pvrPixelFormat = 1; + glInternalFormat = KTXHeader.GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + glBaseInternalFormat = KTXHeader.GL_RGBA; + break; + } + case TextureFormat.PVRTC_RGB4: //test pass + { + pvrPixelFormat = 2; + glInternalFormat = KTXHeader.GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + glBaseInternalFormat = KTXHeader.GL_RGB; + break; + } + case TextureFormat.PVRTC_RGBA4: //test pass + { + pvrPixelFormat = 3; + glInternalFormat = KTXHeader.GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + glBaseInternalFormat = KTXHeader.GL_RGBA; + break; + } + case TextureFormat.ETC_RGB4Crunched: //test pass + case TextureFormat.ETC_RGB4_3DS: //test pass + case TextureFormat.ETC_RGB4: //test pass + { + pvrPixelFormat = 6; + glInternalFormat = KTXHeader.GL_ETC1_RGB8_OES; + glBaseInternalFormat = KTXHeader.GL_RGB; + break; + } + case TextureFormat.ATC_RGB4: //test pass + { + q_format = QFORMAT.Q_FORMAT_ATITC_RGB; + glInternalFormat = KTXHeader.GL_ATC_RGB_AMD; + glBaseInternalFormat = KTXHeader.GL_RGB; + break; + } + case TextureFormat.ATC_RGBA8: //test pass + { + q_format = QFORMAT.Q_FORMAT_ATC_RGBA_INTERPOLATED_ALPHA; + glInternalFormat = KTXHeader.GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD; + glBaseInternalFormat = KTXHeader.GL_RGBA; + break; + } + case TextureFormat.EAC_R: //test pass + { + q_format = QFORMAT.Q_FORMAT_EAC_R_UNSIGNED; + glInternalFormat = KTXHeader.GL_COMPRESSED_R11_EAC; + glBaseInternalFormat = KTXHeader.GL_RED; + break; + } + case TextureFormat.EAC_R_SIGNED: //test pass + { + q_format = QFORMAT.Q_FORMAT_EAC_R_SIGNED; + glInternalFormat = KTXHeader.GL_COMPRESSED_SIGNED_R11_EAC; + glBaseInternalFormat = KTXHeader.GL_RED; + break; + } + case TextureFormat.EAC_RG: //test pass + { + q_format = QFORMAT.Q_FORMAT_EAC_RG_UNSIGNED; + glInternalFormat = KTXHeader.GL_COMPRESSED_RG11_EAC; + glBaseInternalFormat = KTXHeader.GL_RG; + break; + } + case TextureFormat.EAC_RG_SIGNED: //test pass + { + q_format = QFORMAT.Q_FORMAT_EAC_RG_SIGNED; + glInternalFormat = KTXHeader.GL_COMPRESSED_SIGNED_RG11_EAC; + glBaseInternalFormat = KTXHeader.GL_RG; + break; + } + case TextureFormat.ETC2_RGB: //test pass + { + pvrPixelFormat = 22; + glInternalFormat = KTXHeader.GL_COMPRESSED_RGB8_ETC2; + glBaseInternalFormat = KTXHeader.GL_RGB; + break; + } + case TextureFormat.ETC2_RGBA1: //test pass + { + pvrPixelFormat = 24; + glInternalFormat = KTXHeader.GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + glBaseInternalFormat = KTXHeader.GL_RGBA; + break; + } + case TextureFormat.ETC2_RGBA8Crunched: //test pass + case TextureFormat.ETC_RGBA8_3DS: //test pass + case TextureFormat.ETC2_RGBA8: //test pass + { + pvrPixelFormat = 23; + glInternalFormat = KTXHeader.GL_COMPRESSED_RGBA8_ETC2_EAC; + glBaseInternalFormat = KTXHeader.GL_RGBA; + break; + } + case TextureFormat.ASTC_RGB_4x4: //test pass + case TextureFormat.ASTC_RGBA_4x4: //test pass + { + pvrPixelFormat = 27; + break; + } + case TextureFormat.ASTC_RGB_5x5: //test pass + case TextureFormat.ASTC_RGBA_5x5: //test pass + { + pvrPixelFormat = 29; + break; + } + case TextureFormat.ASTC_RGB_6x6: //test pass + case TextureFormat.ASTC_RGBA_6x6: //test pass + { + pvrPixelFormat = 31; + break; + } + case TextureFormat.ASTC_RGB_8x8: //test pass + case TextureFormat.ASTC_RGBA_8x8: //test pass + { + pvrPixelFormat = 34; + break; + } + case TextureFormat.ASTC_RGB_10x10: //test pass + case TextureFormat.ASTC_RGBA_10x10: //test pass + { + pvrPixelFormat = 38; + break; + } + case TextureFormat.ASTC_RGB_12x12: //test pass + case TextureFormat.ASTC_RGBA_12x12: //test pass + { + pvrPixelFormat = 40; + break; + } + case TextureFormat.RG16: //test pass + { + //转BGRA32 + var BGRA32 = new byte[image_data_size * 2]; + for (var i = 0; i < image_data_size; i += 2) + { + BGRA32[i * 2 + 1] = image_data[i + 1];//G + BGRA32[i * 2 + 2] = image_data[i];//R + BGRA32[i * 2 + 3] = 255;//A + } + SetBGRA32Info(BGRA32); + break; + } + case TextureFormat.R8: //test pass + { + //转BGRA32 + var BGRA32 = new byte[image_data_size * 4]; + for (var i = 0; i < image_data_size; i++) + { + BGRA32[i * 4 + 2] = image_data[i];//R + BGRA32[i * 4 + 3] = 255;//A + } + SetBGRA32Info(BGRA32); + break; + } + } + } + + private void SetBGRA32Info(byte[] BGRA32) + { + image_data = BGRA32; + image_data_size = BGRA32.Length; + dwFlags2 = 0x41; + dwRGBBitCount = 0x20; + dwRBitMask = 0xFF0000; + dwGBitMask = 0xFF00; + dwBBitMask = 0xFF; + dwABitMask = -16777216; + } + + private void SwapBytesForXbox(int platform) + { + if (platform == 11) //swap bytes for Xbox confirmed, PS3 not encountered + { + for (var i = 0; i < image_data_size / 2; i++) + { + var b0 = image_data[i * 2]; + image_data[i * 2] = image_data[i * 2 + 1]; + image_data[i * 2 + 1] = b0; + } + } + } + + public string GetExtensionName() + { + switch (m_TextureFormat) + { + case TextureFormat.Alpha8: + case TextureFormat.ARGB4444: + case TextureFormat.RGB24: + case TextureFormat.RGBA32: + case TextureFormat.ARGB32: + case TextureFormat.RGB565: + case TextureFormat.R16: + case TextureFormat.DXT1: + case TextureFormat.DXT5: + case TextureFormat.RGBA4444: + case TextureFormat.BGRA32: + case TextureFormat.RG16: + case TextureFormat.R8: + return ".dds"; + case TextureFormat.DXT1Crunched: + case TextureFormat.DXT5Crunched: + case TextureFormat.ETC_RGB4Crunched: + case TextureFormat.ETC2_RGBA8Crunched: + return ".crn"; + case TextureFormat.YUY2: + case TextureFormat.PVRTC_RGB2: + case TextureFormat.PVRTC_RGBA2: + case TextureFormat.PVRTC_RGB4: + case TextureFormat.PVRTC_RGBA4: + case TextureFormat.ETC_RGB4: + case TextureFormat.ETC2_RGB: + case TextureFormat.ETC2_RGBA1: + case TextureFormat.ETC2_RGBA8: + case TextureFormat.ASTC_RGB_4x4: + case TextureFormat.ASTC_RGB_5x5: + case TextureFormat.ASTC_RGB_6x6: + case TextureFormat.ASTC_RGB_8x8: + case TextureFormat.ASTC_RGB_10x10: + case TextureFormat.ASTC_RGB_12x12: + case TextureFormat.ASTC_RGBA_4x4: + case TextureFormat.ASTC_RGBA_5x5: + case TextureFormat.ASTC_RGBA_6x6: + case TextureFormat.ASTC_RGBA_8x8: + case TextureFormat.ASTC_RGBA_10x10: + case TextureFormat.ASTC_RGBA_12x12: + case TextureFormat.ETC_RGB4_3DS: + case TextureFormat.ETC_RGBA8_3DS: + return ".pvr"; + case TextureFormat.RHalf: + case TextureFormat.RGHalf: + case TextureFormat.RGBAHalf: + case TextureFormat.RFloat: + case TextureFormat.RGFloat: + case TextureFormat.RGBAFloat: + case TextureFormat.BC4: + case TextureFormat.BC5: + case TextureFormat.BC6H: + case TextureFormat.BC7: + case TextureFormat.ATC_RGB4: + case TextureFormat.ATC_RGBA8: + case TextureFormat.EAC_R: + case TextureFormat.EAC_R_SIGNED: + case TextureFormat.EAC_RG: + case TextureFormat.EAC_RG_SIGNED: + return ".ktx"; + default: + return ".tex"; + } + } + + + public byte[] ConvertToContainer() + { + if (image_data == null || image_data.Length == 0) + return null; + switch (m_TextureFormat) + { + case TextureFormat.Alpha8: + case TextureFormat.ARGB4444: + case TextureFormat.RGB24: + case TextureFormat.RGBA32: + case TextureFormat.ARGB32: + case TextureFormat.RGB565: + case TextureFormat.R16: + case TextureFormat.DXT1: + case TextureFormat.DXT5: + case TextureFormat.RGBA4444: + case TextureFormat.BGRA32: + case TextureFormat.RG16: + case TextureFormat.R8: + return ConvertToDDS(); + case TextureFormat.YUY2: + case TextureFormat.PVRTC_RGB2: + case TextureFormat.PVRTC_RGBA2: + case TextureFormat.PVRTC_RGB4: + case TextureFormat.PVRTC_RGBA4: + case TextureFormat.ETC_RGB4: + case TextureFormat.ETC2_RGB: + case TextureFormat.ETC2_RGBA1: + case TextureFormat.ETC2_RGBA8: + case TextureFormat.ASTC_RGB_4x4: + case TextureFormat.ASTC_RGB_5x5: + case TextureFormat.ASTC_RGB_6x6: + case TextureFormat.ASTC_RGB_8x8: + case TextureFormat.ASTC_RGB_10x10: + case TextureFormat.ASTC_RGB_12x12: + case TextureFormat.ASTC_RGBA_4x4: + case TextureFormat.ASTC_RGBA_5x5: + case TextureFormat.ASTC_RGBA_6x6: + case TextureFormat.ASTC_RGBA_8x8: + case TextureFormat.ASTC_RGBA_10x10: + case TextureFormat.ASTC_RGBA_12x12: + case TextureFormat.ETC_RGB4_3DS: + case TextureFormat.ETC_RGBA8_3DS: + return ConvertToPVR(); + case TextureFormat.RHalf: + case TextureFormat.RGHalf: + case TextureFormat.RGBAHalf: + case TextureFormat.RFloat: + case TextureFormat.RGFloat: + case TextureFormat.RGBAFloat: + case TextureFormat.BC4: + case TextureFormat.BC5: + case TextureFormat.BC6H: + case TextureFormat.BC7: + case TextureFormat.ATC_RGB4: + case TextureFormat.ATC_RGBA8: + case TextureFormat.EAC_R: + case TextureFormat.EAC_R_SIGNED: + case TextureFormat.EAC_RG: + case TextureFormat.EAC_RG_SIGNED: + return ConvertToKTX(); + default: + return image_data; + } + } + + private byte[] ConvertToDDS() + { + var imageBuffer = new byte[128 + image_data_size]; + dwMagic.CopyTo(imageBuffer, 0); + BitConverter.GetBytes(dwFlags).CopyTo(imageBuffer, 8); + BitConverter.GetBytes(m_Height).CopyTo(imageBuffer, 12); + BitConverter.GetBytes(m_Width).CopyTo(imageBuffer, 16); + BitConverter.GetBytes(dwPitchOrLinearSize).CopyTo(imageBuffer, 20); + BitConverter.GetBytes(dwMipMapCount).CopyTo(imageBuffer, 28); + BitConverter.GetBytes(dwSize).CopyTo(imageBuffer, 76); + BitConverter.GetBytes(dwFlags2).CopyTo(imageBuffer, 80); + BitConverter.GetBytes(dwFourCC).CopyTo(imageBuffer, 84); + BitConverter.GetBytes(dwRGBBitCount).CopyTo(imageBuffer, 88); + BitConverter.GetBytes(dwRBitMask).CopyTo(imageBuffer, 92); + BitConverter.GetBytes(dwGBitMask).CopyTo(imageBuffer, 96); + BitConverter.GetBytes(dwBBitMask).CopyTo(imageBuffer, 100); + BitConverter.GetBytes(dwABitMask).CopyTo(imageBuffer, 104); + BitConverter.GetBytes(dwCaps).CopyTo(imageBuffer, 108); + BitConverter.GetBytes(dwCaps2).CopyTo(imageBuffer, 112); + image_data.CopyTo(imageBuffer, 128); + return imageBuffer; + } + + private byte[] ConvertToPVR() + { + var mstream = new MemoryStream(); + using (var writer = new BinaryWriter(mstream)) + { + writer.Write(pvrVersion); + writer.Write(pvrFlags); + writer.Write(pvrPixelFormat); + writer.Write(pvrColourSpace); + writer.Write(pvrChannelType); + writer.Write(m_Height); + writer.Write(m_Width); + writer.Write(pvrDepth); + writer.Write(pvrNumSurfaces); + writer.Write(pvrNumFaces); + writer.Write(dwMipMapCount); + writer.Write(pvrMetaDataSize); + writer.Write(image_data); + return mstream.ToArray(); + } + } + + private byte[] ConvertToKTX() + { + var mstream = new MemoryStream(); + using (var writer = new BinaryWriter(mstream)) + { + writer.Write(KTXHeader.IDENTIFIER); + writer.Write(KTXHeader.ENDIANESS_LE); + writer.Write(glType); + writer.Write(glTypeSize); + writer.Write(glFormat); + writer.Write(glInternalFormat); + writer.Write(glBaseInternalFormat); + writer.Write(m_Width); + writer.Write(m_Height); + writer.Write(pixelDepth); + writer.Write(numberOfArrayElements); + writer.Write(numberOfFaces); + writer.Write(numberOfMipmapLevels); + writer.Write(bytesOfKeyValueData); + writer.Write(image_data_size); + writer.Write(image_data); + return mstream.ToArray(); + } + } + + public Bitmap ConvertToBitmap(bool flip) + { + if (image_data == null || image_data.Length == 0) + return null; + Bitmap bitmap; + switch (m_TextureFormat) + { + case TextureFormat.Alpha8: + case TextureFormat.ARGB4444: + case TextureFormat.RGB24: + case TextureFormat.RGBA32: + case TextureFormat.ARGB32: + case TextureFormat.R16: + case TextureFormat.RGBA4444: + case TextureFormat.BGRA32: + case TextureFormat.RG16: + case TextureFormat.R8: + bitmap = BGRA32ToBitmap(); + break; + case TextureFormat.RGB565: + bitmap = RGB565ToBitmap(); + break; + case TextureFormat.YUY2: + case TextureFormat.PVRTC_RGB2: + case TextureFormat.PVRTC_RGBA2: + case TextureFormat.PVRTC_RGB4: + case TextureFormat.PVRTC_RGBA4: + case TextureFormat.ETC_RGB4: + case TextureFormat.ETC2_RGB: + case TextureFormat.ETC2_RGBA1: + case TextureFormat.ETC2_RGBA8: + case TextureFormat.ASTC_RGB_4x4: + case TextureFormat.ASTC_RGB_5x5: + case TextureFormat.ASTC_RGB_6x6: + case TextureFormat.ASTC_RGB_8x8: + case TextureFormat.ASTC_RGB_10x10: + case TextureFormat.ASTC_RGB_12x12: + case TextureFormat.ASTC_RGBA_4x4: + case TextureFormat.ASTC_RGBA_5x5: + case TextureFormat.ASTC_RGBA_6x6: + case TextureFormat.ASTC_RGBA_8x8: + case TextureFormat.ASTC_RGBA_10x10: + case TextureFormat.ASTC_RGBA_12x12: + case TextureFormat.ETC_RGB4_3DS: + case TextureFormat.ETC_RGBA8_3DS: + bitmap = PVRToBitmap(ConvertToPVR()); + break; + case TextureFormat.DXT1: + case TextureFormat.DXT5: + case TextureFormat.RHalf: + case TextureFormat.RGHalf: + case TextureFormat.RGBAHalf: + case TextureFormat.RFloat: + case TextureFormat.RGFloat: + case TextureFormat.RGBAFloat: + case TextureFormat.RGB9e5Float: + case TextureFormat.ATC_RGB4: + case TextureFormat.ATC_RGBA8: + case TextureFormat.EAC_R: + case TextureFormat.EAC_R_SIGNED: + case TextureFormat.EAC_RG: + case TextureFormat.EAC_RG_SIGNED: + bitmap = TextureConverter(); + break; + case TextureFormat.BC4: + case TextureFormat.BC5: + case TextureFormat.BC6H: + case TextureFormat.BC7: + bitmap = Texgenpack(); + break; + case TextureFormat.DXT1Crunched: + case TextureFormat.DXT5Crunched: + DecompressCRN(); + bitmap = TextureConverter(); + break; + case TextureFormat.ETC_RGB4Crunched: + case TextureFormat.ETC2_RGBA8Crunched: + DecompressCRN(); + bitmap = PVRToBitmap(ConvertToPVR()); + break; + default: + return null; + } + if (bitmap != null && flip) + bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); + return bitmap; + } + + private Bitmap BGRA32ToBitmap() + { + var hObject = GCHandle.Alloc(image_data, GCHandleType.Pinned); + var pObject = hObject.AddrOfPinnedObject(); + var bitmap = new Bitmap(m_Width, m_Height, m_Width * 4, PixelFormat.Format32bppArgb, pObject); + hObject.Free(); + return bitmap; + } + + private Bitmap RGB565ToBitmap() + { + //stride = m_Width * 2 + m_Width * 2 % 4 + //所以m_Width * 2不为4的倍数时,需要在每行补上相应的像素 + byte[] buff; + var padding = m_Width * 2 % 4; + var stride = m_Width * 2 + padding; + if (padding != 0) + { + buff = new byte[stride * m_Height]; + for (int i = 0; i < m_Height; i++) + { + Array.Copy(image_data, i * m_Width * 2, buff, i * stride, m_Width * 2); + } + } + else + { + buff = image_data; + } + var hObject = GCHandle.Alloc(buff, GCHandleType.Pinned); + var pObject = hObject.AddrOfPinnedObject(); + var bitmap = new Bitmap(m_Width, m_Height, stride, PixelFormat.Format16bppRgb565, pObject); + hObject.Free(); + return bitmap; + } + + private Bitmap PVRToBitmap(byte[] pvrdata) + { + var bitmap = new Bitmap(m_Width, m_Height); + var rect = new Rectangle(0, 0, m_Width, m_Height); + var bmd = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + var len = Math.Abs(bmd.Stride) * bmd.Height; + if (!DecompressPVR(pvrdata, bmd.Scan0, len)) + { + bitmap.UnlockBits(bmd); + bitmap.Dispose(); + return null; + } + bitmap.UnlockBits(bmd); + return bitmap; + } + + private Bitmap TextureConverter() + { + var bitmap = new Bitmap(m_Width, m_Height); + var rect = new Rectangle(0, 0, m_Width, m_Height); + var bmd = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + var len = Math.Abs(bmd.Stride) * bmd.Height; + var fixAlpha = glBaseInternalFormat == KTXHeader.GL_RED || glBaseInternalFormat == KTXHeader.GL_RG; + if (!Ponvert(image_data, bmd.Scan0, m_Width, m_Height, image_data_size, (int)q_format, len, fixAlpha)) + { + bitmap.UnlockBits(bmd); + bitmap.Dispose(); + return null; + } + bitmap.UnlockBits(bmd); + return bitmap; + } + + private void DecompressCRN() + { + IntPtr uncompressedData; + int uncompressedSize; + bool result; + if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)) //2017.3 and up + { + result = DecompressUnityCRN(image_data, image_data_size, out uncompressedData, out uncompressedSize); + } + else + { + result = DecompressCRN(image_data, image_data_size, out uncompressedData, out uncompressedSize); + } + + if (result) + { + var uncompressedBytes = new byte[uncompressedSize]; + Marshal.Copy(uncompressedData, uncompressedBytes, 0, uncompressedSize); + Marshal.FreeHGlobal(uncompressedData); + image_data = uncompressedBytes; + image_data_size = uncompressedSize; + } + } + + private Bitmap Texgenpack() + { + var bitmap = new Bitmap(m_Width, m_Height); + var rect = new Rectangle(0, 0, m_Width, m_Height); + var bmd = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + Decode((int)texturetype, image_data, m_Width, m_Height, bmd.Scan0); + bitmap.UnlockBits(bmd); + return bitmap; + } + } + + public static class KTXHeader + { + public static byte[] IDENTIFIER = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; + public static byte[] ENDIANESS_LE = { 1, 2, 3, 4 }; + + // constants for glInternalFormat + public static int GL_ETC1_RGB8_OES = 0x8D64; + + public static int GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00; + public static int GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01; + public static int GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02; + public static int GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03; + + public static int GL_ATC_RGB_AMD = 0x8C92; + public static int GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD = 0x87EE; + + public static int GL_COMPRESSED_RGB8_ETC2 = 0x9274; + public static int GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276; + public static int GL_COMPRESSED_RGBA8_ETC2_EAC = 0x9278; + public static int GL_COMPRESSED_R11_EAC = 0x9270; + public static int GL_COMPRESSED_SIGNED_R11_EAC = 0x9271; + public static int GL_COMPRESSED_RG11_EAC = 0x9272; + public static int GL_COMPRESSED_SIGNED_RG11_EAC = 0x9273; + + public static int GL_COMPRESSED_RED_RGTC1 = 0x8DBB; + public static int GL_COMPRESSED_RG_RGTC2 = 0x8DBD; + public static int GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F; + public static int GL_COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C; + + public static int GL_R16F = 0x822D; + public static int GL_RG16F = 0x822F; + public static int GL_RGBA16F = 0x881A; + public static int GL_R32F = 0x822E; + public static int GL_RG32F = 0x8230; + public static int GL_RGBA32F = 0x8814; + + // constants for glBaseInternalFormat + public static int GL_RED = 0x1903; + public static int GL_RGB = 0x1907; + public static int GL_RGBA = 0x1908; + public static int GL_RG = 0x8227; + } + + //from TextureConverter.h + public enum QFORMAT + { + // General formats + Q_FORMAT_RGBA_8UI = 1, + Q_FORMAT_RGBA_8I, + Q_FORMAT_RGB5_A1UI, + Q_FORMAT_RGBA_4444, + Q_FORMAT_RGBA_16UI, + Q_FORMAT_RGBA_16I, + Q_FORMAT_RGBA_32UI, + Q_FORMAT_RGBA_32I, + + Q_FORMAT_PALETTE_8_RGBA_8888, + Q_FORMAT_PALETTE_8_RGBA_5551, + Q_FORMAT_PALETTE_8_RGBA_4444, + Q_FORMAT_PALETTE_4_RGBA_8888, + Q_FORMAT_PALETTE_4_RGBA_5551, + Q_FORMAT_PALETTE_4_RGBA_4444, + Q_FORMAT_PALETTE_1_RGBA_8888, + Q_FORMAT_PALETTE_8_RGB_888, + Q_FORMAT_PALETTE_8_RGB_565, + Q_FORMAT_PALETTE_4_RGB_888, + Q_FORMAT_PALETTE_4_RGB_565, + + Q_FORMAT_R2_GBA10UI, + Q_FORMAT_RGB10_A2UI, + Q_FORMAT_RGB10_A2I, + Q_FORMAT_RGBA_F, + Q_FORMAT_RGBA_HF, + + Q_FORMAT_RGB9_E5, // Last five bits are exponent bits (Read following section in GLES3 spec: "3.8.17 Shared Exponent Texture Color Conversion") + Q_FORMAT_RGB_8UI, + Q_FORMAT_RGB_8I, + Q_FORMAT_RGB_565, + Q_FORMAT_RGB_16UI, + Q_FORMAT_RGB_16I, + Q_FORMAT_RGB_32UI, + Q_FORMAT_RGB_32I, + + Q_FORMAT_RGB_F, + Q_FORMAT_RGB_HF, + Q_FORMAT_RGB_11_11_10_F, + + Q_FORMAT_RG_F, + Q_FORMAT_RG_HF, + Q_FORMAT_RG_32UI, + Q_FORMAT_RG_32I, + Q_FORMAT_RG_16I, + Q_FORMAT_RG_16UI, + Q_FORMAT_RG_8I, + Q_FORMAT_RG_8UI, + Q_FORMAT_RG_S88, + + Q_FORMAT_R_32UI, + Q_FORMAT_R_32I, + Q_FORMAT_R_F, + Q_FORMAT_R_16F, + Q_FORMAT_R_16I, + Q_FORMAT_R_16UI, + Q_FORMAT_R_8I, + Q_FORMAT_R_8UI, + + Q_FORMAT_LUMINANCE_ALPHA_88, + Q_FORMAT_LUMINANCE_8, + Q_FORMAT_ALPHA_8, + + Q_FORMAT_LUMINANCE_ALPHA_F, + Q_FORMAT_LUMINANCE_F, + Q_FORMAT_ALPHA_F, + Q_FORMAT_LUMINANCE_ALPHA_HF, + Q_FORMAT_LUMINANCE_HF, + Q_FORMAT_ALPHA_HF, + Q_FORMAT_DEPTH_16, + Q_FORMAT_DEPTH_24, + Q_FORMAT_DEPTH_24_STENCIL_8, + Q_FORMAT_DEPTH_32, + + Q_FORMAT_BGR_565, + Q_FORMAT_BGRA_8888, + Q_FORMAT_BGRA_5551, + Q_FORMAT_BGRX_8888, + Q_FORMAT_BGRA_4444, + // Compressed formats + Q_FORMAT_ATITC_RGBA, + Q_FORMAT_ATC_RGBA_EXPLICIT_ALPHA = Q_FORMAT_ATITC_RGBA, + Q_FORMAT_ATITC_RGB, + Q_FORMAT_ATC_RGB = Q_FORMAT_ATITC_RGB, + Q_FORMAT_ATC_RGBA_INTERPOLATED_ALPHA, + Q_FORMAT_ETC1_RGB8, + Q_FORMAT_3DC_X, + Q_FORMAT_3DC_XY, + + Q_FORMAT_ETC2_RGB8, + Q_FORMAT_ETC2_RGBA8, + Q_FORMAT_ETC2_RGB8_PUNCHTHROUGH_ALPHA1, + Q_FORMAT_ETC2_SRGB8, + Q_FORMAT_ETC2_SRGB8_ALPHA8, + Q_FORMAT_ETC2_SRGB8_PUNCHTHROUGH_ALPHA1, + Q_FORMAT_EAC_R_SIGNED, + Q_FORMAT_EAC_R_UNSIGNED, + Q_FORMAT_EAC_RG_SIGNED, + Q_FORMAT_EAC_RG_UNSIGNED, + + Q_FORMAT_S3TC_DXT1_RGB, + Q_FORMAT_S3TC_DXT1_RGBA, + Q_FORMAT_S3TC_DXT3_RGBA, + Q_FORMAT_S3TC_DXT5_RGBA, + + // YUV formats + Q_FORMAT_AYUV_32, + Q_FORMAT_I444_24, + Q_FORMAT_YUYV_16, + Q_FORMAT_UYVY_16, + Q_FORMAT_I420_12, + Q_FORMAT_YV12_12, + Q_FORMAT_NV21_12, + Q_FORMAT_NV12_12, + + // ASTC Format + Q_FORMAT_ASTC_8, + Q_FORMAT_ASTC_16, + }; + + public enum texgenpack_texturetype + { + RGTC1, + RGTC2, + BPTC_FLOAT, + BPTC + } +} diff --git a/AzurLaneLive2DExtract/AzurLaneLive2DExtract.csproj b/AzurLaneLive2DExtract/AzurLaneLive2DExtract.csproj new file mode 100644 index 0000000..17facc8 --- /dev/null +++ b/AzurLaneLive2DExtract/AzurLaneLive2DExtract.csproj @@ -0,0 +1,105 @@ + + + + + Debug + AnyCPU + {FEC68C25-0231-46BC-B7B9-2E2D85EB085E} + Exe + AzurLaneLive2DExtract + AzurLaneLive2DExtract + v4.5 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + + + ..\packages\SharpDX.Mathematics.4.2.0\lib\net45\SharpDX.Mathematics.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AzurLaneLive2DExtract/CubismModel3Json.cs b/AzurLaneLive2DExtract/CubismModel3Json.cs new file mode 100644 index 0000000..6e79850 --- /dev/null +++ b/AzurLaneLive2DExtract/CubismModel3Json.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AzurLaneLive2DExtract +{ + public class CubismModel3Json + { + public int Version; + public SerializableFileReferences FileReferences; + public SerializableGroup[] Groups; + } + + public struct SerializableFileReferences + { + public string Moc; + public string[] Textures; + public string[] Motions; + public string Physics; + } + + public struct SerializableGroup + { + public string Target; + public string Name; + public string[] Ids; + } +} diff --git a/AzurLaneLive2DExtract/CubismMotion3Converter.cs b/AzurLaneLive2DExtract/CubismMotion3Converter.cs new file mode 100644 index 0000000..feecb1c --- /dev/null +++ b/AzurLaneLive2DExtract/CubismMotion3Converter.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using AssetStudioCore; +using AssetStudioCore.Classes; + +namespace AzurLaneLive2DExtract +{ + class CubismMotion3Converter + { + private Dictionary bonePathHash = new Dictionary(); + public List AnimationList { get; protected set; } = new List(); + + public CubismMotion3Converter(GameObject rootGameObject, AnimationClip[] animationClips) + { + var rootTransform = GetTransform(rootGameObject); + CreateBonePathHash(rootTransform); + ConvertAnimations(animationClips); + } + + private void ConvertAnimations(AnimationClip[] animationClips) + { + foreach (var animationClip in animationClips) + { + var iAnim = new ImportedKeyframedAnimation(); + AnimationList.Add(iAnim); + iAnim.Name = animationClip.m_Name; + iAnim.SampleRate = animationClip.m_SampleRate; + iAnim.Duration = animationClip.m_MuscleClip.m_StopTime; + var m_Clip = animationClip.m_MuscleClip.m_Clip; + var streamedFrames = m_Clip.m_StreamedClip.ReadData(); + var m_ClipBindingConstant = animationClip.m_ClipBindingConstant; + for (int frameIndex = 1; frameIndex < streamedFrames.Count - 1; frameIndex++) + { + var frame = streamedFrames[frameIndex]; + var streamedValues = frame.keyList.Select(x => x.value).ToArray(); + for (int curveIndex = 0; curveIndex < frame.keyList.Count;) + { + ReadCurveData(iAnim, m_ClipBindingConstant, frame.keyList[curveIndex].index, frame.time, streamedValues, 0, ref curveIndex); + } + } + var m_DenseClip = m_Clip.m_DenseClip; + var streamCount = m_Clip.m_StreamedClip.curveCount; + for (int frameIndex = 0; frameIndex < m_DenseClip.m_FrameCount; frameIndex++) + { + var time = frameIndex / m_DenseClip.m_SampleRate; + var frameOffset = frameIndex * m_DenseClip.m_CurveCount; + for (int curveIndex = 0; curveIndex < m_DenseClip.m_CurveCount;) + { + var index = streamCount + curveIndex; + ReadCurveData(iAnim, m_ClipBindingConstant, (int)index, time, m_DenseClip.m_SampleArray, (int)frameOffset, ref curveIndex); + } + } + var m_ConstantClip = m_Clip.m_ConstantClip; + var denseCount = m_Clip.m_DenseClip.m_CurveCount; + var time2 = 0.0f; + for (int i = 0; i < 2; i++) + { + for (int curveIndex = 0; curveIndex < m_ConstantClip.data.Length;) + { + var index = streamCount + denseCount + curveIndex; + ReadCurveData(iAnim, m_ClipBindingConstant, (int)index, time2, m_ConstantClip.data, 0, ref curveIndex); + } + time2 = animationClip.m_MuscleClip.m_StopTime; + } + } + } + + private void ReadCurveData(ImportedKeyframedAnimation iAnim, AnimationClipBindingConstant m_ClipBindingConstant, int index, float time, float[] data, int offset, ref int curveIndex) + { + var binding = m_ClipBindingConstant.FindBinding(index); + if (binding.path == 0) + { + curveIndex++; + return; + } + + GetLive2dPath(binding.path, out var target, out var boneName); + var track = iAnim.FindTrack(boneName); + track.Target = target; + switch (binding.attribute) + { + default: + track.Curve.Add(new ImportedKeyframe(time, data[curveIndex++])); + break; + } + } + + private void GetLive2dPath(uint path, out string target, out string id) + { + id = path.ToString(); + target = path.ToString(); + if (bonePathHash.TryGetValue(path, out var boneName)) + { + var index = boneName.LastIndexOf('/'); + id = boneName.Substring(index + 1); + target = boneName.Substring(0, index); + if (target == "Parameters") + { + target = "Parameter"; + } + else if (target == "Parts") + { + target = "PartOpacity"; + } + } + } + + private Transform GetTransform(GameObject gameObject) + { + foreach (var m_Component in gameObject.m_Component) + { + var asset = m_Component.Get(); + if (asset.Type == ClassIDReference.Transform) + { + return new Transform(asset); + } + } + + return null; + } + + private void CreateBonePathHash(Transform m_Transform) + { + var name = GetTransformPath(m_Transform); + var crc = new SevenZip.CRC(); + var bytes = Encoding.UTF8.GetBytes(name); + crc.Update(bytes, 0, (uint)bytes.Length); + bonePathHash[crc.GetDigest()] = name; + int index; + while ((index = name.IndexOf("/", StringComparison.Ordinal)) >= 0) + { + name = name.Substring(index + 1); + crc = new SevenZip.CRC(); + bytes = Encoding.UTF8.GetBytes(name); + crc.Update(bytes, 0, (uint)bytes.Length); + bonePathHash[crc.GetDigest()] = name; + } + foreach (var pptr in m_Transform.m_Children) + { + CreateBonePathHash(new Transform(pptr.Get())); + } + } + + private string GetTransformPath(Transform meshTransform) + { + var m_GameObject = new GameObject(meshTransform.m_GameObject.Get()); + if (meshTransform.m_Father.TryGet(out var father)) + { + var transform = new Transform(father); + return GetTransformPath(transform) + "/" + m_GameObject.m_Name; + } + + return m_GameObject.m_Name; + } + } +} diff --git a/AzurLaneLive2DExtract/CubismMotion3Json.cs b/AzurLaneLive2DExtract/CubismMotion3Json.cs new file mode 100644 index 0000000..0a9abd0 --- /dev/null +++ b/AzurLaneLive2DExtract/CubismMotion3Json.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudioCore +{ + public class CubismMotion3Json + { + public int Version; + public SerializableMeta Meta; + public SerializableCurve[] Curves; + } + + public class SerializableMeta + { + public float Duration; + public float Fps; + public bool Loop; + public bool AreBeziersRestricted; + public int CurveCount; + public int TotalSegmentCount; + public int TotalPointCount; + public int UserDataCount; + public int TotalUserDataSize; + }; + + public class SerializableCurve + { + public string Target; + public string Id; + public List Segments; + }; +} diff --git a/AzurLaneLive2DExtract/ImportedKeyframedAnimation.cs b/AzurLaneLive2DExtract/ImportedKeyframedAnimation.cs new file mode 100644 index 0000000..d8fb4ec --- /dev/null +++ b/AzurLaneLive2DExtract/ImportedKeyframedAnimation.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SharpDX; + +namespace AzurLaneLive2DExtract +{ + public class ImportedKeyframedAnimation + { + public string Name { get; set; } + public float SampleRate { get; set; } + public float Duration { get; set; } + + public List TrackList { get; set; } = new List(); + + public ImportedAnimationKeyframedTrack FindTrack(string name) + { + var track = TrackList.Find(x => x.Name == name); + if (track == null) + { + track = new ImportedAnimationKeyframedTrack { Name = name }; + TrackList.Add(track); + } + return track; + } + } + + public class ImportedKeyframe + { + public float time { get; set; } + public T value { get; set; } + + public ImportedKeyframe(float time, T value) + { + this.time = time; + this.value = value; + } + } + + public class ImportedAnimationKeyframedTrack + { + public string Name { get; set; } + public string Target { get; set; } + public List> Curve = new List>(); + } +} diff --git a/AzurLaneLive2DExtract/MyJsonConverter.cs b/AzurLaneLive2DExtract/MyJsonConverter.cs new file mode 100644 index 0000000..316dda9 --- /dev/null +++ b/AzurLaneLive2DExtract/MyJsonConverter.cs @@ -0,0 +1,37 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; + +namespace AssetStudioCore +{ + public class MyJsonConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(List); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteStartArray(); + Convert(writer, (List)value); + writer.WriteEndArray(); + } + + private void Convert(JsonWriter writer, List array) + { + foreach (var n in array) + { + var v = $"{n:0.###}"; + writer.WriteRawValue(v); + } + } + } +} \ No newline at end of file diff --git a/AzurLaneLive2DExtract/Program.cs b/AzurLaneLive2DExtract/Program.cs new file mode 100644 index 0000000..67ff45f --- /dev/null +++ b/AzurLaneLive2DExtract/Program.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using AssetStudioCore; +using AssetStudioCore.Classes; +using Newtonsoft.Json; + +namespace AzurLaneLive2DExtract +{ + class Program + { + static void Main(string[] args) + { + foreach (var arg in args) + { + if (!File.Exists(arg)) + continue; + var path = Path.GetFullPath(arg); + var bundleFile = new BundleFile(path, new EndianBinaryReader(File.OpenRead(path))); + if (bundleFile.fileList.Count == 0) + { + return; + } + var assetsFile = new AssetsFile(path, new EndianBinaryReader(bundleFile.fileList[0].stream)); + var assets = assetsFile.preloadTable.Select(x => x.Value).ToArray(); + var name = Path.GetFileName(path); + var destPath = @"live2d\" + name + @"\"; + var destTexturePath = @"live2d\" + name + @"\textures\"; + var destAnimationPath = @"live2d\" + name + @"\motions\"; + Directory.CreateDirectory(destPath); + Directory.CreateDirectory(destTexturePath); + Directory.CreateDirectory(destAnimationPath); + Console.WriteLine($"Extract {name}"); + //physics + var physics = new TextAsset(assets.First(x => x.Type == ClassIDReference.TextAsset)); + File.WriteAllBytes($"{destPath}{physics.m_Name}.json", physics.m_Script); + //moc + var moc = assets.First(x => x.Type == ClassIDReference.MonoBehaviour); + foreach (var assetPreloadData in assets.Where(x => x.Type == ClassIDReference.MonoBehaviour)) + { + if (assetPreloadData.Size > moc.Size) + { + moc = assetPreloadData; + } + } + var mocReader = moc.InitReader(); + mocReader.Position += 28; + mocReader.ReadAlignedString(); + var mocBuff = mocReader.ReadBytes(mocReader.ReadInt32()); + File.WriteAllBytes($"{destPath}{name}.moc3", mocBuff); + //texture + var textures = new SortedSet(); + foreach (var texture in assets.Where(x => x.Type == ClassIDReference.Texture2D)) + { + var texture2D = new Texture2D(texture); + using (var bitmap = new Texture2DConverter(texture2D).ConvertToBitmap(true)) + { + textures.Add($"textures/{texture2D.m_Name}.png"); + bitmap.Save($"{destTexturePath}{texture2D.m_Name}.png", ImageFormat.Png); + } + } + //motions + var motions = new List(); + var animatorAsset = assets.First(x => x.Type == ClassIDReference.Animator); + var animator = new Animator(animatorAsset); + var rootGameObject = new GameObject(animator.m_GameObject.Get()); + var animations = assets.Where(x => x.Type == ClassIDReference.AnimationClip).Select(x => new AnimationClip(x)).ToArray(); + var converter = new CubismMotion3Converter(rootGameObject, animations); + foreach (ImportedKeyframedAnimation animation in converter.AnimationList) + { + var json = new CubismMotion3Json + { + Version = 3, + Meta = new SerializableMeta + { + Duration = animation.Duration, + Fps = animation.SampleRate, + Loop = true, + AreBeziersRestricted = true, + CurveCount = animation.TrackList.Count, + TotalSegmentCount = 63, //TODO How to calculate this? + TotalPointCount = 165, //TODO How to calculate this? + UserDataCount = 0, + TotalUserDataSize = 0 + }, + Curves = new SerializableCurve[animation.TrackList.Count] + }; + for (int i = 0; i < animation.TrackList.Count; i++) + { + var track = animation.TrackList[i]; + json.Curves[i] = new SerializableCurve + { + Target = track.Target, + Id = track.Name, + Segments = new List { 0f, track.Curve[0].value } + }; + for (var j = 1; j < track.Curve.Count; j++) + { + var curve = track.Curve[j]; + if (curve != null) + { + json.Curves[i].Segments.Add(0f); + json.Curves[i].Segments.Add(curve.time); + json.Curves[i].Segments.Add(curve.value); + } + } + } + motions.Add($"motions/{animation.Name}.motion3.json"); + File.WriteAllText($"{destAnimationPath}{animation.Name}.motion3.json", JsonConvert.SerializeObject(json, Formatting.Indented, new MyJsonConverter())); + } + //model + var model3 = new CubismModel3Json + { + Version = 3, + FileReferences = new SerializableFileReferences + { + Moc = $"{name}.moc3", + Textures = textures.ToArray(), + Motions = motions.ToArray(), + Physics = $"{physics.m_Name}.json" + }, + Groups = new[] + { + new SerializableGroup + { + Target = "Parameter", + Name = "LipSync", + Ids = new[] {"ParamMouthOpenY"} + }, + new SerializableGroup + { + Target = "Parameter", + Name = "EyeBlink", + Ids = new[] {"ParamEyeLOpen", "ParamEyeROpen"} + } + } + }; + File.WriteAllText($"{destPath}{name}.model3.json", JsonConvert.SerializeObject(model3, Formatting.Indented)); + } + Console.WriteLine("Done!"); + Console.Read(); + } + } +} diff --git a/AzurLaneLive2DExtract/Properties/AssemblyInfo.cs b/AzurLaneLive2DExtract/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..863400c --- /dev/null +++ b/AzurLaneLive2DExtract/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("AzurLaneLive2DExtract")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AzurLaneLive2DExtract")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("fec68c25-0231-46bc-b7b9-2e2d85eb085e")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 +// 方法是按如下所示使用“*”: : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AzurLaneLive2DExtract/packages.config b/AzurLaneLive2DExtract/packages.config new file mode 100644 index 0000000..658ed17 --- /dev/null +++ b/AzurLaneLive2DExtract/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file